pax_global_header00006660000000000000000000000064147576147320014532gustar00rootroot0000000000000052 comment=0fb05cc1359836b1e31e3ba8bee7db479ad3765e golang-gitlab-gitlab-org-api-client-go-0.123.0/000077500000000000000000000000001475761473200210575ustar00rootroot00000000000000golang-gitlab-gitlab-org-api-client-go-0.123.0/.devcontainer/000077500000000000000000000000001475761473200236165ustar00rootroot00000000000000golang-gitlab-gitlab-org-api-client-go-0.123.0/.devcontainer/devcontainer.json000066400000000000000000000014121475761473200271700ustar00rootroot00000000000000// The Dev Container format allows you to configure your environment. At the heart of it // is a Docker image or Dockerfile which controls the tools available in your environment. // // See https://aka.ms/devcontainer.json for more information. { "name": "devcontainer-client-go", "image": "mcr.microsoft.com/vscode/devcontainers/base:ubuntu", // Features add additional features to your environment. See https://containers.dev/features "features": { "ghcr.io/devcontainers/features/docker-in-docker:2": {}, "ghcr.io/devcontainers/features/go:1": { "version": "1.22.10" } }, // Extensions for VSCode "customizations": { "vscode": { "extensions": [ "golang.Go", "ms-vscode-remote.remote-containers", "GitLab.gitlab-workflow" ] } } } golang-gitlab-gitlab-org-api-client-go-0.123.0/.gitignore000066400000000000000000000005251475761473200230510ustar00rootroot00000000000000# Compiled Object files, Static and Dynamic libs (Shared Objects) *.o *.a *.so # Folders _obj _test # Architecture specific extensions/prefixes *.[568vq] [568vq].out *.cgo1.go *.cgo2.c _cgo_defun.c _cgo_gotypes.go _cgo_export.* _testmain.go *.exe *.test *.prof # IDE specific files and folders .idea *.iml *.swp *.swo # vendor vendor golang-gitlab-gitlab-org-api-client-go-0.123.0/.gitlab-ci.yml000066400000000000000000000103311475761473200235110ustar00rootroot00000000000000workflow: rules: - if: $CI_PIPELINE_SOURCE == "merge_request_event" - if: $CI_PIPELINE_SOURCE == "schedule" - if: $CI_COMMIT_TAG - if: $CI_COMMIT_REF_PROTECTED == "true" include: - component: ${CI_SERVER_FQDN}/gitlab-org/components/danger-review/danger-review@2.0.0 inputs: job_stage: lint job_allow_failure: true stages: - lint - test - deploy .go:versions: parallel: matrix: - GOLANG_IMAGE_VERSION: - '1.22' - '1.23' - '1.24' .go:base: # From: https://docs.gitlab.com/ee/ci/caching/#cache-go-dependencies variables: GOPATH: $CI_PROJECT_DIR/.go GOLANGCI_LINT_CACHE: $CI_PROJECT_DIR/.golangci-lint before_script: - mkdir -p "${GOPATH}" "${GOLANGCI_LINT_CACHE}" cache: paths: - $GOPATH/pkg/mod/ - $GOLANGCI_LINT_CACHE/ key: files: - go.sum # We want to speed up CI a bit. # Community contributors are recommended to use the Community fork # which has access to this runners. # For other forks to free tier namespaces this might fail, # which is a good reminder to use the Community fork and not # to accidentally burn to personal compute minutes. tags: - saas-linux-large-amd64 # We only need to run Go-related jobs when actual Go files changed # or when running either on the default branch or for a tag. rules: - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH' - if: $CI_COMMIT_TAG - changes: - '**/*.go' - testdata/** - go.mod - go.sum - .gitlab-ci.yml golangci-lint: extends: - .go:base stage: lint needs: [] variables: REPORT_FILENAME: 'gl-code-quality-report.json' image: golangci/golangci-lint:v1.64.5 script: - golangci-lint run --print-issued-lines=false --out-format code-climate:$REPORT_FILENAME,line-number artifacts: reports: codequality: $REPORT_FILENAME paths: [$REPORT_FILENAME] when: always tests:unit: extends: - .go:base - .go:versions stage: test needs: [] image: golang:$GOLANG_IMAGE_VERSION variables: # configure tooling versions GOTESTSUM_VERSION: 'v1.12.0' GOCOVER_COBERTURA_VERSION: 'v1.2.1-0.20240107185409-0818f3538137' # configure artifact files JUNIT_FILENAME: tests.xml COVERPROFILE_FILENAME: coverage.out COVERPROFILE_XML_FILENAME: coverage.xml script: - go run gotest.tools/gotestsum@${GOTESTSUM_VERSION} --format=standard-quiet --junitfile=$JUNIT_FILENAME -- -race -coverprofile=$COVERPROFILE_FILENAME -covermode=atomic ./... - go run github.com/boumenot/gocover-cobertura@${GOCOVER_COBERTURA_VERSION} < $COVERPROFILE_FILENAME > $COVERPROFILE_XML_FILENAME - go tool cover -func $COVERPROFILE_FILENAME coverage: '/total:.+\(statements\).+\d+\.\d+/' artifacts: paths: - $JUNIT_FILENAME - $COVERPROFILE_XML_FILENAME reports: junit: $JUNIT_FILENAME coverage_report: path: $COVERPROFILE_XML_FILENAME coverage_format: cobertura when: always generate-release-notes: stage: deploy needs: [] image: alpine:3.21.2 before_script: - apk add --update jq curl git script: - | if [ -z "$CI_COMMIT_TAG" ]; then last_stable_version_sha="$(git tag | grep -E '^v(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$' | sort -Vr | head -n1)" version="${last_stable_version_sha}+${CI_COMMIT_SHA}" else version="$CI_COMMIT_TAG" fi urlencoded_version="$(jq -rn --arg x "${version}" '$x|@uri')" - echo "Generating release notes for ${version} (urlencoded=${urlencoded_version}) ..." - 'curl --fail-with-body --header "JOB-TOKEN: $CI_JOB_TOKEN" "$CI_API_V4_URL/projects/$CI_PROJECT_ID/repository/changelog?version=${urlencoded_version}" | jq -r .notes > release-notes.md' - cat release-notes.md artifacts: paths: - release-notes.md release: stage: deploy rules: - if: $CI_COMMIT_TAG needs: - golangci-lint - tests:unit - job: generate-release-notes artifacts: true image: registry.gitlab.com/gitlab-org/release-cli:latest script: - echo "Create release for $CI_COMMIT_TAG" release: tag_name: '$CI_COMMIT_TAG' tag_message: 'Version $CI_COMMIT_TAG' name: '$CI_COMMIT_TAG' description: release-notes.md golang-gitlab-gitlab-org-api-client-go-0.123.0/.gitlab/000077500000000000000000000000001475761473200223775ustar00rootroot00000000000000golang-gitlab-gitlab-org-api-client-go-0.123.0/.gitlab/changelog_config.yml000066400000000000000000000006211475761473200263750ustar00rootroot00000000000000# See https://docs.gitlab.com/ee/user/project/changelogs.html # For API see https://docs.gitlab.com/ee/api/repositories.html#add-changelog-data-to-a-changelog-file tag_regex: '^v(?P0|[1-9]\d*)\.(?P0|[1-9]\d*)\.(?P0|[1-9]\d*)(?:-(?P
(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+(?P[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$'
golang-gitlab-gitlab-org-api-client-go-0.123.0/.golangci.yml000066400000000000000000000020611475761473200234420ustar00rootroot00000000000000# This file contains all available configuration options
# with their default values.

# Options for analysis running
run:
  concurrency: 4
  timeout: 10m
  issues-exit-code: 1
  tests: true

# Output configuration options
output:
  formats:
    - format: line-number

# All available settings of specific linters
linters-settings:
  misspell:
    locale: US
    ignore-words:
    - noteable
  revive:
    enable-all-rules: false
    rules:
      - name: deep-exit

linters:
  enable:
    - asciicheck
    - dogsled
    - errorlint
    - goconst
    - gosimple
    - govet
    - ineffassign
    - misspell
    - nakedret
    - nolintlint
    - revive
    - staticcheck
    - typecheck
    - unconvert
    - unused
    - whitespace
  disable:
    - errcheck
  disable-all: false
  fast: false

issues:
  # List of regexps of issue texts to exclude.
  exclude:
    - "^.*, make it a constant$"

  # Maximum issues count per one linter (set to 0 to disable)
  max-issues-per-linter: 0

  # Maximum count of issues with the same text (set to 0 to disable)
  max-same-issues: 0
golang-gitlab-gitlab-org-api-client-go-0.123.0/.tool-versions000066400000000000000000000000171475761473200237010ustar00rootroot00000000000000golang 1.22.10
golang-gitlab-gitlab-org-api-client-go-0.123.0/CONTRIBUTING.md000066400000000000000000000045001475761473200233070ustar00rootroot00000000000000# How to Contribute

We want to make contributing to this project as easy as possible.

## Reporting Issues

If you have an issue, please report it on the
[issue tracker](https://gitlab.com/gitlab-org/api/client-go/-/issues).

When you are up for writing a MR to solve the issue you encountered, it's not
needed to first open a separate issue. In that case only opening a MR with a
description of the issue you are trying to solve is just fine.

## Contributing Code

Merge requests are always welcome. When in doubt if your contribution fits within
the rest of the project, feel free to first open an issue to discuss your idea.

This is not needed when fixing a bug or adding an enhancement, as long as the
enhancement you are trying to add can be found in the public GitLab API docs as
this project only supports what is in the public API docs.

### Use community fork to contribute

To contribute to this project we recommend that you use the
[community fork](https://gitlab.com/gitlab-community/api/client-go).
Have a look at the
[community fork README](https://gitlab.com/gitlab-community#gitlab-community-forks)
to learn more about what it is and why you should prefer it over
creating your own fork to contribute.

## Coding style

We try to follow the Go best practices, where it makes sense, and use
[`gofumpt`](https://github.com/mvdan/gofumpt) to format code in this project.
As a general rule of thumb we prefer to keep line width for comments below 80
chars and for code (where possible and sensible) below 100 chars.

Before making a MR, please look at the rest this package and try to make sure
your contribution is consistent with the rest of the coding style.

New `struct` fields or methods should be placed (as much as possible) in the same
order as the ordering used in the public API docs. The idea is that this makes it
easier to find things.

### Setting up your local development environment to contribute

1. [Fork](https://gitlab.com/gitlab-org/api/client-go), then clone the repository.
   ```sh
   git clone https://gitlab.com//client-go.git
   # or via ssh
   git clone git@gitlab.com:/client-go.git
   ```
1. Install dependencies:
   ```sh
   make setup
   ```
1. Make your changes on your feature branch
1. Run the tests and `gofumpt`
   ```sh
   make test && make fmt
   ```
1. Open up your merge request
golang-gitlab-gitlab-org-api-client-go-0.123.0/Dangerfile000066400000000000000000000007501475761473200230440ustar00rootroot00000000000000require 'gitlab-dangerfiles'

# see https://docs.gitlab.com/ee/development/dangerbot.html#enable-danger-on-a-project
# see https://gitlab.com/gitlab-org/ruby/gems/gitlab-dangerfiles
Gitlab::Dangerfiles.for_project(self, 'gitlab-api-client-go') do |dangerfiles|
  # Import all plugins from the gem
  dangerfiles.import_plugins

  # Import a defined set of danger rules
  dangerfiles.import_dangerfiles(only: %w[simple_roulette changelog metadata type_label z_add_labels z_retry_link])
end
golang-gitlab-gitlab-org-api-client-go-0.123.0/LICENSE000066400000000000000000000261351475761473200220730ustar00rootroot00000000000000                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

   APPENDIX: How to apply the Apache License to your work.

      To apply the Apache License to your work, attach the following
      boilerplate notice, with the fields enclosed by brackets "{}"
      replaced with your own identifying information. (Don't include
      the brackets!)  The text should be enclosed in the appropriate
      comment syntax for the file format. We also recommend that a
      file or class name and description of purpose be included on the
      same "printed page" as the copyright notice for easier
      identification within third-party archives.

   Copyright {yyyy} {name of copyright owner}

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
golang-gitlab-gitlab-org-api-client-go-0.123.0/Makefile000066400000000000000000000011271475761473200225200ustar00rootroot00000000000000##@ General

.PHONY: help
help: ## Display this help
	@awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n  make \033[36m\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf "  \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST)

##@ Development

fmt: ## Format code
	@gofumpt -l -w .

lint: ## Run linter
	@golangci-lint run

setup: ## Setup your local environment
	go mod tidy
	@go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
	@go install mvdan.cc/gofumpt@latest
.PHONY: setup

test: ## Run tests
	go test ./... -race
golang-gitlab-gitlab-org-api-client-go-0.123.0/README.md000066400000000000000000000066121475761473200223430ustar00rootroot00000000000000# GitLab client-go (former `github.com/xanzy/go-gitlab`)

A GitLab API client enabling Go programs to interact with GitLab in a simple and uniform way.

## Usage

```go
import "gitlab.com/gitlab-org/api/client-go"
```

Construct a new GitLab client, then use the various services on the client to
access different parts of the GitLab API. For example, to list all
users:

```go
git, err := gitlab.NewClient("yourtokengoeshere")
if err != nil {
  log.Fatalf("Failed to create client: %v", err)
}
users, _, err := git.Users.ListUsers(&gitlab.ListUsersOptions{})
```

There are a few `With...` option functions that can be used to customize
the API client. For example, to set a custom base URL:

```go
git, err := gitlab.NewClient("yourtokengoeshere", gitlab.WithBaseURL("https://git.mydomain.com/api/v4"))
if err != nil {
  log.Fatalf("Failed to create client: %v", err)
}
users, _, err := git.Users.ListUsers(&gitlab.ListUsersOptions{})
```

Some API methods have optional parameters that can be passed. For example,
to list all projects for user "svanharmelen":

```go
git := gitlab.NewClient("yourtokengoeshere")
opt := &gitlab.ListProjectsOptions{Search: gitlab.Ptr("svanharmelen")}
projects, _, err := git.Projects.ListProjects(opt)
```

### Examples

The [examples](/examples) directory
contains a couple for clear examples, of which one is partially listed here as well:

```go
package main

import (
	"log"

	"gitlab.com/gitlab-org/api/client-go"
)

func main() {
	git, err := gitlab.NewClient("yourtokengoeshere")
	if err != nil {
		log.Fatalf("Failed to create client: %v", err)
	}

	// Create new project
	p := &gitlab.CreateProjectOptions{
		Name:                     gitlab.Ptr("My Project"),
		Description:              gitlab.Ptr("Just a test project to play with"),
		MergeRequestsAccessLevel: gitlab.Ptr(gitlab.EnabledAccessControl),
		SnippetsAccessLevel:      gitlab.Ptr(gitlab.EnabledAccessControl),
		Visibility:               gitlab.Ptr(gitlab.PublicVisibility),
	}
	project, _, err := git.Projects.CreateProject(p)
	if err != nil {
		log.Fatal(err)
	}

	// Add a new snippet
	s := &gitlab.CreateProjectSnippetOptions{
		Title:           gitlab.Ptr("Dummy Snippet"),
		FileName:        gitlab.Ptr("snippet.go"),
		Content:         gitlab.Ptr("package main...."),
		Visibility:      gitlab.Ptr(gitlab.PublicVisibility),
	}
	_, _, err = git.ProjectSnippets.CreateSnippet(project.ID, s)
	if err != nil {
		log.Fatal(err)
	}
}
```

For complete usage of go-gitlab, see the full [package docs](https://godoc.org/gitlab.com/gitlab-org/api/client-go).

## Contributing

Contributions are always welcome. For more information, check out the
[contributing guide](/CONTRIBUTING.md).

## Maintenance

This is a community maintained project. If you have a paid GitLab subscription,
please note that this project is not packaged as a part of GitLab, and falls outside
of the scope of support.

For more information, see GitLab's
[Statement of Support](https://about.gitlab.com/support/statement-of-support.html).
Please fill out an issue in this projects issue tracker and someone from the community
will respond as soon as they are available to help you.

### Known GitLab Projects using this package

- [GitLab Terraform Provider](https://gitlab.com/gitlab-org/terraform-provider-gitlab)
  maintained by the community with support from ~"group::environments"
- [GitLab CLI (`glab`)](https://gitlab.com/gitlab-org/cli)
  maintained by ~"group::code review"
golang-gitlab-gitlab-org-api-client-go-0.123.0/access_requests.go000066400000000000000000000172071475761473200246110ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"fmt"
	"net/http"
	"time"
)

// AccessRequest represents a access request for a group or project.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/access_requests.html
type AccessRequest struct {
	ID          int              `json:"id"`
	Username    string           `json:"username"`
	Name        string           `json:"name"`
	State       string           `json:"state"`
	CreatedAt   *time.Time       `json:"created_at"`
	RequestedAt *time.Time       `json:"requested_at"`
	AccessLevel AccessLevelValue `json:"access_level"`
}

// AccessRequestsService handles communication with the project/group
// access requests related methods of the GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/access_requests.html
type AccessRequestsService struct {
	client *Client
}

// ListAccessRequestsOptions represents the available
// ListProjectAccessRequests() or ListGroupAccessRequests() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/access_requests.html#list-access-requests-for-a-group-or-project
type ListAccessRequestsOptions ListOptions

// ListProjectAccessRequests gets a list of access requests
// viewable by the authenticated user.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/access_requests.html#list-access-requests-for-a-group-or-project
func (s *AccessRequestsService) ListProjectAccessRequests(pid interface{}, opt *ListAccessRequestsOptions, options ...RequestOptionFunc) ([]*AccessRequest, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/access_requests", PathEscape(project))

	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var ars []*AccessRequest
	resp, err := s.client.Do(req, &ars)
	if err != nil {
		return nil, resp, err
	}

	return ars, resp, nil
}

// ListGroupAccessRequests gets a list of access requests
// viewable by the authenticated user.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/access_requests.html#list-access-requests-for-a-group-or-project
func (s *AccessRequestsService) ListGroupAccessRequests(gid interface{}, opt *ListAccessRequestsOptions, options ...RequestOptionFunc) ([]*AccessRequest, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/access_requests", PathEscape(group))

	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var ars []*AccessRequest
	resp, err := s.client.Do(req, &ars)
	if err != nil {
		return nil, resp, err
	}

	return ars, resp, nil
}

// RequestProjectAccess requests access for the authenticated user
// to a group or project.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/access_requests.html#request-access-to-a-group-or-project
func (s *AccessRequestsService) RequestProjectAccess(pid interface{}, options ...RequestOptionFunc) (*AccessRequest, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/access_requests", PathEscape(project))

	req, err := s.client.NewRequest(http.MethodPost, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	ar := new(AccessRequest)
	resp, err := s.client.Do(req, ar)
	if err != nil {
		return nil, resp, err
	}

	return ar, resp, nil
}

// RequestGroupAccess requests access for the authenticated user
// to a group or project.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/access_requests.html#request-access-to-a-group-or-project
func (s *AccessRequestsService) RequestGroupAccess(gid interface{}, options ...RequestOptionFunc) (*AccessRequest, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/access_requests", PathEscape(group))

	req, err := s.client.NewRequest(http.MethodPost, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	ar := new(AccessRequest)
	resp, err := s.client.Do(req, ar)
	if err != nil {
		return nil, resp, err
	}

	return ar, resp, nil
}

// ApproveAccessRequestOptions represents the available
// ApproveProjectAccessRequest() and ApproveGroupAccessRequest() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/access_requests.html#approve-an-access-request
type ApproveAccessRequestOptions struct {
	AccessLevel *AccessLevelValue `url:"access_level,omitempty" json:"access_level,omitempty"`
}

// ApproveProjectAccessRequest approves an access request for the given user.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/access_requests.html#approve-an-access-request
func (s *AccessRequestsService) ApproveProjectAccessRequest(pid interface{}, user int, opt *ApproveAccessRequestOptions, options ...RequestOptionFunc) (*AccessRequest, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/access_requests/%d/approve", PathEscape(project), user)

	req, err := s.client.NewRequest(http.MethodPut, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	ar := new(AccessRequest)
	resp, err := s.client.Do(req, ar)
	if err != nil {
		return nil, resp, err
	}

	return ar, resp, nil
}

// ApproveGroupAccessRequest approves an access request for the given user.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/access_requests.html#approve-an-access-request
func (s *AccessRequestsService) ApproveGroupAccessRequest(gid interface{}, user int, opt *ApproveAccessRequestOptions, options ...RequestOptionFunc) (*AccessRequest, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/access_requests/%d/approve", PathEscape(group), user)

	req, err := s.client.NewRequest(http.MethodPut, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	ar := new(AccessRequest)
	resp, err := s.client.Do(req, ar)
	if err != nil {
		return nil, resp, err
	}

	return ar, resp, nil
}

// DenyProjectAccessRequest denies an access request for the given user.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/access_requests.html#deny-an-access-request
func (s *AccessRequestsService) DenyProjectAccessRequest(pid interface{}, user int, options ...RequestOptionFunc) (*Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("projects/%s/access_requests/%d", PathEscape(project), user)

	req, err := s.client.NewRequest(http.MethodDelete, u, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}

// DenyGroupAccessRequest denies an access request for the given user.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/access_requests.html#deny-an-access-request
func (s *AccessRequestsService) DenyGroupAccessRequest(gid interface{}, user int, options ...RequestOptionFunc) (*Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("groups/%s/access_requests/%d", PathEscape(group), user)

	req, err := s.client.NewRequest(http.MethodDelete, u, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/access_requests_test.go000066400000000000000000000302731475761473200256460ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"encoding/json"
	"fmt"
	"net/http"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
)

func TestListProjectAccessRequests(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/access_requests", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `[
			{
			  "id": 1,
			  "username": "raymond_smith",
			  "name": "Raymond Smith",
			  "state": "active",
			  "created_at": "2012-10-22T14:13:35Z",
			  "requested_at": "2012-10-22T14:13:35Z"
			},
			{
			  "id": 2,
			  "username": "john_doe",
			  "name": "John Doe",
			  "state": "active",
			  "created_at": "2012-10-22T14:13:35Z",
			  "requested_at": "2012-10-22T14:13:35Z"
			}
		]`)
	})

	created := time.Date(2012, 10, 22, 14, 13, 35, 0, time.UTC)
	expected := []*AccessRequest{
		{
			ID:          1,
			Username:    "raymond_smith",
			Name:        "Raymond Smith",
			State:       "active",
			CreatedAt:   &created,
			RequestedAt: &created,
		},
		{
			ID:          2,
			Username:    "john_doe",
			Name:        "John Doe",
			State:       "active",
			CreatedAt:   &created,
			RequestedAt: &created,
		},
	}

	requests, resp, err := client.AccessRequests.ListProjectAccessRequests(1, nil)
	assert.NoError(t, err)
	assert.NotNil(t, resp)
	assert.Equal(t, expected, requests)

	requests, resp, err = client.AccessRequests.ListProjectAccessRequests(1.5, nil)
	assert.EqualError(t, err, "invalid ID type 1.5, the ID must be an int or a string")
	assert.Nil(t, resp)
	assert.Nil(t, requests)

	requests, resp, err = client.AccessRequests.ListProjectAccessRequests(2, nil)
	assert.Error(t, err)
	assert.Equal(t, http.StatusNotFound, resp.StatusCode)
	assert.Nil(t, requests)

	requests, resp, err = client.AccessRequests.ListProjectAccessRequests(1, nil, errorOption)
	assert.EqualError(t, err, "RequestOptionFunc returns an error")
	assert.Nil(t, resp)
	assert.Nil(t, requests)
}

func TestListGroupAccessRequests(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1/access_requests", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `[
			{
			  "id": 1,
			  "username": "raymond_smith",
			  "name": "Raymond Smith",
			  "state": "active",
			  "created_at": "2012-10-22T14:13:35Z",
			  "requested_at": "2012-10-22T14:13:35Z"
			},
			{
			  "id": 2,
			  "username": "john_doe",
			  "name": "John Doe",
			  "state": "active",
			  "created_at": "2012-10-22T14:13:35Z",
			  "requested_at": "2012-10-22T14:13:35Z"
			}
		]`)
	})

	created := time.Date(2012, 10, 22, 14, 13, 35, 0, time.UTC)
	expected := []*AccessRequest{
		{
			ID:          1,
			Username:    "raymond_smith",
			Name:        "Raymond Smith",
			State:       "active",
			CreatedAt:   &created,
			RequestedAt: &created,
		},
		{
			ID:          2,
			Username:    "john_doe",
			Name:        "John Doe",
			State:       "active",
			CreatedAt:   &created,
			RequestedAt: &created,
		},
	}

	requests, resp, err := client.AccessRequests.ListGroupAccessRequests(1, nil)
	assert.NoError(t, err)
	assert.NotNil(t, resp)
	assert.Equal(t, expected, requests)

	requests, resp, err = client.AccessRequests.ListGroupAccessRequests(1.5, nil)
	assert.EqualError(t, err, "invalid ID type 1.5, the ID must be an int or a string")
	assert.Nil(t, resp)
	assert.Nil(t, requests)

	requests, resp, err = client.AccessRequests.ListGroupAccessRequests(2, nil)
	assert.Error(t, err)
	assert.Equal(t, http.StatusNotFound, resp.StatusCode)
	assert.Nil(t, requests)

	requests, resp, err = client.AccessRequests.ListGroupAccessRequests(1, nil, errorOption)
	assert.EqualError(t, err, "RequestOptionFunc returns an error")
	assert.Nil(t, resp)
	assert.Nil(t, requests)
}

func TestRequestProjectAccess(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/access_requests", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		fmt.Fprintf(w, `{
				"id": 1,
				"username": "raymond_smith",
				"name": "Raymond Smith",
				"state": "active",
				"created_at": "2012-10-22T14:13:35Z",
				"requested_at": "2012-10-22T14:13:35Z"
			}`)
	})

	created := time.Date(2012, 10, 22, 14, 13, 35, 0, time.UTC)
	expected := &AccessRequest{
		ID:          1,
		Username:    "raymond_smith",
		Name:        "Raymond Smith",
		State:       "active",
		CreatedAt:   &created,
		RequestedAt: &created,
	}

	accessRequest, resp, err := client.AccessRequests.RequestProjectAccess(1, nil)
	assert.NoError(t, err)
	assert.NotNil(t, resp)
	assert.Equal(t, expected, accessRequest)

	accessRequest, resp, err = client.AccessRequests.RequestProjectAccess(1.5, nil)
	assert.EqualError(t, err, "invalid ID type 1.5, the ID must be an int or a string")
	assert.Nil(t, resp)
	assert.Nil(t, accessRequest)

	accessRequest, resp, err = client.AccessRequests.RequestProjectAccess(2, nil)
	assert.Error(t, err)
	assert.Equal(t, http.StatusNotFound, resp.StatusCode)
	assert.Nil(t, accessRequest)

	accessRequest, resp, err = client.AccessRequests.RequestProjectAccess(1, nil, errorOption)
	assert.EqualError(t, err, "RequestOptionFunc returns an error")
	assert.Nil(t, resp)
	assert.Nil(t, accessRequest)
}

func TestRequestGroupAccess(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1/access_requests", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		fmt.Fprintf(w, `{
				"id": 1,
				"username": "raymond_smith",
				"name": "Raymond Smith",
				"state": "active",
				"created_at": "2012-10-22T14:13:35Z",
				"requested_at": "2012-10-22T14:13:35Z"
			}`)
	})

	created := time.Date(2012, 10, 22, 14, 13, 35, 0, time.UTC)
	expected := &AccessRequest{
		ID:          1,
		Username:    "raymond_smith",
		Name:        "Raymond Smith",
		State:       "active",
		CreatedAt:   &created,
		RequestedAt: &created,
	}

	accessRequest, resp, err := client.AccessRequests.RequestGroupAccess(1, nil)
	assert.NoError(t, err)
	assert.NotNil(t, resp)
	assert.Equal(t, expected, accessRequest)

	accessRequest, resp, err = client.AccessRequests.RequestGroupAccess(1.5, nil)
	assert.EqualError(t, err, "invalid ID type 1.5, the ID must be an int or a string")
	assert.Nil(t, resp)
	assert.Nil(t, accessRequest)

	accessRequest, resp, err = client.AccessRequests.RequestGroupAccess(2, nil)
	assert.Error(t, err)
	assert.Equal(t, http.StatusNotFound, resp.StatusCode)
	assert.Nil(t, accessRequest)

	accessRequest, resp, err = client.AccessRequests.RequestGroupAccess(1, nil, errorOption)
	assert.EqualError(t, err, "RequestOptionFunc returns an error")
	assert.Nil(t, resp)
	assert.Nil(t, accessRequest)
}

func TestApproveProjectAccessRequest(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/access_requests/10/approve", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPut)

		var opt ApproveAccessRequestOptions
		err := json.NewDecoder(r.Body).Decode(&opt)
		assert.NoError(t, err)
		defer r.Body.Close()

		fmt.Fprintf(w, `{
				"id": 10,
				"username": "raymond_smith",
				"name": "Raymond Smith",
				"state": "active",
				"created_at": "2012-10-22T14:13:35Z",
				"requested_at": "2012-10-22T14:13:35Z",
				"access_level": %d
			}`,
			*opt.AccessLevel)
	})

	created := time.Date(2012, 10, 22, 14, 13, 35, 0, time.UTC)
	expected := &AccessRequest{
		ID:          10,
		Username:    "raymond_smith",
		Name:        "Raymond Smith",
		State:       "active",
		CreatedAt:   &created,
		RequestedAt: &created,
		AccessLevel: DeveloperPermissions,
	}

	opt := &ApproveAccessRequestOptions{
		AccessLevel: Ptr(DeveloperPermissions),
	}

	request, resp, err := client.AccessRequests.ApproveProjectAccessRequest(1, 10, opt)
	assert.NoError(t, err)
	assert.NotNil(t, resp)
	assert.Equal(t, expected, request)

	request, resp, err = client.AccessRequests.ApproveProjectAccessRequest(1.5, 10, opt)
	assert.EqualError(t, err, "invalid ID type 1.5, the ID must be an int or a string")
	assert.Nil(t, resp)
	assert.Nil(t, request)

	request, resp, err = client.AccessRequests.ApproveProjectAccessRequest(2, 10, opt)
	assert.Error(t, err)
	assert.Equal(t, http.StatusNotFound, resp.StatusCode)
	assert.Nil(t, request)

	request, resp, err = client.AccessRequests.ApproveProjectAccessRequest(1, 10, opt, errorOption)
	assert.EqualError(t, err, "RequestOptionFunc returns an error")
	assert.Nil(t, resp)
	assert.Nil(t, request)
}

func TestApproveGroupAccessRequest(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1/access_requests/10/approve", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPut)

		var opt ApproveAccessRequestOptions
		err := json.NewDecoder(r.Body).Decode(&opt)
		assert.NoError(t, err)
		defer r.Body.Close()

		fmt.Fprintf(w, `{
				"id": 10,
				"username": "raymond_smith",
				"name": "Raymond Smith",
				"state": "active",
				"created_at": "2012-10-22T14:13:35Z",
				"requested_at": "2012-10-22T14:13:35Z",
				"access_level": %d
			}`,
			*opt.AccessLevel)
	})

	created := time.Date(2012, 10, 22, 14, 13, 35, 0, time.UTC)
	expected := &AccessRequest{
		ID:          10,
		Username:    "raymond_smith",
		Name:        "Raymond Smith",
		State:       "active",
		CreatedAt:   &created,
		RequestedAt: &created,
		AccessLevel: DeveloperPermissions,
	}

	opt := &ApproveAccessRequestOptions{
		AccessLevel: Ptr(DeveloperPermissions),
	}

	request, resp, err := client.AccessRequests.ApproveGroupAccessRequest(1, 10, opt)
	assert.NoError(t, err)
	assert.NotNil(t, resp)
	assert.Equal(t, expected, request)

	request, resp, err = client.AccessRequests.ApproveGroupAccessRequest(1.5, 10, opt)
	assert.EqualError(t, err, "invalid ID type 1.5, the ID must be an int or a string")
	assert.Nil(t, resp)
	assert.Nil(t, request)

	request, resp, err = client.AccessRequests.ApproveGroupAccessRequest(2, 10, opt)
	assert.Error(t, err)
	assert.Equal(t, http.StatusNotFound, resp.StatusCode)
	assert.Nil(t, request)

	request, resp, err = client.AccessRequests.ApproveGroupAccessRequest(1, 10, opt, errorOption)
	assert.EqualError(t, err, "RequestOptionFunc returns an error")
	assert.Nil(t, resp)
	assert.Nil(t, request)
}

func TestDenyProjectAccessRequest(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/access_requests/10", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodDelete)
	})

	resp, err := client.AccessRequests.DenyProjectAccessRequest(1, 10)
	assert.NoError(t, err)
	assert.NotNil(t, resp)

	resp, err = client.AccessRequests.DenyProjectAccessRequest(1.5, 10)
	assert.EqualError(t, err, "invalid ID type 1.5, the ID must be an int or a string")
	assert.Nil(t, resp)

	resp, err = client.AccessRequests.DenyProjectAccessRequest(2, 10)
	assert.Error(t, err)
	assert.Equal(t, http.StatusNotFound, resp.StatusCode)

	resp, err = client.AccessRequests.DenyProjectAccessRequest(1, 10, errorOption)
	assert.EqualError(t, err, "RequestOptionFunc returns an error")
	assert.Nil(t, resp)
}

func TestDenyGroupAccessRequest(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1/access_requests/10", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodDelete)
	})

	resp, err := client.AccessRequests.DenyGroupAccessRequest(1, 10)
	assert.NoError(t, err)
	assert.NotNil(t, resp)

	resp, err = client.AccessRequests.DenyGroupAccessRequest(1.5, 10)
	assert.EqualError(t, err, "invalid ID type 1.5, the ID must be an int or a string")
	assert.Nil(t, resp)

	resp, err = client.AccessRequests.DenyGroupAccessRequest(2, 10)
	assert.Error(t, err)
	assert.Equal(t, http.StatusNotFound, resp.StatusCode)

	resp, err = client.AccessRequests.DenyGroupAccessRequest(1, 10, errorOption)
	assert.EqualError(t, err, "RequestOptionFunc returns an error")
	assert.Nil(t, resp)
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/appearance.go000066400000000000000000000121341475761473200235060ustar00rootroot00000000000000//
// Copyright 2023, 徐晓伟 
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import "net/http"

// AppearanceService handles communication with appearance of the Gitlab API.
//
// Gitlab API docs : https://docs.gitlab.com/ee/api/appearance.html
type AppearanceService struct {
	client *Client
}

// Appearance represents a GitLab appearance.
//
// Gitlab API docs : https://docs.gitlab.com/ee/api/appearance.html
type Appearance struct {
	Title                       string `json:"title"`
	Description                 string `json:"description"`
	PWAName                     string `json:"pwa_name"`
	PWAShortName                string `json:"pwa_short_name"`
	PWADescription              string `json:"pwa_description"`
	PWAIcon                     string `json:"pwa_icon"`
	Logo                        string `json:"logo"`
	HeaderLogo                  string `json:"header_logo"`
	Favicon                     string `json:"favicon"`
	MemberGuidelines            string `json:"member_guidelines"`
	NewProjectGuidelines        string `json:"new_project_guidelines"`
	ProfileImageGuidelines      string `json:"profile_image_guidelines"`
	HeaderMessage               string `json:"header_message"`
	FooterMessage               string `json:"footer_message"`
	MessageBackgroundColor      string `json:"message_background_color"`
	MessageFontColor            string `json:"message_font_color"`
	EmailHeaderAndFooterEnabled bool   `json:"email_header_and_footer_enabled"`
}

// GetAppearance gets the current appearance configuration of the GitLab instance.
//
// Gitlab API docs:
// https://docs.gitlab.com/ee/api/appearance.html#get-current-appearance-configuration
func (s *AppearanceService) GetAppearance(options ...RequestOptionFunc) (*Appearance, *Response, error) {
	req, err := s.client.NewRequest(http.MethodGet, "application/appearance", nil, options)
	if err != nil {
		return nil, nil, err
	}

	as := new(Appearance)
	resp, err := s.client.Do(req, as)
	if err != nil {
		return nil, resp, err
	}

	return as, resp, nil
}

// ChangeAppearanceOptions represents the available ChangeAppearance() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/appearance.html#change-appearance-configuration
type ChangeAppearanceOptions struct {
	Title                       *string `url:"title,omitempty" json:"title,omitempty"`
	Description                 *string `url:"description,omitempty" json:"description,omitempty"`
	PWAName                     *string `url:"pwa_name,omitempty" json:"pwa_name,omitempty"`
	PWAShortName                *string `url:"pwa_short_name,omitempty" json:"pwa_short_name,omitempty"`
	PWADescription              *string `url:"pwa_description,omitempty" json:"pwa_description,omitempty"`
	PWAIcon                     *string `url:"pwa_icon,omitempty" json:"pwa_icon,omitempty"`
	Logo                        *string `url:"logo,omitempty" json:"logo,omitempty"`
	HeaderLogo                  *string `url:"header_logo,omitempty" json:"header_logo,omitempty"`
	Favicon                     *string `url:"favicon,omitempty" json:"favicon,omitempty"`
	MemberGuidelines            *string `url:"member_guidelines,omitempty" json:"member_guidelines,omitempty"`
	NewProjectGuidelines        *string `url:"new_project_guidelines,omitempty" json:"new_project_guidelines,omitempty"`
	ProfileImageGuidelines      *string `url:"profile_image_guidelines,omitempty" json:"profile_image_guidelines,omitempty"`
	HeaderMessage               *string `url:"header_message,omitempty" json:"header_message,omitempty"`
	FooterMessage               *string `url:"footer_message,omitempty" json:"footer_message,omitempty"`
	MessageBackgroundColor      *string `url:"message_background_color,omitempty" json:"message_background_color,omitempty"`
	MessageFontColor            *string `url:"message_font_color,omitempty" json:"message_font_color,omitempty"`
	EmailHeaderAndFooterEnabled *bool   `url:"email_header_and_footer_enabled,omitempty" json:"email_header_and_footer_enabled,omitempty"`
	URL                         *string `url:"url,omitempty" json:"url,omitempty"`
}

// ChangeAppearance changes the appearance configuration.
//
// Gitlab API docs:
// https://docs.gitlab.com/ee/api/appearance.html#change-appearance-configuration
func (s *AppearanceService) ChangeAppearance(opt *ChangeAppearanceOptions, options ...RequestOptionFunc) (*Appearance, *Response, error) {
	req, err := s.client.NewRequest(http.MethodPut, "application/appearance", opt, options)
	if err != nil {
		return nil, nil, err
	}

	as := new(Appearance)
	resp, err := s.client.Do(req, as)
	if err != nil {
		return nil, resp, err
	}

	return as, resp, nil
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/appearance_test.go000066400000000000000000000144361475761473200245540ustar00rootroot00000000000000//
// Copyright 2023, 徐晓伟 
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"fmt"
	"net/http"
	"reflect"
	"testing"
)

func TestGetAppearance(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/application/appearance", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprint(w, `{
 		 	"title": "GitLab Test Instance",
 		 	"description": "gitlab-test.example.com",
 		 	"pwa_name": "GitLab PWA",
 		 	"pwa_short_name": "GitLab",
 		 	"pwa_description": "GitLab as PWA",
 		 	"pwa_icon": "/uploads/-/system/appearance/pwa_icon/1/pwa_logo.png",
 		 	"logo": "/uploads/-/system/appearance/logo/1/logo.png",
 		 	"header_logo": "/uploads/-/system/appearance/header_logo/1/header.png",
 		 	"favicon": "/uploads/-/system/appearance/favicon/1/favicon.png",
 		 	"member_guidelines": "Please be nice.",
 		 	"new_project_guidelines": "Please read the FAQs for help.",
 		 	"profile_image_guidelines": "Custom profile image guidelines",
 		 	"header_message": "",
 		 	"footer_message": "",
 		 	"message_background_color": "#e75e40",
 		 	"message_font_color": "#ffffff",
 		 	"email_header_and_footer_enabled": false
 		}`)
	})

	appearance, _, err := client.Appearance.GetAppearance()
	if err != nil {
		t.Errorf("Appearance.GetAppearance returned error: %v", err)
	}

	want := &Appearance{
		Title:                       "GitLab Test Instance",
		Description:                 "gitlab-test.example.com",
		PWAName:                     "GitLab PWA",
		PWAShortName:                "GitLab",
		PWADescription:              "GitLab as PWA",
		PWAIcon:                     "/uploads/-/system/appearance/pwa_icon/1/pwa_logo.png",
		Logo:                        "/uploads/-/system/appearance/logo/1/logo.png",
		HeaderLogo:                  "/uploads/-/system/appearance/header_logo/1/header.png",
		Favicon:                     "/uploads/-/system/appearance/favicon/1/favicon.png",
		MemberGuidelines:            "Please be nice.",
		NewProjectGuidelines:        "Please read the FAQs for help.",
		ProfileImageGuidelines:      "Custom profile image guidelines",
		HeaderMessage:               "",
		FooterMessage:               "",
		MessageBackgroundColor:      "#e75e40",
		MessageFontColor:            "#ffffff",
		EmailHeaderAndFooterEnabled: false,
	}

	if !reflect.DeepEqual(want, appearance) {
		t.Errorf("Appearance.GetAppearance returned %+v, want %+v", appearance, want)
	}
}

func TestChangeAppearance(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/application/appearance", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPut)
		fmt.Fprint(w, `{
		 	"title": "GitLab Test Instance - 001",
 		 	"description": "gitlab-test.example.com",
 		 	"pwa_name": "GitLab PWA",
 		 	"pwa_short_name": "GitLab",
 		 	"pwa_description": "GitLab as PWA",
 		 	"pwa_icon": "/uploads/-/system/appearance/pwa_icon/1/pwa_logo.png",
 		 	"logo": "/uploads/-/system/appearance/logo/1/logo.png",
 		 	"header_logo": "/uploads/-/system/appearance/header_logo/1/header.png",
 		 	"favicon": "/uploads/-/system/appearance/favicon/1/favicon.png",
 		 	"member_guidelines": "Please be nice.",
 		 	"new_project_guidelines": "Please read the FAQs for help.",
 		 	"profile_image_guidelines": "Custom profile image guidelines",
 		 	"header_message": "",
 		 	"footer_message": "",
 		 	"message_background_color": "#e75e40",
 		 	"message_font_color": "#ffffff",
 		 	"email_header_and_footer_enabled": false
 		}`)
	})

	opt := &ChangeAppearanceOptions{
		Title:                       Ptr("GitLab Test Instance - 001"),
		Description:                 Ptr("gitlab-test.example.com"),
		PWAName:                     Ptr("GitLab PWA"),
		PWAShortName:                Ptr("GitLab"),
		PWADescription:              Ptr("GitLab as PWA"),
		PWAIcon:                     Ptr("/uploads/-/system/appearance/pwa_icon/1/pwa_logo.png"),
		Logo:                        Ptr("/uploads/-/system/appearance/logo/1/logo.png"),
		HeaderLogo:                  Ptr("/uploads/-/system/appearance/header_logo/1/header.png"),
		Favicon:                     Ptr("/uploads/-/system/appearance/favicon/1/favicon.png"),
		MemberGuidelines:            Ptr("Please be nice."),
		NewProjectGuidelines:        Ptr("Please read the FAQs for help."),
		ProfileImageGuidelines:      Ptr("Custom profile image guidelines"),
		HeaderMessage:               Ptr(""),
		FooterMessage:               Ptr(""),
		MessageBackgroundColor:      Ptr("#e75e40"),
		MessageFontColor:            Ptr("#ffffff"),
		EmailHeaderAndFooterEnabled: Ptr(false),
	}

	appearance, _, err := client.Appearance.ChangeAppearance(opt)
	if err != nil {
		t.Errorf("Appearance.ChangeAppearance returned error: %v", err)
	}

	want := &Appearance{
		Title:                       "GitLab Test Instance - 001",
		Description:                 "gitlab-test.example.com",
		PWAName:                     "GitLab PWA",
		PWAShortName:                "GitLab",
		PWADescription:              "GitLab as PWA",
		PWAIcon:                     "/uploads/-/system/appearance/pwa_icon/1/pwa_logo.png",
		Logo:                        "/uploads/-/system/appearance/logo/1/logo.png",
		HeaderLogo:                  "/uploads/-/system/appearance/header_logo/1/header.png",
		Favicon:                     "/uploads/-/system/appearance/favicon/1/favicon.png",
		MemberGuidelines:            "Please be nice.",
		NewProjectGuidelines:        "Please read the FAQs for help.",
		ProfileImageGuidelines:      "Custom profile image guidelines",
		HeaderMessage:               "",
		FooterMessage:               "",
		MessageBackgroundColor:      "#e75e40",
		MessageFontColor:            "#ffffff",
		EmailHeaderAndFooterEnabled: false,
	}

	if !reflect.DeepEqual(want, appearance) {
		t.Errorf("Appearance.GetAppearance returned %+v, want %+v", appearance, want)
	}
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/applications.go000066400000000000000000000066221475761473200241020ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"fmt"
	"net/http"
)

// ApplicationsService handles communication with administrables applications
// of the Gitlab API.
//
// Gitlab API docs : https://docs.gitlab.com/ee/api/applications.html
type ApplicationsService struct {
	client *Client
}

// Application represents a GitLab application
type Application struct {
	ID              int    `json:"id"`
	ApplicationID   string `json:"application_id"`
	ApplicationName string `json:"application_name"`
	Secret          string `json:"secret"`
	CallbackURL     string `json:"callback_url"`
	Confidential    bool   `json:"confidential"`
}

// CreateApplicationOptions represents the available CreateApplication() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/applications.html#create-an-application
type CreateApplicationOptions struct {
	Name         *string `url:"name,omitempty" json:"name,omitempty"`
	RedirectURI  *string `url:"redirect_uri,omitempty" json:"redirect_uri,omitempty"`
	Scopes       *string `url:"scopes,omitempty" json:"scopes,omitempty"`
	Confidential *bool   `url:"confidential,omitempty" json:"confidential,omitempty"`
}

// CreateApplication creates a new application owned by the authenticated user.
//
// Gitlab API docs : https://docs.gitlab.com/ee/api/applications.html#create-an-application
func (s *ApplicationsService) CreateApplication(opt *CreateApplicationOptions, options ...RequestOptionFunc) (*Application, *Response, error) {
	req, err := s.client.NewRequest(http.MethodPost, "applications", opt, options)
	if err != nil {
		return nil, nil, err
	}

	a := new(Application)
	resp, err := s.client.Do(req, a)
	if err != nil {
		return nil, resp, err
	}

	return a, resp, nil
}

// ListApplicationsOptions represents the available
// ListApplications() options.
type ListApplicationsOptions ListOptions

// ListApplications get a list of administrables applications by the authenticated user
//
// Gitlab API docs : https://docs.gitlab.com/ee/api/applications.html#list-all-applications
func (s *ApplicationsService) ListApplications(opt *ListApplicationsOptions, options ...RequestOptionFunc) ([]*Application, *Response, error) {
	req, err := s.client.NewRequest(http.MethodGet, "applications", opt, options)
	if err != nil {
		return nil, nil, err
	}

	var as []*Application
	resp, err := s.client.Do(req, &as)
	if err != nil {
		return nil, resp, err
	}

	return as, resp, nil
}

// DeleteApplication removes a specific application.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/applications.html#delete-an-application
func (s *ApplicationsService) DeleteApplication(application int, options ...RequestOptionFunc) (*Response, error) {
	u := fmt.Sprintf("applications/%d", application)

	req, err := s.client.NewRequest(http.MethodDelete, u, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/applications_test.go000066400000000000000000000047311475761473200251400ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"fmt"
	"net/http"
	"reflect"
	"testing"
)

func TestCreateApplication(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/applications",
		func(w http.ResponseWriter, r *http.Request) {
			testMethod(t, r, http.MethodPost)
			fmt.Fprint(w, `
{
	"id":1,
	"application_name":"testApplication"
}`)
		},
	)

	opt := &CreateApplicationOptions{
		Name: Ptr("testApplication"),
	}
	app, _, err := client.Applications.CreateApplication(opt)
	if err != nil {
		t.Errorf("Applications.CreateApplication returned error: %v", err)
	}

	want := &Application{
		ID:              1,
		ApplicationName: "testApplication",
	}
	if !reflect.DeepEqual(want, app) {
		t.Errorf("Applications.CreateApplication returned %+v, want %+v", app, want)
	}
}

func TestListApplications(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/applications",
		func(w http.ResponseWriter, r *http.Request) {
			testMethod(t, r, http.MethodGet)
			fmt.Fprint(w, `[
	{"id":1},
	{"id":2}
]`)
		},
	)

	apps, _, err := client.Applications.ListApplications(&ListApplicationsOptions{})
	if err != nil {
		t.Errorf("Applications.ListApplications returned error: %v", err)
	}

	want := []*Application{
		{ID: 1},
		{ID: 2},
	}
	if !reflect.DeepEqual(want, apps) {
		t.Errorf("Applications.ListApplications returned %+v, want %+v", apps, want)
	}
}

func TestDeleteApplication(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/applications/4",
		func(w http.ResponseWriter, r *http.Request) {
			testMethod(t, r, http.MethodDelete)
			w.WriteHeader(http.StatusAccepted)
		},
	)

	resp, err := client.Applications.DeleteApplication(4)
	if err != nil {
		t.Errorf("Applications.DeleteApplication returned error: %v", err)
	}

	want := http.StatusAccepted
	got := resp.StatusCode
	if got != want {
		t.Errorf("Applications.DeleteApplication returned status code %d, want %d", got, want)
	}
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/audit_events.go000066400000000000000000000147471475761473200241150ustar00rootroot00000000000000package gitlab

import (
	"fmt"
	"net/http"
	"time"
)

// AuditEvent represents an audit event for a group, a project or the instance.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/audit_events.html
type AuditEvent struct {
	ID         int               `json:"id"`
	AuthorID   int               `json:"author_id"`
	EntityID   int               `json:"entity_id"`
	EntityType string            `json:"entity_type"`
	EventName  string            `json:"event_name"`
	Details    AuditEventDetails `json:"details"`
	CreatedAt  *time.Time        `json:"created_at"`
	EventType  string            `json:"event_type"`
}

// AuditEventDetails represents the details portion of an audit event for
// a group, a project or the instance. The exact fields that are returned
// for an audit event depend on the action being recorded.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/audit_events.html
type AuditEventDetails struct {
	With          string      `json:"with"`
	Add           string      `json:"add"`
	As            string      `json:"as"`
	Change        string      `json:"change"`
	From          string      `json:"from"`
	To            string      `json:"to"`
	Remove        string      `json:"remove"`
	CustomMessage string      `json:"custom_message"`
	AuthorName    string      `json:"author_name"`
	AuthorEmail   string      `json:"author_email"`
	AuthorClass   string      `json:"author_class"`
	TargetID      interface{} `json:"target_id"`
	TargetType    string      `json:"target_type"`
	TargetDetails string      `json:"target_details"`
	IPAddress     string      `json:"ip_address"`
	EntityPath    string      `json:"entity_path"`
	FailedLogin   string      `json:"failed_login"`
	EventName     string      `json:"event_name"`
}

// AuditEventsService handles communication with the project/group/instance
// audit event related methods of the GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/audit_events.html
type AuditEventsService struct {
	client *Client
}

// ListAuditEventsOptions represents the available ListProjectAuditEvents(),
// ListGroupAuditEvents() or ListInstanceAuditEvents() options.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/audit_events.html
type ListAuditEventsOptions struct {
	ListOptions
	CreatedAfter  *time.Time `url:"created_after,omitempty" json:"created_after,omitempty"`
	CreatedBefore *time.Time `url:"created_before,omitempty" json:"created_before,omitempty"`
}

// ListInstanceAuditEvents gets a list of audit events for instance.
// Authentication as Administrator is required.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/audit_events.html#retrieve-all-instance-audit-events
func (s *AuditEventsService) ListInstanceAuditEvents(opt *ListAuditEventsOptions, options ...RequestOptionFunc) ([]*AuditEvent, *Response, error) {
	req, err := s.client.NewRequest(http.MethodGet, "audit_events", opt, options)
	if err != nil {
		return nil, nil, err
	}

	var aes []*AuditEvent
	resp, err := s.client.Do(req, &aes)
	if err != nil {
		return nil, resp, err
	}

	return aes, resp, nil
}

// GetInstanceAuditEvent gets a specific instance audit event.
// Authentication as Administrator is required.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/audit_events.html#retrieve-single-instance-audit-event
func (s *AuditEventsService) GetInstanceAuditEvent(event int, options ...RequestOptionFunc) (*AuditEvent, *Response, error) {
	u := fmt.Sprintf("audit_events/%d", event)

	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	ae := new(AuditEvent)
	resp, err := s.client.Do(req, ae)
	if err != nil {
		return nil, resp, err
	}

	return ae, resp, nil
}

// ListGroupAuditEvents gets a list of audit events for the specified group
// viewable by the authenticated user.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/audit_events.html#retrieve-all-group-audit-events
func (s *AuditEventsService) ListGroupAuditEvents(gid interface{}, opt *ListAuditEventsOptions, options ...RequestOptionFunc) ([]*AuditEvent, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/audit_events", PathEscape(group))

	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var aes []*AuditEvent
	resp, err := s.client.Do(req, &aes)
	if err != nil {
		return nil, resp, err
	}

	return aes, resp, nil
}

// GetGroupAuditEvent gets a specific group audit event.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/audit_events.html#retrieve-a-specific-group-audit-event
func (s *AuditEventsService) GetGroupAuditEvent(gid interface{}, event int, options ...RequestOptionFunc) (*AuditEvent, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/audit_events/%d", PathEscape(group), event)

	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	ae := new(AuditEvent)
	resp, err := s.client.Do(req, ae)
	if err != nil {
		return nil, resp, err
	}

	return ae, resp, nil
}

// ListProjectAuditEvents gets a list of audit events for the specified project
// viewable by the authenticated user.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/audit_events.html#retrieve-all-project-audit-events
func (s *AuditEventsService) ListProjectAuditEvents(pid interface{}, opt *ListAuditEventsOptions, options ...RequestOptionFunc) ([]*AuditEvent, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/audit_events", PathEscape(project))

	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var aes []*AuditEvent
	resp, err := s.client.Do(req, &aes)
	if err != nil {
		return nil, resp, err
	}

	return aes, resp, nil
}

// GetProjectAuditEvent gets a specific project audit event.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/audit_events.html#retrieve-a-specific-project-audit-event
func (s *AuditEventsService) GetProjectAuditEvent(pid interface{}, event int, options ...RequestOptionFunc) (*AuditEvent, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/audit_events/%d", PathEscape(project), event)

	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	ae := new(AuditEvent)
	resp, err := s.client.Do(req, ae)
	if err != nil {
		return nil, resp, err
	}

	return ae, resp, nil
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/audit_events_test.go000066400000000000000000000257431475761473200251520ustar00rootroot00000000000000package gitlab

import (
	"fmt"
	"net/http"
	"testing"

	"github.com/stretchr/testify/require"
)

func TestAuditEventsService_ListInstanceAuditEvents(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/audit_events", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
			[
			  {
				"id": 1,
				"author_id": 1,
				"entity_id": 6,
				"entity_type": "Project",
				"event_name": "project_archived",
				"details": {
				  "event_name": "project_archived",
				  "custom_message": "Project archived",
				  "author_name": "Venkatesh Thalluri",
				  "target_id": "flightjs/flight",
				  "target_type": "Project",
				  "target_details": "flightjs/flight",
				  "ip_address": "127.0.0.1",
				  "entity_path": "flightjs/flight"
				}
			  }
			]
		`)
	})

	want := []*AuditEvent{{
		ID:         1,
		AuthorID:   1,
		EntityID:   6,
		EntityType: "Project",
		EventName:  "project_archived",
		Details: AuditEventDetails{
			EventName:     "project_archived",
			CustomMessage: "Project archived",
			AuthorName:    "Venkatesh Thalluri",
			TargetID:      "flightjs/flight",
			TargetType:    "Project",
			TargetDetails: "flightjs/flight",
			IPAddress:     "127.0.0.1",
			EntityPath:    "flightjs/flight",
		},
	}}

	aes, resp, err := client.AuditEvents.ListInstanceAuditEvents(nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, aes)

	aes, resp, err = client.AuditEvents.ListInstanceAuditEvents(nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, aes)
}

func TestAuditEventsService_ListInstanceAuditEvents_StatusNotFound(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/audit_events", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		w.WriteHeader(http.StatusNotFound)
	})

	aes, resp, err := client.AuditEvents.ListInstanceAuditEvents(nil)
	require.Error(t, err)
	require.Nil(t, aes)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestAuditEventsService_GetInstanceAuditEvent(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/audit_events/1", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
		  {
			"id": 1,
			"author_id": 1,
			"entity_id": 6,
			"entity_type": "Project",
			"event_name": "project_archived",
			"details": {
			  "event_name": "project_archived",
			  "custom_message": "Project archived",
			  "author_name": "Venkatesh Thalluri",
			  "target_id": "flightjs/flight",
			  "target_type": "Project",
			  "target_details": "flightjs/flight",
			  "ip_address": "127.0.0.1",
			  "entity_path": "flightjs/flight"
			}
		  }
		`)
	})

	want := &AuditEvent{
		ID:         1,
		AuthorID:   1,
		EntityID:   6,
		EventName:  "project_archived",
		EntityType: "Project",
		Details: AuditEventDetails{
			EventName:     "project_archived",
			CustomMessage: "Project archived",
			AuthorName:    "Venkatesh Thalluri",
			TargetID:      "flightjs/flight",
			TargetType:    "Project",
			TargetDetails: "flightjs/flight",
			IPAddress:     "127.0.0.1",
			EntityPath:    "flightjs/flight",
		},
	}

	ae, resp, err := client.AuditEvents.GetInstanceAuditEvent(1, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, ae)

	ae, resp, err = client.AuditEvents.GetInstanceAuditEvent(1, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, ae)

	ae, resp, err = client.AuditEvents.GetInstanceAuditEvent(3, nil)
	require.Error(t, err)
	require.Nil(t, ae)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestAuditEventsService_ListGroupAuditEvents(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/6/audit_events", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
			[
			  {
				"id": 1,
				"author_id": 1,
				"entity_id": 6,
				"entity_type": "Group",
			    "event_name": "group_archived",
				"details": {
			      "event_name": "group_archived",
				  "custom_message": "Group archived",
				  "author_name": "Venkatesh Thalluri",
				  "target_id": "flightjs/flight",
				  "target_type": "Group",
				  "target_details": "flightjs/flight",
				  "ip_address": "127.0.0.1",
				  "entity_path": "flightjs/flight"
				}
			  }
			]
		`)
	})

	want := []*AuditEvent{{
		ID:         1,
		AuthorID:   1,
		EntityID:   6,
		EntityType: "Group",
		EventName:  "group_archived",
		Details: AuditEventDetails{
			EventName:     "group_archived",
			CustomMessage: "Group archived",
			AuthorName:    "Venkatesh Thalluri",
			TargetID:      "flightjs/flight",
			TargetType:    "Group",
			TargetDetails: "flightjs/flight",
			IPAddress:     "127.0.0.1",
			EntityPath:    "flightjs/flight",
		},
	}}

	aes, resp, err := client.AuditEvents.ListGroupAuditEvents(6, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, aes)

	aes, resp, err = client.AuditEvents.ListGroupAuditEvents(6.01, nil)
	require.EqualError(t, err, "invalid ID type 6.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, aes)

	aes, resp, err = client.AuditEvents.ListGroupAuditEvents(6, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, aes)

	aes, resp, err = client.AuditEvents.ListGroupAuditEvents(3, nil)
	require.Error(t, err)
	require.Nil(t, aes)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestAuditEventsService_GetGroupAuditEvent(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/6/audit_events/1", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
		  {
			"id": 1,
			"author_id": 1,
			"entity_id": 6,
			"entity_type": "Group",
			"event_name": "group_archived",
			"details": {
			  "event_name": "group_archived",
			  "custom_message": "Group archived",
			  "author_name": "Venkatesh Thalluri",
			  "target_id": "flightjs/flight",
			  "target_type": "Group",
			  "target_details": "flightjs/flight",
			  "ip_address": "127.0.0.1",
			  "entity_path": "flightjs/flight"
			}
		  }
		`)
	})

	want := &AuditEvent{
		ID:         1,
		AuthorID:   1,
		EntityID:   6,
		EntityType: "Group",
		EventName:  "group_archived",
		Details: AuditEventDetails{
			EventName:     "group_archived",
			CustomMessage: "Group archived",
			AuthorName:    "Venkatesh Thalluri",
			TargetID:      "flightjs/flight",
			TargetType:    "Group",
			TargetDetails: "flightjs/flight",
			IPAddress:     "127.0.0.1",
			EntityPath:    "flightjs/flight",
		},
	}

	ae, resp, err := client.AuditEvents.GetGroupAuditEvent(6, 1, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, ae)

	ae, resp, err = client.AuditEvents.GetGroupAuditEvent(6.01, 1, nil)
	require.EqualError(t, err, "invalid ID type 6.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, ae)

	ae, resp, err = client.AuditEvents.GetGroupAuditEvent(6, 1, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, ae)

	ae, resp, err = client.AuditEvents.GetGroupAuditEvent(3, 1, nil)
	require.Error(t, err)
	require.Nil(t, ae)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestAuditEventsService_ListProjectAuditEvents(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/6/audit_events", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
			[
			  {
				"id": 1,
				"author_id": 1,
				"entity_id": 6,
				"entity_type": "Project",
				"event_name": "project_archived",
				"details": {
				  "event_name": "project_archived",
				  "custom_message": "Project archived",
				  "author_name": "Venkatesh Thalluri",
				  "target_id": "flightjs/flight",
				  "target_type": "Project",
				  "target_details": "flightjs/flight",
				  "ip_address": "127.0.0.1",
				  "entity_path": "flightjs/flight"
				}
			  }
			]
		`)
	})

	want := []*AuditEvent{{
		ID:         1,
		AuthorID:   1,
		EntityID:   6,
		EntityType: "Project",
		EventName:  "project_archived",
		Details: AuditEventDetails{
			EventName:     "project_archived",
			CustomMessage: "Project archived",
			AuthorName:    "Venkatesh Thalluri",
			TargetID:      "flightjs/flight",
			TargetType:    "Project",
			TargetDetails: "flightjs/flight",
			IPAddress:     "127.0.0.1",
			EntityPath:    "flightjs/flight",
		},
	}}

	aes, resp, err := client.AuditEvents.ListProjectAuditEvents(6, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, aes)

	aes, resp, err = client.AuditEvents.ListProjectAuditEvents(6.01, nil)
	require.EqualError(t, err, "invalid ID type 6.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, aes)

	aes, resp, err = client.AuditEvents.ListProjectAuditEvents(6, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, aes)

	aes, resp, err = client.AuditEvents.ListProjectAuditEvents(3, nil)
	require.Error(t, err)
	require.Nil(t, aes)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestAuditEventsService_GetProjectAuditEvent(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/6/audit_events/1", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
		  {
			"id": 1,
			"author_id": 1,
			"entity_id": 6,
			"entity_type": "Project",
			"event_name": "project_archived",
			"details": {
			  "event_name": "project_archived",
			  "custom_message": "Project archived",
			  "author_name": "Venkatesh Thalluri",
			  "target_id": "flightjs/flight",
			  "target_type": "Project",
			  "target_details": "flightjs/flight",
			  "ip_address": "127.0.0.1",
			  "entity_path": "flightjs/flight"
			}
		  }
		`)
	})

	want := &AuditEvent{
		ID:         1,
		AuthorID:   1,
		EntityID:   6,
		EntityType: "Project",
		EventName:  "project_archived",
		Details: AuditEventDetails{
			EventName:     "project_archived",
			CustomMessage: "Project archived",
			AuthorName:    "Venkatesh Thalluri",
			TargetID:      "flightjs/flight",
			TargetType:    "Project",
			TargetDetails: "flightjs/flight",
			IPAddress:     "127.0.0.1",
			EntityPath:    "flightjs/flight",
		},
	}

	ae, resp, err := client.AuditEvents.GetProjectAuditEvent(6, 1, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, ae)

	ae, resp, err = client.AuditEvents.GetProjectAuditEvent(6.01, 1, nil)
	require.EqualError(t, err, "invalid ID type 6.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, ae)

	ae, resp, err = client.AuditEvents.GetProjectAuditEvent(6, 1, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, ae)

	ae, resp, err = client.AuditEvents.GetProjectAuditEvent(3, 1, nil)
	require.Error(t, err)
	require.Nil(t, ae)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/avatar.go000066400000000000000000000035531475761473200226720ustar00rootroot00000000000000//
// Copyright 2021, Pavel Kostohrys
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"net/http"
)

// AvatarRequestsService handles communication with the avatar related methods
// of the GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/avatar.html
type AvatarRequestsService struct {
	client *Client
}

// Avatar represents a GitLab avatar.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/avatar.html
type Avatar struct {
	AvatarURL string `json:"avatar_url"`
}

// GetAvatarOptions represents the available GetAvatar() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/avatar.html#get-a-single-avatar-url
type GetAvatarOptions struct {
	Email *string `url:"email,omitempty" json:"email,omitempty"`
	Size  *int    `url:"size,omitempty" json:"size,omitempty"`
}

// GetAvatar gets the avatar URL for a user with the given email address.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/avatar.html#get-a-single-avatar-url
func (s *AvatarRequestsService) GetAvatar(opt *GetAvatarOptions, options ...RequestOptionFunc) (*Avatar, *Response, error) {
	req, err := s.client.NewRequest(http.MethodGet, "avatar", opt, options)
	if err != nil {
		return nil, nil, err
	}

	avatar := new(Avatar)
	response, err := s.client.Do(req, avatar)
	if err != nil {
		return nil, response, err
	}

	return avatar, response, nil
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/avatar_test.go000066400000000000000000000027231475761473200237270ustar00rootroot00000000000000//
// Copyright 2021, Pavel Kostohrys
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"encoding/json"
	"net/http"
	"testing"
)

func TestGetAvatar(t *testing.T) {
	mux, client := setup(t)

	const url = "https://www.gravatar.com/avatar/10e6bf7bcf22c2f00a3ef684b4ada178"

	mux.HandleFunc("/api/v4/avatar",
		func(w http.ResponseWriter, r *http.Request) {
			testMethod(t, r, http.MethodGet)
			w.WriteHeader(http.StatusAccepted)
			avatar := Avatar{AvatarURL: url}
			resp, _ := json.Marshal(avatar)
			_, _ = w.Write(resp)
		},
	)

	opt := &GetAvatarOptions{Email: Ptr("sander@vanharmelen.nnl")}
	avatar, resp, err := client.Avatar.GetAvatar(opt)
	if err != nil {
		t.Fatalf("Avatar.GetAvatar returned error: %v", err)
	}

	if resp.Status != "202 Accepted" {
		t.Fatalf("Avatar.GetAvatar returned wrong status code: %v", resp.Status)
	}

	if url != avatar.AvatarURL {
		t.Errorf("Avatar.GetAvatar wrong result %s, want %s", avatar.AvatarURL, url)
	}
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/award_emojis.go000066400000000000000000000423151475761473200240570ustar00rootroot00000000000000//
// Copyright 2021, Arkbriar
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"fmt"
	"net/http"
	"time"
)

// AwardEmojiService handles communication with the emoji awards related methods
// of the GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/award_emoji.html
type AwardEmojiService struct {
	client *Client
}

// AwardEmoji represents a GitLab Award Emoji.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/award_emoji.html
type AwardEmoji struct {
	ID   int    `json:"id"`
	Name string `json:"name"`
	User struct {
		Name      string `json:"name"`
		Username  string `json:"username"`
		ID        int    `json:"id"`
		State     string `json:"state"`
		AvatarURL string `json:"avatar_url"`
		WebURL    string `json:"web_url"`
	} `json:"user"`
	CreatedAt     *time.Time `json:"created_at"`
	UpdatedAt     *time.Time `json:"updated_at"`
	AwardableID   int        `json:"awardable_id"`
	AwardableType string     `json:"awardable_type"`
}

const (
	awardMergeRequest = "merge_requests"
	awardIssue        = "issues"
	awardSnippets     = "snippets"
)

// ListAwardEmojiOptions represents the available options for listing emoji
// for each resources
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/award_emoji.html
type ListAwardEmojiOptions ListOptions

// ListMergeRequestAwardEmoji gets a list of all award emoji on the merge request.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/award_emoji.html#list-an-awardables-award-emojis
func (s *AwardEmojiService) ListMergeRequestAwardEmoji(pid interface{}, mergeRequestIID int, opt *ListAwardEmojiOptions, options ...RequestOptionFunc) ([]*AwardEmoji, *Response, error) {
	return s.listAwardEmoji(pid, awardMergeRequest, mergeRequestIID, opt, options...)
}

// ListIssueAwardEmoji gets a list of all award emoji on the issue.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/award_emoji.html#list-an-awardables-award-emojis
func (s *AwardEmojiService) ListIssueAwardEmoji(pid interface{}, issueIID int, opt *ListAwardEmojiOptions, options ...RequestOptionFunc) ([]*AwardEmoji, *Response, error) {
	return s.listAwardEmoji(pid, awardIssue, issueIID, opt, options...)
}

// ListSnippetAwardEmoji gets a list of all award emoji on the snippet.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/award_emoji.html#list-an-awardables-award-emojis
func (s *AwardEmojiService) ListSnippetAwardEmoji(pid interface{}, snippetID int, opt *ListAwardEmojiOptions, options ...RequestOptionFunc) ([]*AwardEmoji, *Response, error) {
	return s.listAwardEmoji(pid, awardSnippets, snippetID, opt, options...)
}

func (s *AwardEmojiService) listAwardEmoji(pid interface{}, resource string, resourceID int, opt *ListAwardEmojiOptions, options ...RequestOptionFunc) ([]*AwardEmoji, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/%s/%d/award_emoji",
		PathEscape(project),
		resource,
		resourceID,
	)

	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var as []*AwardEmoji
	resp, err := s.client.Do(req, &as)
	if err != nil {
		return nil, resp, err
	}

	return as, resp, nil
}

// GetMergeRequestAwardEmoji get an award emoji from merge request.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/award_emoji.html#get-single-award-emoji
func (s *AwardEmojiService) GetMergeRequestAwardEmoji(pid interface{}, mergeRequestIID, awardID int, options ...RequestOptionFunc) (*AwardEmoji, *Response, error) {
	return s.getAwardEmoji(pid, awardMergeRequest, mergeRequestIID, awardID, options...)
}

// GetIssueAwardEmoji get an award emoji from issue.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/award_emoji.html#get-single-award-emoji
func (s *AwardEmojiService) GetIssueAwardEmoji(pid interface{}, issueIID, awardID int, options ...RequestOptionFunc) (*AwardEmoji, *Response, error) {
	return s.getAwardEmoji(pid, awardIssue, issueIID, awardID, options...)
}

// GetSnippetAwardEmoji get an award emoji from snippet.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/award_emoji.html#get-single-award-emoji
func (s *AwardEmojiService) GetSnippetAwardEmoji(pid interface{}, snippetID, awardID int, options ...RequestOptionFunc) (*AwardEmoji, *Response, error) {
	return s.getAwardEmoji(pid, awardSnippets, snippetID, awardID, options...)
}

func (s *AwardEmojiService) getAwardEmoji(pid interface{}, resource string, resourceID, awardID int, options ...RequestOptionFunc) (*AwardEmoji, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/%s/%d/award_emoji/%d",
		PathEscape(project),
		resource,
		resourceID,
		awardID,
	)

	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	a := new(AwardEmoji)
	resp, err := s.client.Do(req, &a)
	if err != nil {
		return nil, resp, err
	}

	return a, resp, nil
}

// CreateAwardEmojiOptions represents the available options for awarding emoji
// for a resource
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/award_emoji.html#award-a-new-emoji
type CreateAwardEmojiOptions struct {
	Name string `json:"name"`
}

// CreateMergeRequestAwardEmoji get an award emoji from merge request.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/award_emoji.html#award-a-new-emoji
func (s *AwardEmojiService) CreateMergeRequestAwardEmoji(pid interface{}, mergeRequestIID int, opt *CreateAwardEmojiOptions, options ...RequestOptionFunc) (*AwardEmoji, *Response, error) {
	return s.createAwardEmoji(pid, awardMergeRequest, mergeRequestIID, opt, options...)
}

// CreateIssueAwardEmoji get an award emoji from issue.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/award_emoji.html#award-a-new-emoji
func (s *AwardEmojiService) CreateIssueAwardEmoji(pid interface{}, issueIID int, opt *CreateAwardEmojiOptions, options ...RequestOptionFunc) (*AwardEmoji, *Response, error) {
	return s.createAwardEmoji(pid, awardIssue, issueIID, opt, options...)
}

// CreateSnippetAwardEmoji get an award emoji from snippet.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/award_emoji.html#award-a-new-emoji
func (s *AwardEmojiService) CreateSnippetAwardEmoji(pid interface{}, snippetID int, opt *CreateAwardEmojiOptions, options ...RequestOptionFunc) (*AwardEmoji, *Response, error) {
	return s.createAwardEmoji(pid, awardSnippets, snippetID, opt, options...)
}

func (s *AwardEmojiService) createAwardEmoji(pid interface{}, resource string, resourceID int, opt *CreateAwardEmojiOptions, options ...RequestOptionFunc) (*AwardEmoji, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/%s/%d/award_emoji",
		PathEscape(project),
		resource,
		resourceID,
	)

	req, err := s.client.NewRequest(http.MethodPost, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	a := new(AwardEmoji)
	resp, err := s.client.Do(req, &a)
	if err != nil {
		return nil, resp, err
	}

	return a, resp, nil
}

// DeleteIssueAwardEmoji delete award emoji on an issue.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/award_emoji.html#delete-an-award-emoji
func (s *AwardEmojiService) DeleteIssueAwardEmoji(pid interface{}, issueIID, awardID int, options ...RequestOptionFunc) (*Response, error) {
	return s.deleteAwardEmoji(pid, awardIssue, issueIID, awardID, options...)
}

// DeleteMergeRequestAwardEmoji delete award emoji on a merge request.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/award_emoji.html#delete-an-award-emoji
func (s *AwardEmojiService) DeleteMergeRequestAwardEmoji(pid interface{}, mergeRequestIID, awardID int, options ...RequestOptionFunc) (*Response, error) {
	return s.deleteAwardEmoji(pid, awardMergeRequest, mergeRequestIID, awardID, options...)
}

// DeleteSnippetAwardEmoji delete award emoji on a snippet.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/award_emoji.html#delete-an-award-emoji
func (s *AwardEmojiService) DeleteSnippetAwardEmoji(pid interface{}, snippetID, awardID int, options ...RequestOptionFunc) (*Response, error) {
	return s.deleteAwardEmoji(pid, awardSnippets, snippetID, awardID, options...)
}

// DeleteAwardEmoji Delete an award emoji on the specified resource.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/award_emoji.html#delete-an-award-emoji
func (s *AwardEmojiService) deleteAwardEmoji(pid interface{}, resource string, resourceID, awardID int, options ...RequestOptionFunc) (*Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("projects/%s/%s/%d/award_emoji/%d", PathEscape(project), resource,
		resourceID, awardID)

	req, err := s.client.NewRequest(http.MethodDelete, u, nil, options)
	if err != nil {
		return nil, err
	}
	return s.client.Do(req, nil)
}

// ListIssuesAwardEmojiOnNote gets a list of all award emoji on a note from the
// issue.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/award_emoji.html#list-a-comments-award-emojis
func (s *AwardEmojiService) ListIssuesAwardEmojiOnNote(pid interface{}, issueID, noteID int, opt *ListAwardEmojiOptions, options ...RequestOptionFunc) ([]*AwardEmoji, *Response, error) {
	return s.listAwardEmojiOnNote(pid, awardIssue, issueID, noteID, opt, options...)
}

// ListMergeRequestAwardEmojiOnNote gets a list of all award emoji on a note
// from the merge request.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/award_emoji.html#list-a-comments-award-emojis
func (s *AwardEmojiService) ListMergeRequestAwardEmojiOnNote(pid interface{}, mergeRequestIID, noteID int, opt *ListAwardEmojiOptions, options ...RequestOptionFunc) ([]*AwardEmoji, *Response, error) {
	return s.listAwardEmojiOnNote(pid, awardMergeRequest, mergeRequestIID, noteID, opt, options...)
}

// ListSnippetAwardEmojiOnNote gets a list of all award emoji on a note from the
// snippet.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/award_emoji.html#list-a-comments-award-emojis
func (s *AwardEmojiService) ListSnippetAwardEmojiOnNote(pid interface{}, snippetIID, noteID int, opt *ListAwardEmojiOptions, options ...RequestOptionFunc) ([]*AwardEmoji, *Response, error) {
	return s.listAwardEmojiOnNote(pid, awardSnippets, snippetIID, noteID, opt, options...)
}

func (s *AwardEmojiService) listAwardEmojiOnNote(pid interface{}, resources string, ressourceID, noteID int, opt *ListAwardEmojiOptions, options ...RequestOptionFunc) ([]*AwardEmoji, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/%s/%d/notes/%d/award_emoji", PathEscape(project), resources,
		ressourceID, noteID)

	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var as []*AwardEmoji
	resp, err := s.client.Do(req, &as)
	if err != nil {
		return nil, resp, err
	}

	return as, resp, nil
}

// GetIssuesAwardEmojiOnNote gets an award emoji on a note from an issue.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/award_emoji.html#get-an-award-emoji-for-a-comment
func (s *AwardEmojiService) GetIssuesAwardEmojiOnNote(pid interface{}, issueID, noteID, awardID int, options ...RequestOptionFunc) (*AwardEmoji, *Response, error) {
	return s.getSingleNoteAwardEmoji(pid, awardIssue, issueID, noteID, awardID, options...)
}

// GetMergeRequestAwardEmojiOnNote gets an award emoji on a note from a
// merge request.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/award_emoji.html#get-an-award-emoji-for-a-comment
func (s *AwardEmojiService) GetMergeRequestAwardEmojiOnNote(pid interface{}, mergeRequestIID, noteID, awardID int, options ...RequestOptionFunc) (*AwardEmoji, *Response, error) {
	return s.getSingleNoteAwardEmoji(pid, awardMergeRequest, mergeRequestIID, noteID, awardID,
		options...)
}

// GetSnippetAwardEmojiOnNote gets an award emoji on a note from a snippet.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/award_emoji.html#get-an-award-emoji-for-a-comment
func (s *AwardEmojiService) GetSnippetAwardEmojiOnNote(pid interface{}, snippetIID, noteID, awardID int, options ...RequestOptionFunc) (*AwardEmoji, *Response, error) {
	return s.getSingleNoteAwardEmoji(pid, awardSnippets, snippetIID, noteID, awardID, options...)
}

func (s *AwardEmojiService) getSingleNoteAwardEmoji(pid interface{}, ressource string, resourceID, noteID, awardID int, options ...RequestOptionFunc) (*AwardEmoji, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/%s/%d/notes/%d/award_emoji/%d",
		PathEscape(project),
		ressource,
		resourceID,
		noteID,
		awardID,
	)

	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	a := new(AwardEmoji)
	resp, err := s.client.Do(req, &a)
	if err != nil {
		return nil, resp, err
	}

	return a, resp, nil
}

// CreateIssuesAwardEmojiOnNote gets an award emoji on a note from an issue.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/award_emoji.html#award-a-new-emoji-on-a-comment
func (s *AwardEmojiService) CreateIssuesAwardEmojiOnNote(pid interface{}, issueID, noteID int, opt *CreateAwardEmojiOptions, options ...RequestOptionFunc) (*AwardEmoji, *Response, error) {
	return s.createAwardEmojiOnNote(pid, awardIssue, issueID, noteID, opt, options...)
}

// CreateMergeRequestAwardEmojiOnNote gets an award emoji on a note from a
// merge request.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/award_emoji.html#award-a-new-emoji-on-a-comment
func (s *AwardEmojiService) CreateMergeRequestAwardEmojiOnNote(pid interface{}, mergeRequestIID, noteID int, opt *CreateAwardEmojiOptions, options ...RequestOptionFunc) (*AwardEmoji, *Response, error) {
	return s.createAwardEmojiOnNote(pid, awardMergeRequest, mergeRequestIID, noteID, opt, options...)
}

// CreateSnippetAwardEmojiOnNote gets an award emoji on a note from a snippet.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/award_emoji.html#award-a-new-emoji-on-a-comment
func (s *AwardEmojiService) CreateSnippetAwardEmojiOnNote(pid interface{}, snippetIID, noteID int, opt *CreateAwardEmojiOptions, options ...RequestOptionFunc) (*AwardEmoji, *Response, error) {
	return s.createAwardEmojiOnNote(pid, awardSnippets, snippetIID, noteID, opt, options...)
}

// CreateAwardEmojiOnNote award emoji on a note.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/award_emoji.html#award-a-new-emoji-on-a-comment
func (s *AwardEmojiService) createAwardEmojiOnNote(pid interface{}, resource string, resourceID, noteID int, opt *CreateAwardEmojiOptions, options ...RequestOptionFunc) (*AwardEmoji, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/%s/%d/notes/%d/award_emoji",
		PathEscape(project),
		resource,
		resourceID,
		noteID,
	)

	req, err := s.client.NewRequest(http.MethodPost, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	a := new(AwardEmoji)
	resp, err := s.client.Do(req, &a)
	if err != nil {
		return nil, resp, err
	}

	return a, resp, nil
}

// DeleteIssuesAwardEmojiOnNote deletes an award emoji on a note from an issue.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/award_emoji.html#delete-an-award-emoji-from-a-comment
func (s *AwardEmojiService) DeleteIssuesAwardEmojiOnNote(pid interface{}, issueID, noteID, awardID int, options ...RequestOptionFunc) (*Response, error) {
	return s.deleteAwardEmojiOnNote(pid, awardIssue, issueID, noteID, awardID, options...)
}

// DeleteMergeRequestAwardEmojiOnNote deletes an award emoji on a note from a
// merge request.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/award_emoji.html#delete-an-award-emoji-from-a-comment
func (s *AwardEmojiService) DeleteMergeRequestAwardEmojiOnNote(pid interface{}, mergeRequestIID, noteID, awardID int, options ...RequestOptionFunc) (*Response, error) {
	return s.deleteAwardEmojiOnNote(pid, awardMergeRequest, mergeRequestIID, noteID, awardID,
		options...)
}

// DeleteSnippetAwardEmojiOnNote deletes an award emoji on a note from a snippet.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/award_emoji.html#delete-an-award-emoji-from-a-comment
func (s *AwardEmojiService) DeleteSnippetAwardEmojiOnNote(pid interface{}, snippetIID, noteID, awardID int, options ...RequestOptionFunc) (*Response, error) {
	return s.deleteAwardEmojiOnNote(pid, awardSnippets, snippetIID, noteID, awardID, options...)
}

func (s *AwardEmojiService) deleteAwardEmojiOnNote(pid interface{}, resource string, resourceID, noteID, awardID int, options ...RequestOptionFunc) (*Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("projects/%s/%s/%d/notes/%d/award_emoji/%d",
		PathEscape(project),
		resource,
		resourceID,
		noteID,
		awardID,
	)

	req, err := s.client.NewRequest(http.MethodDelete, u, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/award_emojis_test.go000066400000000000000000001226201475761473200251140ustar00rootroot00000000000000package gitlab

import (
	"fmt"
	"net/http"
	"testing"

	"github.com/stretchr/testify/require"
)

func TestAwardEmojiService_ListMergeRequestAwardEmoji(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/merge_requests/80/award_emoji", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
			[
			  {
				"id": 4,
				"name": "1234",
				"user": {
				  "name": "Venkatesh Thalluri",
				  "username": "venky333",
				  "id": 1,
				  "state": "active",
				  "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
				  "web_url": "http://gitlab.example.com/venky333"
				},
				"awardable_id": 80,
				"awardable_type": "Merge request"
			  }
			]
		`)
	})

	want := []*AwardEmoji{{
		ID:   4,
		Name: "1234",
		User: struct {
			Name      string `json:"name"`
			Username  string `json:"username"`
			ID        int    `json:"id"`
			State     string `json:"state"`
			AvatarURL string `json:"avatar_url"`
			WebURL    string `json:"web_url"`
		}{
			Name:      "Venkatesh Thalluri",
			Username:  "venky333",
			ID:        1,
			State:     "active",
			AvatarURL: "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
			WebURL:    "http://gitlab.example.com/venky333",
		},
		CreatedAt:     nil,
		UpdatedAt:     nil,
		AwardableID:   80,
		AwardableType: "Merge request",
	}}

	aes, resp, err := client.AwardEmoji.ListMergeRequestAwardEmoji(1, 80, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, aes)

	aes, resp, err = client.AwardEmoji.ListMergeRequestAwardEmoji(1.01, 80, nil)
	require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, aes)

	aes, resp, err = client.AwardEmoji.ListMergeRequestAwardEmoji(1, 80, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, aes)

	aes, resp, err = client.AwardEmoji.ListMergeRequestAwardEmoji(3, 80, nil)
	require.Error(t, err)
	require.Nil(t, aes)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestAwardEmojiService_ListIssueAwardEmoji(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/issues/80/award_emoji", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
			[
			  {
				"id": 4,
				"name": "1234",
				"user": {
				  "name": "Venkatesh Thalluri",
				  "username": "venky333",
				  "id": 1,
				  "state": "active",
				  "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
				  "web_url": "http://gitlab.example.com/venky333"
				},
				"awardable_id": 80,
				"awardable_type": "Issue"
			  }
			]
		`)
	})

	want := []*AwardEmoji{{
		ID:   4,
		Name: "1234",
		User: struct {
			Name      string `json:"name"`
			Username  string `json:"username"`
			ID        int    `json:"id"`
			State     string `json:"state"`
			AvatarURL string `json:"avatar_url"`
			WebURL    string `json:"web_url"`
		}{
			Name:      "Venkatesh Thalluri",
			Username:  "venky333",
			ID:        1,
			State:     "active",
			AvatarURL: "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
			WebURL:    "http://gitlab.example.com/venky333",
		},
		CreatedAt:     nil,
		UpdatedAt:     nil,
		AwardableID:   80,
		AwardableType: "Issue",
	}}

	aes, resp, err := client.AwardEmoji.ListIssueAwardEmoji(1, 80, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, aes)

	aes, resp, err = client.AwardEmoji.ListIssueAwardEmoji(1.01, 80, nil)
	require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, aes)

	aes, resp, err = client.AwardEmoji.ListIssueAwardEmoji(1, 80, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, aes)

	aes, resp, err = client.AwardEmoji.ListIssueAwardEmoji(3, 80, nil)
	require.Error(t, err)
	require.Nil(t, aes)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestAwardEmojiService_ListSnippetAwardEmoji(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/snippets/80/award_emoji", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
			[
			  {
				"id": 4,
				"name": "1234",
				"user": {
				  "name": "Venkatesh Thalluri",
				  "username": "venky333",
				  "id": 1,
				  "state": "active",
				  "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
				  "web_url": "http://gitlab.example.com/venky333"
				},
				"awardable_id": 80,
				"awardable_type": "Snippet"
			  }
			]
		`)
	})

	want := []*AwardEmoji{{
		ID:   4,
		Name: "1234",
		User: struct {
			Name      string `json:"name"`
			Username  string `json:"username"`
			ID        int    `json:"id"`
			State     string `json:"state"`
			AvatarURL string `json:"avatar_url"`
			WebURL    string `json:"web_url"`
		}{
			Name:      "Venkatesh Thalluri",
			Username:  "venky333",
			ID:        1,
			State:     "active",
			AvatarURL: "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
			WebURL:    "http://gitlab.example.com/venky333",
		},
		CreatedAt:     nil,
		UpdatedAt:     nil,
		AwardableID:   80,
		AwardableType: "Snippet",
	}}

	aes, resp, err := client.AwardEmoji.ListSnippetAwardEmoji(1, 80, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, aes)

	aes, resp, err = client.AwardEmoji.ListSnippetAwardEmoji(1.01, 80, nil)
	require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, aes)

	aes, resp, err = client.AwardEmoji.ListSnippetAwardEmoji(1, 80, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, aes)

	aes, resp, err = client.AwardEmoji.ListSnippetAwardEmoji(3, 80, nil)
	require.Error(t, err)
	require.Nil(t, aes)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestAwardEmojiService_GetMergeRequestAwardEmoji(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/merge_requests/80/award_emoji/4", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
		  {
			"id": 4,
			"name": "1234",
			"user": {
			  "name": "Venkatesh Thalluri",
			  "username": "venky333",
			  "id": 1,
			  "state": "active",
			  "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
			  "web_url": "http://gitlab.example.com/venky333"
			},
			"awardable_id": 80,
			"awardable_type": "Merge request"
		  }
		`)
	})

	want := &AwardEmoji{
		ID:   4,
		Name: "1234",
		User: struct {
			Name      string `json:"name"`
			Username  string `json:"username"`
			ID        int    `json:"id"`
			State     string `json:"state"`
			AvatarURL string `json:"avatar_url"`
			WebURL    string `json:"web_url"`
		}{
			Name:      "Venkatesh Thalluri",
			Username:  "venky333",
			ID:        1,
			State:     "active",
			AvatarURL: "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
			WebURL:    "http://gitlab.example.com/venky333",
		},
		CreatedAt:     nil,
		UpdatedAt:     nil,
		AwardableID:   80,
		AwardableType: "Merge request",
	}

	ae, resp, err := client.AwardEmoji.GetMergeRequestAwardEmoji(1, 80, 4, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, ae)

	ae, resp, err = client.AwardEmoji.GetMergeRequestAwardEmoji(1.01, 80, 4, nil)
	require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, ae)

	ae, resp, err = client.AwardEmoji.GetMergeRequestAwardEmoji(1, 80, 4, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, ae)

	ae, resp, err = client.AwardEmoji.GetMergeRequestAwardEmoji(3, 80, 4, nil)
	require.Error(t, err)
	require.Nil(t, ae)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestAwardEmojiService_GetIssueAwardEmoji(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/issues/80/award_emoji/4", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
		  {
			"id": 4,
			"name": "1234",
			"user": {
			  "name": "Venkatesh Thalluri",
			  "username": "venky333",
			  "id": 1,
			  "state": "active",
			  "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
			  "web_url": "http://gitlab.example.com/venky333"
			},
			"awardable_id": 80,
			"awardable_type": "Issue"
		  }
		`)
	})

	want := &AwardEmoji{
		ID:   4,
		Name: "1234",
		User: struct {
			Name      string `json:"name"`
			Username  string `json:"username"`
			ID        int    `json:"id"`
			State     string `json:"state"`
			AvatarURL string `json:"avatar_url"`
			WebURL    string `json:"web_url"`
		}{
			Name:      "Venkatesh Thalluri",
			Username:  "venky333",
			ID:        1,
			State:     "active",
			AvatarURL: "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
			WebURL:    "http://gitlab.example.com/venky333",
		},
		CreatedAt:     nil,
		UpdatedAt:     nil,
		AwardableID:   80,
		AwardableType: "Issue",
	}

	ae, resp, err := client.AwardEmoji.GetIssueAwardEmoji(1, 80, 4, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, ae)

	ae, resp, err = client.AwardEmoji.GetIssueAwardEmoji(1.01, 80, 4, nil)
	require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, ae)

	ae, resp, err = client.AwardEmoji.GetIssueAwardEmoji(1, 80, 4, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, ae)

	ae, resp, err = client.AwardEmoji.GetIssueAwardEmoji(3, 80, 4, nil)
	require.Error(t, err)
	require.Nil(t, ae)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestAwardEmojiService_GetSnippetAwardEmoji(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/snippets/80/award_emoji/4", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
		  {
			"id": 4,
			"name": "1234",
			"user": {
			  "name": "Venkatesh Thalluri",
			  "username": "venky333",
			  "id": 1,
			  "state": "active",
			  "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
			  "web_url": "http://gitlab.example.com/venky333"
			},
			"awardable_id": 80,
			"awardable_type": "Snippet"
		  }
		`)
	})

	want := &AwardEmoji{
		ID:   4,
		Name: "1234",
		User: struct {
			Name      string `json:"name"`
			Username  string `json:"username"`
			ID        int    `json:"id"`
			State     string `json:"state"`
			AvatarURL string `json:"avatar_url"`
			WebURL    string `json:"web_url"`
		}{
			Name:      "Venkatesh Thalluri",
			Username:  "venky333",
			ID:        1,
			State:     "active",
			AvatarURL: "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
			WebURL:    "http://gitlab.example.com/venky333",
		},
		CreatedAt:     nil,
		UpdatedAt:     nil,
		AwardableID:   80,
		AwardableType: "Snippet",
	}

	ae, resp, err := client.AwardEmoji.GetSnippetAwardEmoji(1, 80, 4, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, ae)

	ae, resp, err = client.AwardEmoji.GetSnippetAwardEmoji(1.01, 80, 4, nil)
	require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, ae)

	ae, resp, err = client.AwardEmoji.GetSnippetAwardEmoji(1, 80, 4, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, ae)

	ae, resp, err = client.AwardEmoji.GetSnippetAwardEmoji(3, 80, 4, nil)
	require.Error(t, err)
	require.Nil(t, ae)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestAwardEmojiService_CreateMergeRequestAwardEmoji(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/merge_requests/80/award_emoji", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		fmt.Fprintf(w, `
		  {
			"id": 4,
			"name": "1234",
			"user": {
			  "name": "Venkatesh Thalluri",
			  "username": "venky333",
			  "id": 1,
			  "state": "active",
			  "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
			  "web_url": "http://gitlab.example.com/venky333"
			},
			"awardable_id": 80,
			"awardable_type": "Merge request"
		  }
		`)
	})

	want := &AwardEmoji{
		ID:   4,
		Name: "1234",
		User: struct {
			Name      string `json:"name"`
			Username  string `json:"username"`
			ID        int    `json:"id"`
			State     string `json:"state"`
			AvatarURL string `json:"avatar_url"`
			WebURL    string `json:"web_url"`
		}{
			Name:      "Venkatesh Thalluri",
			Username:  "venky333",
			ID:        1,
			State:     "active",
			AvatarURL: "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
			WebURL:    "http://gitlab.example.com/venky333",
		},
		CreatedAt:     nil,
		UpdatedAt:     nil,
		AwardableID:   80,
		AwardableType: "Merge request",
	}

	ae, resp, err := client.AwardEmoji.CreateMergeRequestAwardEmoji(1, 80, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, ae)

	ae, resp, err = client.AwardEmoji.CreateMergeRequestAwardEmoji(1.01, 80, nil)
	require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, ae)

	ae, resp, err = client.AwardEmoji.CreateMergeRequestAwardEmoji(1, 80, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, ae)

	ae, resp, err = client.AwardEmoji.CreateMergeRequestAwardEmoji(3, 80, nil)
	require.Error(t, err)
	require.Nil(t, ae)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestAwardEmojiService_CreateIssueAwardEmoji(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/issues/80/award_emoji", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		fmt.Fprintf(w, `
		  {
			"id": 4,
			"name": "1234",
			"user": {
			  "name": "Venkatesh Thalluri",
			  "username": "venky333",
			  "id": 1,
			  "state": "active",
			  "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
			  "web_url": "http://gitlab.example.com/venky333"
			},
			"awardable_id": 80,
			"awardable_type": "Issue"
		  }
		`)
	})

	want := &AwardEmoji{
		ID:   4,
		Name: "1234",
		User: struct {
			Name      string `json:"name"`
			Username  string `json:"username"`
			ID        int    `json:"id"`
			State     string `json:"state"`
			AvatarURL string `json:"avatar_url"`
			WebURL    string `json:"web_url"`
		}{
			Name:      "Venkatesh Thalluri",
			Username:  "venky333",
			ID:        1,
			State:     "active",
			AvatarURL: "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
			WebURL:    "http://gitlab.example.com/venky333",
		},
		CreatedAt:     nil,
		UpdatedAt:     nil,
		AwardableID:   80,
		AwardableType: "Issue",
	}

	ae, resp, err := client.AwardEmoji.CreateIssueAwardEmoji(1, 80, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, ae)

	ae, resp, err = client.AwardEmoji.CreateIssueAwardEmoji(1.01, 80, nil)
	require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, ae)

	ae, resp, err = client.AwardEmoji.CreateIssueAwardEmoji(1, 80, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, ae)

	ae, resp, err = client.AwardEmoji.CreateIssueAwardEmoji(3, 80, nil)
	require.Error(t, err)
	require.Nil(t, ae)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestAwardEmojiService_CreateSnippetAwardEmoji(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/snippets/80/award_emoji", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		fmt.Fprintf(w, `
		  {
			"id": 4,
			"name": "1234",
			"user": {
			  "name": "Venkatesh Thalluri",
			  "username": "venky333",
			  "id": 1,
			  "state": "active",
			  "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
			  "web_url": "http://gitlab.example.com/venky333"
			},
			"awardable_id": 80,
			"awardable_type": "Snippet"
		  }
		`)
	})

	want := &AwardEmoji{
		ID:   4,
		Name: "1234",
		User: struct {
			Name      string `json:"name"`
			Username  string `json:"username"`
			ID        int    `json:"id"`
			State     string `json:"state"`
			AvatarURL string `json:"avatar_url"`
			WebURL    string `json:"web_url"`
		}{
			Name:      "Venkatesh Thalluri",
			Username:  "venky333",
			ID:        1,
			State:     "active",
			AvatarURL: "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
			WebURL:    "http://gitlab.example.com/venky333",
		},
		CreatedAt:     nil,
		UpdatedAt:     nil,
		AwardableID:   80,
		AwardableType: "Snippet",
	}

	ae, resp, err := client.AwardEmoji.CreateSnippetAwardEmoji(1, 80, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, ae)

	ae, resp, err = client.AwardEmoji.CreateSnippetAwardEmoji(1.01, 80, nil)
	require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, ae)

	ae, resp, err = client.AwardEmoji.CreateSnippetAwardEmoji(1, 80, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, ae)

	ae, resp, err = client.AwardEmoji.CreateSnippetAwardEmoji(3, 80, nil)
	require.Error(t, err)
	require.Nil(t, ae)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestAwardEmojiService_DeleteMergeRequestAwardEmoji(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/merge_requests/80/award_emoji/1", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodDelete)
	})

	resp, err := client.AwardEmoji.DeleteMergeRequestAwardEmoji(1, 80, 1, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)

	resp, err = client.AwardEmoji.DeleteMergeRequestAwardEmoji(1.01, 80, 1, nil)
	require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string")
	require.Nil(t, resp)

	resp, err = client.AwardEmoji.DeleteMergeRequestAwardEmoji(1, 80, 1, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)

	resp, err = client.AwardEmoji.DeleteMergeRequestAwardEmoji(3, 80, 1, nil)
	require.Error(t, err)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestAwardEmojiService_DeleteIssueAwardEmoji(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/issues/80/award_emoji/1", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodDelete)
	})

	resp, err := client.AwardEmoji.DeleteIssueAwardEmoji(1, 80, 1, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)

	resp, err = client.AwardEmoji.DeleteIssueAwardEmoji(1.01, 80, 1, nil)
	require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string")
	require.Nil(t, resp)

	resp, err = client.AwardEmoji.DeleteIssueAwardEmoji(1, 80, 1, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)

	resp, err = client.AwardEmoji.DeleteIssueAwardEmoji(3, 80, 1, nil)
	require.Error(t, err)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestAwardEmojiService_DeleteSnippetAwardEmoji(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/snippets/80/award_emoji/1", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodDelete)
	})

	resp, err := client.AwardEmoji.DeleteSnippetAwardEmoji(1, 80, 1, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)

	resp, err = client.AwardEmoji.DeleteSnippetAwardEmoji(1.01, 80, 1, nil)
	require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string")
	require.Nil(t, resp)

	resp, err = client.AwardEmoji.DeleteSnippetAwardEmoji(1, 80, 1, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)

	resp, err = client.AwardEmoji.DeleteSnippetAwardEmoji(3, 80, 1, nil)
	require.Error(t, err)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestAwardEmojiService_ListMergeRequestAwardEmojiOnNote(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/merge_requests/80/notes/1/award_emoji", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
			[
			  {
				"id": 4,
				"name": "1234",
				"user": {
				  "name": "Venkatesh Thalluri",
				  "username": "venky333",
				  "id": 1,
				  "state": "active",
				  "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
				  "web_url": "http://gitlab.example.com/venky333"
				},
				"awardable_id": 80,
				"awardable_type": "Merge request"
			  }
			]
		`)
	})

	want := []*AwardEmoji{{
		ID:   4,
		Name: "1234",
		User: struct {
			Name      string `json:"name"`
			Username  string `json:"username"`
			ID        int    `json:"id"`
			State     string `json:"state"`
			AvatarURL string `json:"avatar_url"`
			WebURL    string `json:"web_url"`
		}{
			Name:      "Venkatesh Thalluri",
			Username:  "venky333",
			ID:        1,
			State:     "active",
			AvatarURL: "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
			WebURL:    "http://gitlab.example.com/venky333",
		},
		CreatedAt:     nil,
		UpdatedAt:     nil,
		AwardableID:   80,
		AwardableType: "Merge request",
	}}

	aes, resp, err := client.AwardEmoji.ListMergeRequestAwardEmojiOnNote(1, 80, 1, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, aes)

	aes, resp, err = client.AwardEmoji.ListMergeRequestAwardEmojiOnNote(1.01, 80, 1, nil)
	require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, aes)

	aes, resp, err = client.AwardEmoji.ListMergeRequestAwardEmojiOnNote(1, 80, 1, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, aes)

	aes, resp, err = client.AwardEmoji.ListMergeRequestAwardEmojiOnNote(3, 80, 1, nil)
	require.Error(t, err)
	require.Nil(t, aes)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestAwardEmojiService_ListIssuesAwardEmojiOnNote(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/issues/80/notes/1/award_emoji", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
			[
			  {
				"id": 4,
				"name": "1234",
				"user": {
				  "name": "Venkatesh Thalluri",
				  "username": "venky333",
				  "id": 1,
				  "state": "active",
				  "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
				  "web_url": "http://gitlab.example.com/venky333"
				},
				"awardable_id": 80,
				"awardable_type": "Issue"
			  }
			]
		`)
	})

	want := []*AwardEmoji{{
		ID:   4,
		Name: "1234",
		User: struct {
			Name      string `json:"name"`
			Username  string `json:"username"`
			ID        int    `json:"id"`
			State     string `json:"state"`
			AvatarURL string `json:"avatar_url"`
			WebURL    string `json:"web_url"`
		}{
			Name:      "Venkatesh Thalluri",
			Username:  "venky333",
			ID:        1,
			State:     "active",
			AvatarURL: "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
			WebURL:    "http://gitlab.example.com/venky333",
		},
		CreatedAt:     nil,
		UpdatedAt:     nil,
		AwardableID:   80,
		AwardableType: "Issue",
	}}

	aes, resp, err := client.AwardEmoji.ListIssuesAwardEmojiOnNote(1, 80, 1, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, aes)

	aes, resp, err = client.AwardEmoji.ListIssuesAwardEmojiOnNote(1.01, 80, 1, nil)
	require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, aes)

	aes, resp, err = client.AwardEmoji.ListIssuesAwardEmojiOnNote(1, 80, 1, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, aes)

	aes, resp, err = client.AwardEmoji.ListIssuesAwardEmojiOnNote(3, 80, 1, nil)
	require.Error(t, err)
	require.Nil(t, aes)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestAwardEmojiService_ListSnippetAwardEmojiOnNote(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/snippets/80/notes/1/award_emoji", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
			[
			  {
				"id": 4,
				"name": "1234",
				"user": {
				  "name": "Venkatesh Thalluri",
				  "username": "venky333",
				  "id": 1,
				  "state": "active",
				  "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
				  "web_url": "http://gitlab.example.com/venky333"
				},
				"awardable_id": 80,
				"awardable_type": "Snippet"
			  }
			]
		`)
	})

	want := []*AwardEmoji{{
		ID:   4,
		Name: "1234",
		User: struct {
			Name      string `json:"name"`
			Username  string `json:"username"`
			ID        int    `json:"id"`
			State     string `json:"state"`
			AvatarURL string `json:"avatar_url"`
			WebURL    string `json:"web_url"`
		}{
			Name:      "Venkatesh Thalluri",
			Username:  "venky333",
			ID:        1,
			State:     "active",
			AvatarURL: "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
			WebURL:    "http://gitlab.example.com/venky333",
		},
		CreatedAt:     nil,
		UpdatedAt:     nil,
		AwardableID:   80,
		AwardableType: "Snippet",
	}}

	aes, resp, err := client.AwardEmoji.ListSnippetAwardEmojiOnNote(1, 80, 1, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, aes)

	aes, resp, err = client.AwardEmoji.ListSnippetAwardEmojiOnNote(1.01, 80, 1, nil)
	require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, aes)

	aes, resp, err = client.AwardEmoji.ListSnippetAwardEmojiOnNote(1, 80, 1, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, aes)

	aes, resp, err = client.AwardEmoji.ListSnippetAwardEmojiOnNote(3, 80, 1, nil)
	require.Error(t, err)
	require.Nil(t, aes)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestAwardEmojiService_GetMergeRequestAwardEmojiOnNote(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/merge_requests/80/notes/1/award_emoji/4", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
		  {
			"id": 4,
			"name": "1234",
			"user": {
			  "name": "Venkatesh Thalluri",
			  "username": "venky333",
			  "id": 1,
			  "state": "active",
			  "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
			  "web_url": "http://gitlab.example.com/venky333"
			},
			"awardable_id": 80,
			"awardable_type": "Merge request"
		  }
		`)
	})

	want := &AwardEmoji{
		ID:   4,
		Name: "1234",
		User: struct {
			Name      string `json:"name"`
			Username  string `json:"username"`
			ID        int    `json:"id"`
			State     string `json:"state"`
			AvatarURL string `json:"avatar_url"`
			WebURL    string `json:"web_url"`
		}{
			Name:      "Venkatesh Thalluri",
			Username:  "venky333",
			ID:        1,
			State:     "active",
			AvatarURL: "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
			WebURL:    "http://gitlab.example.com/venky333",
		},
		CreatedAt:     nil,
		UpdatedAt:     nil,
		AwardableID:   80,
		AwardableType: "Merge request",
	}

	ae, resp, err := client.AwardEmoji.GetMergeRequestAwardEmojiOnNote(1, 80, 1, 4, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, ae)

	ae, resp, err = client.AwardEmoji.GetMergeRequestAwardEmojiOnNote(1.01, 80, 1, 4, nil)
	require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, ae)

	ae, resp, err = client.AwardEmoji.GetMergeRequestAwardEmojiOnNote(1, 80, 1, 4, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, ae)

	ae, resp, err = client.AwardEmoji.GetMergeRequestAwardEmojiOnNote(3, 80, 1, 4, nil)
	require.Error(t, err)
	require.Nil(t, ae)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestAwardEmojiService_GetIssuesAwardEmojiOnNote(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/issues/80/notes/1/award_emoji/4", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
		  {
			"id": 4,
			"name": "1234",
			"user": {
			  "name": "Venkatesh Thalluri",
			  "username": "venky333",
			  "id": 1,
			  "state": "active",
			  "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
			  "web_url": "http://gitlab.example.com/venky333"
			},
			"awardable_id": 80,
			"awardable_type": "Issue"
		  }
		`)
	})

	want := &AwardEmoji{
		ID:   4,
		Name: "1234",
		User: struct {
			Name      string `json:"name"`
			Username  string `json:"username"`
			ID        int    `json:"id"`
			State     string `json:"state"`
			AvatarURL string `json:"avatar_url"`
			WebURL    string `json:"web_url"`
		}{
			Name:      "Venkatesh Thalluri",
			Username:  "venky333",
			ID:        1,
			State:     "active",
			AvatarURL: "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
			WebURL:    "http://gitlab.example.com/venky333",
		},
		CreatedAt:     nil,
		UpdatedAt:     nil,
		AwardableID:   80,
		AwardableType: "Issue",
	}

	ae, resp, err := client.AwardEmoji.GetIssuesAwardEmojiOnNote(1, 80, 1, 4, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, ae)

	ae, resp, err = client.AwardEmoji.GetIssuesAwardEmojiOnNote(1.01, 80, 1, 4, nil)
	require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, ae)

	ae, resp, err = client.AwardEmoji.GetIssuesAwardEmojiOnNote(1, 80, 1, 4, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, ae)

	ae, resp, err = client.AwardEmoji.GetIssuesAwardEmojiOnNote(3, 80, 1, 4, nil)
	require.Error(t, err)
	require.Nil(t, ae)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestAwardEmojiService_GetSnippetAwardEmojiOnNote(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/snippets/80/notes/1/award_emoji/4", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
		  {
			"id": 4,
			"name": "1234",
			"user": {
			  "name": "Venkatesh Thalluri",
			  "username": "venky333",
			  "id": 1,
			  "state": "active",
			  "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
			  "web_url": "http://gitlab.example.com/venky333"
			},
			"awardable_id": 80,
			"awardable_type": "Snippet"
		  }
		`)
	})

	want := &AwardEmoji{
		ID:   4,
		Name: "1234",
		User: struct {
			Name      string `json:"name"`
			Username  string `json:"username"`
			ID        int    `json:"id"`
			State     string `json:"state"`
			AvatarURL string `json:"avatar_url"`
			WebURL    string `json:"web_url"`
		}{
			Name:      "Venkatesh Thalluri",
			Username:  "venky333",
			ID:        1,
			State:     "active",
			AvatarURL: "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
			WebURL:    "http://gitlab.example.com/venky333",
		},
		CreatedAt:     nil,
		UpdatedAt:     nil,
		AwardableID:   80,
		AwardableType: "Snippet",
	}

	ae, resp, err := client.AwardEmoji.GetSnippetAwardEmojiOnNote(1, 80, 1, 4, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, ae)

	ae, resp, err = client.AwardEmoji.GetSnippetAwardEmojiOnNote(1.01, 80, 1, 4, nil)
	require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, ae)

	ae, resp, err = client.AwardEmoji.GetSnippetAwardEmojiOnNote(1, 80, 1, 4, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, ae)

	ae, resp, err = client.AwardEmoji.GetSnippetAwardEmojiOnNote(3, 80, 1, 4, nil)
	require.Error(t, err)
	require.Nil(t, ae)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestAwardEmojiService_CCreateMergeRequestAwardEmojiOnNote(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/merge_requests/80/notes/1/award_emoji", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		fmt.Fprintf(w, `
		  {
			"id": 4,
			"name": "1234",
			"user": {
			  "name": "Venkatesh Thalluri",
			  "username": "venky333",
			  "id": 1,
			  "state": "active",
			  "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
			  "web_url": "http://gitlab.example.com/venky333"
			},
			"awardable_id": 80,
			"awardable_type": "Merge request"
		  }
		`)
	})

	want := &AwardEmoji{
		ID:   4,
		Name: "1234",
		User: struct {
			Name      string `json:"name"`
			Username  string `json:"username"`
			ID        int    `json:"id"`
			State     string `json:"state"`
			AvatarURL string `json:"avatar_url"`
			WebURL    string `json:"web_url"`
		}{
			Name:      "Venkatesh Thalluri",
			Username:  "venky333",
			ID:        1,
			State:     "active",
			AvatarURL: "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
			WebURL:    "http://gitlab.example.com/venky333",
		},
		CreatedAt:     nil,
		UpdatedAt:     nil,
		AwardableID:   80,
		AwardableType: "Merge request",
	}

	ae, resp, err := client.AwardEmoji.CreateMergeRequestAwardEmojiOnNote(1, 80, 1, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, ae)

	ae, resp, err = client.AwardEmoji.CreateMergeRequestAwardEmojiOnNote(1.01, 80, 1, nil)
	require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, ae)

	ae, resp, err = client.AwardEmoji.CreateMergeRequestAwardEmojiOnNote(1, 80, 1, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, ae)

	ae, resp, err = client.AwardEmoji.CreateMergeRequestAwardEmojiOnNote(3, 80, 1, nil)
	require.Error(t, err)
	require.Nil(t, ae)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestAwardEmojiService_CreateIssuesAwardEmojiOnNote(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/issues/80/notes/1/award_emoji", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		fmt.Fprintf(w, `
		  {
			"id": 4,
			"name": "1234",
			"user": {
			  "name": "Venkatesh Thalluri",
			  "username": "venky333",
			  "id": 1,
			  "state": "active",
			  "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
			  "web_url": "http://gitlab.example.com/venky333"
			},
			"awardable_id": 80,
			"awardable_type": "Issue"
		  }
		`)
	})

	want := &AwardEmoji{
		ID:   4,
		Name: "1234",
		User: struct {
			Name      string `json:"name"`
			Username  string `json:"username"`
			ID        int    `json:"id"`
			State     string `json:"state"`
			AvatarURL string `json:"avatar_url"`
			WebURL    string `json:"web_url"`
		}{
			Name:      "Venkatesh Thalluri",
			Username:  "venky333",
			ID:        1,
			State:     "active",
			AvatarURL: "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
			WebURL:    "http://gitlab.example.com/venky333",
		},
		CreatedAt:     nil,
		UpdatedAt:     nil,
		AwardableID:   80,
		AwardableType: "Issue",
	}

	ae, resp, err := client.AwardEmoji.CreateIssuesAwardEmojiOnNote(1, 80, 1, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, ae)

	ae, resp, err = client.AwardEmoji.CreateIssuesAwardEmojiOnNote(1.01, 80, 1, nil)
	require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, ae)

	ae, resp, err = client.AwardEmoji.CreateIssuesAwardEmojiOnNote(1, 80, 1, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, ae)

	ae, resp, err = client.AwardEmoji.CreateIssuesAwardEmojiOnNote(3, 80, 1, nil)
	require.Error(t, err)
	require.Nil(t, ae)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestAwardEmojiService_CreateSnippetAwardEmojiOnNote(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/snippets/80/notes/1/award_emoji", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		fmt.Fprintf(w, `
		  {
			"id": 4,
			"name": "1234",
			"user": {
			  "name": "Venkatesh Thalluri",
			  "username": "venky333",
			  "id": 1,
			  "state": "active",
			  "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
			  "web_url": "http://gitlab.example.com/venky333"
			},
			"awardable_id": 80,
			"awardable_type": "Snippet"
		  }
		`)
	})

	want := &AwardEmoji{
		ID:   4,
		Name: "1234",
		User: struct {
			Name      string `json:"name"`
			Username  string `json:"username"`
			ID        int    `json:"id"`
			State     string `json:"state"`
			AvatarURL string `json:"avatar_url"`
			WebURL    string `json:"web_url"`
		}{
			Name:      "Venkatesh Thalluri",
			Username:  "venky333",
			ID:        1,
			State:     "active",
			AvatarURL: "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
			WebURL:    "http://gitlab.example.com/venky333",
		},
		CreatedAt:     nil,
		UpdatedAt:     nil,
		AwardableID:   80,
		AwardableType: "Snippet",
	}

	ae, resp, err := client.AwardEmoji.CreateSnippetAwardEmojiOnNote(1, 80, 1, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, ae)

	ae, resp, err = client.AwardEmoji.CreateSnippetAwardEmojiOnNote(1.01, 80, 1, nil)
	require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, ae)

	ae, resp, err = client.AwardEmoji.CreateSnippetAwardEmojiOnNote(1, 80, 1, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, ae)

	ae, resp, err = client.AwardEmoji.CreateSnippetAwardEmojiOnNote(3, 80, 1, nil)
	require.Error(t, err)
	require.Nil(t, ae)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestAwardEmojiService_DeleteMergeRequestAwardEmojiOnNote(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/merge_requests/80/notes/1/award_emoji/1", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodDelete)
	})

	resp, err := client.AwardEmoji.DeleteMergeRequestAwardEmojiOnNote(1, 80, 1, 1, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)

	resp, err = client.AwardEmoji.DeleteMergeRequestAwardEmojiOnNote(1.01, 80, 1, 1, nil)
	require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string")
	require.Nil(t, resp)

	resp, err = client.AwardEmoji.DeleteMergeRequestAwardEmojiOnNote(1, 80, 1, 1, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)

	resp, err = client.AwardEmoji.DeleteMergeRequestAwardEmojiOnNote(3, 80, 1, 1, nil)
	require.Error(t, err)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestAwardEmojiService_DeleteIssuesAwardEmojiOnNote(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/issues/80/notes/1/award_emoji/1", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodDelete)
	})

	resp, err := client.AwardEmoji.DeleteIssuesAwardEmojiOnNote(1, 80, 1, 1, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)

	resp, err = client.AwardEmoji.DeleteIssuesAwardEmojiOnNote(1.01, 80, 1, 1, nil)
	require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string")
	require.Nil(t, resp)

	resp, err = client.AwardEmoji.DeleteIssuesAwardEmojiOnNote(1, 80, 1, 1, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)

	resp, err = client.AwardEmoji.DeleteIssuesAwardEmojiOnNote(3, 80, 1, 1, nil)
	require.Error(t, err)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestAwardEmojiService_DeleteSnippetAwardEmojiOnNote(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/snippets/80/notes/1/award_emoji/1", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodDelete)
	})

	resp, err := client.AwardEmoji.DeleteSnippetAwardEmojiOnNote(1, 80, 1, 1, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)

	resp, err = client.AwardEmoji.DeleteSnippetAwardEmojiOnNote(1.01, 80, 1, 1, nil)
	require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string")
	require.Nil(t, resp)

	resp, err = client.AwardEmoji.DeleteSnippetAwardEmojiOnNote(1, 80, 1, 1, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)

	resp, err = client.AwardEmoji.DeleteSnippetAwardEmojiOnNote(3, 80, 1, 1, nil)
	require.Error(t, err)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/boards.go000066400000000000000000000257411475761473200226710ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"fmt"
	"net/http"
)

// IssueBoardsService handles communication with the issue board related
// methods of the GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/boards.html
type IssueBoardsService struct {
	client *Client
}

// IssueBoard represents a GitLab issue board.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/boards.html
type IssueBoard struct {
	ID        int        `json:"id"`
	Name      string     `json:"name"`
	Project   *Project   `json:"project"`
	Milestone *Milestone `json:"milestone"`
	Assignee  *struct {
		ID        int    `json:"id"`
		Username  string `json:"username"`
		Name      string `json:"name"`
		State     string `json:"state"`
		AvatarURL string `json:"avatar_url"`
		WebURL    string `json:"web_url"`
	} `json:"assignee"`
	Lists  []*BoardList    `json:"lists"`
	Weight int             `json:"weight"`
	Labels []*LabelDetails `json:"labels"`
}

func (b IssueBoard) String() string {
	return Stringify(b)
}

// BoardList represents a GitLab board list.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/boards.html
type BoardList struct {
	ID       int `json:"id"`
	Assignee *struct {
		ID       int    `json:"id"`
		Name     string `json:"name"`
		Username string `json:"username"`
	} `json:"assignee"`
	Iteration      *ProjectIteration `json:"iteration"`
	Label          *Label            `json:"label"`
	MaxIssueCount  int               `json:"max_issue_count"`
	MaxIssueWeight int               `json:"max_issue_weight"`
	Milestone      *Milestone        `json:"milestone"`
	Position       int               `json:"position"`
}

func (b BoardList) String() string {
	return Stringify(b)
}

// CreateIssueBoardOptions represents the available CreateIssueBoard() options.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/boards.html#create-an-issue-board
type CreateIssueBoardOptions struct {
	Name *string `url:"name,omitempty" json:"name,omitempty"`
}

// CreateIssueBoard creates a new issue board.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/boards.html#create-an-issue-board
func (s *IssueBoardsService) CreateIssueBoard(pid interface{}, opt *CreateIssueBoardOptions, options ...RequestOptionFunc) (*IssueBoard, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/boards", PathEscape(project))

	req, err := s.client.NewRequest(http.MethodPost, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	board := new(IssueBoard)
	resp, err := s.client.Do(req, board)
	if err != nil {
		return nil, resp, err
	}

	return board, resp, nil
}

// UpdateIssueBoardOptions represents the available UpdateIssueBoard() options.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/boards.html#update-an-issue-board
type UpdateIssueBoardOptions struct {
	Name        *string       `url:"name,omitempty" json:"name,omitempty"`
	AssigneeID  *int          `url:"assignee_id,omitempty" json:"assignee_id,omitempty"`
	MilestoneID *int          `url:"milestone_id,omitempty" json:"milestone_id,omitempty"`
	Labels      *LabelOptions `url:"labels,omitempty" json:"labels,omitempty"`
	Weight      *int          `url:"weight,omitempty" json:"weight,omitempty"`
}

// UpdateIssueBoard update an issue board.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/boards.html#update-an-issue-board
func (s *IssueBoardsService) UpdateIssueBoard(pid interface{}, board int, opt *UpdateIssueBoardOptions, options ...RequestOptionFunc) (*IssueBoard, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/boards/%d", PathEscape(project), board)

	req, err := s.client.NewRequest(http.MethodPut, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	is := new(IssueBoard)
	resp, err := s.client.Do(req, is)
	if err != nil {
		return nil, resp, err
	}

	return is, resp, nil
}

// DeleteIssueBoard deletes an issue board.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/boards.html#delete-an-issue-board
func (s *IssueBoardsService) DeleteIssueBoard(pid interface{}, board int, options ...RequestOptionFunc) (*Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("projects/%s/boards/%d", PathEscape(project), board)

	req, err := s.client.NewRequest(http.MethodDelete, u, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}

// ListIssueBoardsOptions represents the available ListIssueBoards() options.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/boards.html#list-project-issue-boards
type ListIssueBoardsOptions ListOptions

// ListIssueBoards gets a list of all issue boards in a project.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/boards.html#list-project-issue-boards
func (s *IssueBoardsService) ListIssueBoards(pid interface{}, opt *ListIssueBoardsOptions, options ...RequestOptionFunc) ([]*IssueBoard, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/boards", PathEscape(project))

	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var is []*IssueBoard
	resp, err := s.client.Do(req, &is)
	if err != nil {
		return nil, resp, err
	}

	return is, resp, nil
}

// GetIssueBoard gets a single issue board of a project.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/boards.html#show-a-single-issue-board
func (s *IssueBoardsService) GetIssueBoard(pid interface{}, board int, options ...RequestOptionFunc) (*IssueBoard, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/boards/%d", PathEscape(project), board)

	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	ib := new(IssueBoard)
	resp, err := s.client.Do(req, ib)
	if err != nil {
		return nil, resp, err
	}

	return ib, resp, nil
}

// GetIssueBoardListsOptions represents the available GetIssueBoardLists() options.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/boards.html#list-board-lists-in-a-project-issue-board
type GetIssueBoardListsOptions ListOptions

// GetIssueBoardLists gets a list of the issue board's lists. Does not include
// backlog and closed lists.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/boards.html#list-board-lists-in-a-project-issue-board
func (s *IssueBoardsService) GetIssueBoardLists(pid interface{}, board int, opt *GetIssueBoardListsOptions, options ...RequestOptionFunc) ([]*BoardList, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/boards/%d/lists", PathEscape(project), board)

	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var bl []*BoardList
	resp, err := s.client.Do(req, &bl)
	if err != nil {
		return nil, resp, err
	}

	return bl, resp, nil
}

// GetIssueBoardList gets a single issue board list.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/boards.html#show-a-single-board-list
func (s *IssueBoardsService) GetIssueBoardList(pid interface{}, board, list int, options ...RequestOptionFunc) (*BoardList, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/boards/%d/lists/%d",
		PathEscape(project),
		board,
		list,
	)

	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	bl := new(BoardList)
	resp, err := s.client.Do(req, bl)
	if err != nil {
		return nil, resp, err
	}

	return bl, resp, nil
}

// CreateIssueBoardListOptions represents the available CreateIssueBoardList()
// options.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/boards.html#create-a-board-list
type CreateIssueBoardListOptions struct {
	LabelID     *int `url:"label_id,omitempty" json:"label_id,omitempty"`
	AssigneeID  *int `url:"assignee_id,omitempty" json:"assignee_id,omitempty"`
	MilestoneID *int `url:"milestone_id,omitempty" json:"milestone_id,omitempty"`
	IterationID *int `url:"iteration_id,omitempty" json:"iteration_id,omitempty"`
}

// CreateIssueBoardList creates a new issue board list.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/boards.html#create-a-board-list
func (s *IssueBoardsService) CreateIssueBoardList(pid interface{}, board int, opt *CreateIssueBoardListOptions, options ...RequestOptionFunc) (*BoardList, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/boards/%d/lists", PathEscape(project), board)

	req, err := s.client.NewRequest(http.MethodPost, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	bl := new(BoardList)
	resp, err := s.client.Do(req, bl)
	if err != nil {
		return nil, resp, err
	}

	return bl, resp, nil
}

// UpdateIssueBoardListOptions represents the available UpdateIssueBoardList()
// options.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/boards.html#reorder-a-list-in-a-board
type UpdateIssueBoardListOptions struct {
	Position *int `url:"position" json:"position"`
}

// UpdateIssueBoardList updates the position of an existing issue board list.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/boards.html#reorder-a-list-in-a-board
func (s *IssueBoardsService) UpdateIssueBoardList(pid interface{}, board, list int, opt *UpdateIssueBoardListOptions, options ...RequestOptionFunc) (*BoardList, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/boards/%d/lists/%d",
		PathEscape(project),
		board,
		list,
	)

	req, err := s.client.NewRequest(http.MethodPut, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	bl := new(BoardList)
	resp, err := s.client.Do(req, bl)
	if err != nil {
		return nil, resp, err
	}

	return bl, resp, nil
}

// DeleteIssueBoardList soft deletes an issue board list. Only for admins and
// project owners.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/boards.html#delete-a-board-list-from-a-board
func (s *IssueBoardsService) DeleteIssueBoardList(pid interface{}, board, list int, options ...RequestOptionFunc) (*Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("projects/%s/boards/%d/lists/%d",
		PathEscape(project),
		board,
		list,
	)

	req, err := s.client.NewRequest(http.MethodDelete, u, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/boards_test.go000066400000000000000000000425041475761473200237240ustar00rootroot00000000000000package gitlab

import (
	"fmt"
	"net/http"
	"testing"

	"github.com/stretchr/testify/require"
)

func TestIssueBoardsService_CreateIssueBoard(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/5/boards", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		fmt.Fprintf(w, `
			  {
				"id": 1,
				"project": {
				  "id": 5,
				  "name": "Diaspora Project Site",
				  "name_with_namespace": "Diaspora / Diaspora Project Site",
				  "path": "diaspora-project-site",
				  "path_with_namespace": "diaspora/diaspora-project-site",
				  "http_url_to_repo": "http://example.com/diaspora/diaspora-project-site.git",
				  "web_url": "http://example.com/diaspora/diaspora-project-site"
				},
				"name": "newboard",
				"lists" : [],
				"group": null,
				"milestone": null,
				"assignee" : null,
				"labels" : [],
				"weight" : null
			  }
		`)
	})

	want := &IssueBoard{
		ID:   1,
		Name: "newboard",
		Project: &Project{
			ID:                5,
			HTTPURLToRepo:     "http://example.com/diaspora/diaspora-project-site.git",
			WebURL:            "http://example.com/diaspora/diaspora-project-site",
			Name:              "Diaspora Project Site",
			NameWithNamespace: "Diaspora / Diaspora Project Site",
			Path:              "diaspora-project-site",
			PathWithNamespace: "diaspora/diaspora-project-site",
		},
		Lists:  []*BoardList{},
		Labels: []*LabelDetails{},
	}

	ib, resp, err := client.Boards.CreateIssueBoard(5, nil, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, ib)

	ib, resp, err = client.Boards.CreateIssueBoard(5.01, nil, nil)
	require.EqualError(t, err, "invalid ID type 5.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, ib)

	ib, resp, err = client.Boards.CreateIssueBoard(5, nil, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, ib)

	ib, resp, err = client.Boards.CreateIssueBoard(7, nil, nil)
	require.Error(t, err)
	require.Nil(t, ib)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestIssueBoardsService_UpdateIssueBoard(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/5/boards/1", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPut)
		fmt.Fprintf(w, `
			  {
				"id": 1,
				"project": {
				  "id": 5,
				  "name": "Diaspora Project Site",
				  "name_with_namespace": "Diaspora / Diaspora Project Site",
				  "path": "diaspora-project-site",
				  "path_with_namespace": "diaspora/diaspora-project-site",
				  "http_url_to_repo": "http://example.com/diaspora/diaspora-project-site.git",
				  "web_url": "http://example.com/diaspora/diaspora-project-site"
				},
				"name": "new_name",
				"lists" : [],
				"group": null,
				"milestone": null,
				"assignee" : null,
				"labels" : [],
				"weight" : null
			  }
		`)
	})

	want := &IssueBoard{
		ID:   1,
		Name: "new_name",
		Project: &Project{
			ID:                5,
			HTTPURLToRepo:     "http://example.com/diaspora/diaspora-project-site.git",
			WebURL:            "http://example.com/diaspora/diaspora-project-site",
			Name:              "Diaspora Project Site",
			NameWithNamespace: "Diaspora / Diaspora Project Site",
			Path:              "diaspora-project-site",
			PathWithNamespace: "diaspora/diaspora-project-site",
		},
		Lists:  []*BoardList{},
		Labels: []*LabelDetails{},
	}

	ib, resp, err := client.Boards.UpdateIssueBoard(5, 1, nil, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, ib)

	ib, resp, err = client.Boards.UpdateIssueBoard(5.01, 1, nil, nil)
	require.EqualError(t, err, "invalid ID type 5.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, ib)

	ib, resp, err = client.Boards.UpdateIssueBoard(5, 1, nil, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, ib)

	ib, resp, err = client.Boards.UpdateIssueBoard(7, 1, nil, nil)
	require.Error(t, err)
	require.Nil(t, ib)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestIssueBoardsService_DeleteIssueBoard(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/5/boards/1", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodDelete)
	})

	resp, err := client.Boards.DeleteIssueBoard(5, 1, nil, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)

	resp, err = client.Boards.DeleteIssueBoard(5.01, 1, nil, nil)
	require.EqualError(t, err, "invalid ID type 5.01, the ID must be an int or a string")
	require.Nil(t, resp)

	resp, err = client.Boards.DeleteIssueBoard(5, 1, nil, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)

	resp, err = client.Boards.DeleteIssueBoard(7, 1, nil, nil)
	require.Error(t, err)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestIssueBoardsService_ListIssueBoards(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/5/boards", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
			[
			  {
				"id" : 1,
				"name": "board1",
				"project": {
				  "id": 5,
				  "name": "Diaspora Project Site",
				  "name_with_namespace": "Diaspora / Diaspora Project Site",
				  "path": "diaspora-project-site",
				  "path_with_namespace": "diaspora/diaspora-project-site",
				  "http_url_to_repo": "http://example.com/diaspora/diaspora-project-site.git",
				  "web_url": "http://example.com/diaspora/diaspora-project-site"
				},
				"milestone":   {
				  "id": 12,
				  "title": "10.0"
				},
				"lists" : [
				  {
					"id" : 1,
					"label" : {
					  "name" : "Testing",
					  "color" : "#F0AD4E",
					  "description" : null
					},
					"position" : 1,
					"max_issue_count": 0,
					"max_issue_weight": 0,
					"limit_metric": null
				  },
				  {
					"id" : 2,
					"label" : {
					  "name" : "Ready",
					  "color" : "#FF0000",
					  "description" : null
					},
					"position" : 2,
					"max_issue_count": 0,
					"max_issue_weight": 0,
					"limit_metric":  null
				  },
				  {
					"id" : 3,
					"label" : {
					  "name" : "Production",
					  "color" : "#FF5F00",
					  "description" : null
					},
					"position" : 3,
					"max_issue_count": 0,
					"max_issue_weight": 0,
					"limit_metric":  null
				  }
				]
			  }
			]
		`)
	})

	want := []*IssueBoard{{
		ID:   1,
		Name: "board1",
		Project: &Project{
			ID:                5,
			HTTPURLToRepo:     "http://example.com/diaspora/diaspora-project-site.git",
			WebURL:            "http://example.com/diaspora/diaspora-project-site",
			Name:              "Diaspora Project Site",
			NameWithNamespace: "Diaspora / Diaspora Project Site",
			Path:              "diaspora-project-site",
			PathWithNamespace: "diaspora/diaspora-project-site",
		},
		Milestone: &Milestone{
			ID:    12,
			Title: "10.0",
		},
		Lists: []*BoardList{
			{
				ID: 1,
				Label: &Label{
					Name:  "Testing",
					Color: "#F0AD4E",
				}, Position: 1,
			},
			{
				ID: 2,
				Label: &Label{
					Name:  "Ready",
					Color: "#FF0000",
				},
				Position: 2,
			},
			{
				ID: 3,
				Label: &Label{
					Name:  "Production",
					Color: "#FF5F00",
				},
				Position: 3,
			},
		},
	}}

	ibs, resp, err := client.Boards.ListIssueBoards(5, nil, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, ibs)

	ibs, resp, err = client.Boards.ListIssueBoards(5.01, nil, nil)
	require.EqualError(t, err, "invalid ID type 5.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, ibs)

	ibs, resp, err = client.Boards.ListIssueBoards(5, nil, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, ibs)

	ibs, resp, err = client.Boards.ListIssueBoards(7, nil, nil)
	require.Error(t, err)
	require.Nil(t, ibs)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestIssueBoardsService_GetIssueBoard(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/5/boards/1", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
		  {
			"id" : 1,
			"name": "board1",
			"project": {
			  "id": 5,
			  "name": "Diaspora Project Site",
			  "name_with_namespace": "Diaspora / Diaspora Project Site",
			  "path": "diaspora-project-site",
			  "path_with_namespace": "diaspora/diaspora-project-site",
			  "http_url_to_repo": "http://example.com/diaspora/diaspora-project-site.git",
			  "web_url": "http://example.com/diaspora/diaspora-project-site"
			},
			"milestone":   {
			  "id": 12,
			  "title": "10.0"
			},
			"lists" : [
			  {
				"id" : 1,
				"label" : {
				  "name" : "Testing",
				  "color" : "#F0AD4E",
				  "description" : null
				},
				"position" : 1,
				"max_issue_count": 0,
				"max_issue_weight": 0,
				"limit_metric": null
			  },
			  {
				"id" : 2,
				"label" : {
				  "name" : "Ready",
				  "color" : "#FF0000",
				  "description" : null
				},
				"position" : 2,
				"max_issue_count": 0,
				"max_issue_weight": 0,
				"limit_metric":  null
			  },
			  {
				"id" : 3,
				"label" : {
				  "name" : "Production",
				  "color" : "#FF5F00",
				  "description" : null
				},
				"position" : 3,
				"max_issue_count": 0,
				"max_issue_weight": 0,
				"limit_metric":  null
			  }
			]
		  }
		`)
	})

	want := &IssueBoard{
		ID:   1,
		Name: "board1",
		Project: &Project{
			ID:                5,
			HTTPURLToRepo:     "http://example.com/diaspora/diaspora-project-site.git",
			WebURL:            "http://example.com/diaspora/diaspora-project-site",
			Name:              "Diaspora Project Site",
			NameWithNamespace: "Diaspora / Diaspora Project Site",
			Path:              "diaspora-project-site",
			PathWithNamespace: "diaspora/diaspora-project-site",
		},
		Milestone: &Milestone{
			ID:    12,
			Title: "10.0",
		},
		Lists: []*BoardList{
			{
				ID: 1,
				Label: &Label{
					Name:  "Testing",
					Color: "#F0AD4E",
				},
				Position: 1,
			},
			{
				ID: 2,
				Label: &Label{
					Name:  "Ready",
					Color: "#FF0000",
				},
				Position: 2,
			},
			{
				ID: 3,
				Label: &Label{
					Name:  "Production",
					Color: "#FF5F00",
				},
				Position: 3,
			},
		},
	}

	ib, resp, err := client.Boards.GetIssueBoard(5, 1, nil, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, ib)

	ib, resp, err = client.Boards.GetIssueBoard(5.01, 1, nil, nil)
	require.EqualError(t, err, "invalid ID type 5.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, ib)

	ib, resp, err = client.Boards.GetIssueBoard(5, 1, nil, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, ib)

	ib, resp, err = client.Boards.GetIssueBoard(7, 1, nil, nil)
	require.Error(t, err)
	require.Nil(t, ib)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestIssueBoardsService_GetIssueBoardLists(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/5/boards/1/lists", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
			[
			  {
				"id" : 1,
				"label" : {
				  "name" : "Testing",
				  "color" : "#F0AD4E",
				  "description" : null
				},
				"position" : 1,
				"max_issue_count": 0,
				"max_issue_weight": 0,
				"limit_metric":  null
			  },
			  {
				"id" : 2,
				"label" : {
				  "name" : "Ready",
				  "color" : "#FF0000",
				  "description" : null
				},
				"position" : 2,
				"max_issue_count": 0,
				"max_issue_weight": 0,
				"limit_metric":  null
			  },
			  {
				"id" : 3,
				"label" : {
				  "name" : "Production",
				  "color" : "#FF5F00",
				  "description" : null
				},
				"position" : 3,
				"max_issue_count": 0,
				"max_issue_weight": 0,
				"limit_metric":  null
			  }
			]
		`)
	})

	want := []*BoardList{
		{
			ID: 1,
			Label: &Label{
				Name:  "Testing",
				Color: "#F0AD4E",
			},
			Position: 1,
		},
		{
			ID: 2,
			Label: &Label{
				Name:  "Ready",
				Color: "#FF0000",
			},
			Position: 2,
		},
		{
			ID: 3,
			Label: &Label{
				Name:  "Production",
				Color: "#FF5F00",
			},
			Position: 3,
		},
	}

	bls, resp, err := client.Boards.GetIssueBoardLists(5, 1, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, bls)

	bls, resp, err = client.Boards.GetIssueBoardLists(5.01, 1, nil)
	require.EqualError(t, err, "invalid ID type 5.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, bls)

	bls, resp, err = client.Boards.GetIssueBoardLists(5, 1, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, bls)

	bls, resp, err = client.Boards.GetIssueBoardLists(3, 1, nil)
	require.Error(t, err)
	require.Nil(t, bls)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestIssueBoardsService_GetIssueBoardList(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/5/boards/1/lists/1", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
		  {
			"id" : 1,
			"label" : {
			  "name" : "Testing",
			  "color" : "#F0AD4E",
			  "description" : null
			},
			"position" : 1,
			"max_issue_count": 0,
			"max_issue_weight": 0,
			"limit_metric":  null
		  }
		`)
	})

	want := &BoardList{
		ID: 1,
		Label: &Label{
			Name:  "Testing",
			Color: "#F0AD4E",
		},
		Position: 1,
	}

	bl, resp, err := client.Boards.GetIssueBoardList(5, 1, 1, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, bl)

	bl, resp, err = client.Boards.GetIssueBoardList(5.01, 1, 1, nil)
	require.EqualError(t, err, "invalid ID type 5.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, bl)

	bl, resp, err = client.Boards.GetIssueBoardList(5, 1, 1, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, bl)

	bl, resp, err = client.Boards.GetIssueBoardList(3, 1, 1, nil)
	require.Error(t, err)
	require.Nil(t, bl)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestIssueBoardsService_CreateIssueBoardList(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/5/boards/1/lists", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		fmt.Fprintf(w, `
		  {
			"id" : 1,
			"label" : {
			  "name" : "Testing",
			  "color" : "#F0AD4E",
			  "description" : null
			},
			"position" : 1,
			"max_issue_count": 0,
			"max_issue_weight": 0,
			"limit_metric":  null
		  }
		`)
	})

	want := &BoardList{
		ID: 1,
		Label: &Label{
			Name:  "Testing",
			Color: "#F0AD4E",
		},
		Position: 1,
	}

	bl, resp, err := client.Boards.CreateIssueBoardList(5, 1, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, bl)

	bl, resp, err = client.Boards.CreateIssueBoardList(5.01, 1, nil)
	require.EqualError(t, err, "invalid ID type 5.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, bl)

	bl, resp, err = client.Boards.CreateIssueBoardList(5, 1, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, bl)

	bl, resp, err = client.Boards.CreateIssueBoardList(3, 1, nil)
	require.Error(t, err)
	require.Nil(t, bl)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestIssueBoardsService_UpdateIssueBoardList(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/5/boards/1/lists/1", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPut)
		fmt.Fprintf(w, `
		  {
			"id" : 1,
			"label" : {
			  "name" : "Testing",
			  "color" : "#F0AD4E",
			  "description" : null
			},
			"position" : 1,
			"max_issue_count": 0,
			"max_issue_weight": 0,
			"limit_metric":  null
		  }
		`)
	})

	want := &BoardList{
		ID: 1,
		Label: &Label{
			Name:  "Testing",
			Color: "#F0AD4E",
		},
		Position: 1,
	}

	bl, resp, err := client.Boards.UpdateIssueBoardList(5, 1, 1, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, bl)

	bl, resp, err = client.Boards.UpdateIssueBoardList(5.01, 1, 1, nil)
	require.EqualError(t, err, "invalid ID type 5.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, bl)

	bl, resp, err = client.Boards.UpdateIssueBoardList(5, 1, 1, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, bl)

	bl, resp, err = client.Boards.UpdateIssueBoardList(3, 1, 1, nil)
	require.Error(t, err)
	require.Nil(t, bl)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestIssueBoardsService_DeleteIssueBoardList(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/5/boards/1/lists/1", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodDelete)
	})

	resp, err := client.Boards.DeleteIssueBoardList(5, 1, 1, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)

	resp, err = client.Boards.DeleteIssueBoardList(5.01, 1, 1, nil)
	require.EqualError(t, err, "invalid ID type 5.01, the ID must be an int or a string")
	require.Nil(t, resp)

	resp, err = client.Boards.DeleteIssueBoardList(5, 1, 1, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)

	resp, err = client.Boards.DeleteIssueBoardList(3, 1, 1, nil)
	require.Error(t, err)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/branches.go000066400000000000000000000171221475761473200231760ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"fmt"
	"net/http"
	"net/url"
)

// BranchesService handles communication with the branch related methods
// of the GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/branches.html
type BranchesService struct {
	client *Client
}

// Branch represents a GitLab branch.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/branches.html
type Branch struct {
	Commit             *Commit `json:"commit"`
	Name               string  `json:"name"`
	Protected          bool    `json:"protected"`
	Merged             bool    `json:"merged"`
	Default            bool    `json:"default"`
	CanPush            bool    `json:"can_push"`
	DevelopersCanPush  bool    `json:"developers_can_push"`
	DevelopersCanMerge bool    `json:"developers_can_merge"`
	WebURL             string  `json:"web_url"`
}

func (b Branch) String() string {
	return Stringify(b)
}

// ListBranchesOptions represents the available ListBranches() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/branches.html#list-repository-branches
type ListBranchesOptions struct {
	ListOptions
	Search *string `url:"search,omitempty" json:"search,omitempty"`
	Regex  *string `url:"regex,omitempty" json:"regex,omitempty"`
}

// ListBranches gets a list of repository branches from a project, sorted by
// name alphabetically.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/branches.html#list-repository-branches
func (s *BranchesService) ListBranches(pid interface{}, opts *ListBranchesOptions, options ...RequestOptionFunc) ([]*Branch, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/repository/branches", PathEscape(project))

	req, err := s.client.NewRequest(http.MethodGet, u, opts, options)
	if err != nil {
		return nil, nil, err
	}

	var b []*Branch
	resp, err := s.client.Do(req, &b)
	if err != nil {
		return nil, resp, err
	}

	return b, resp, nil
}

// GetBranch gets a single project repository branch.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/branches.html#get-single-repository-branch
func (s *BranchesService) GetBranch(pid interface{}, branch string, options ...RequestOptionFunc) (*Branch, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/repository/branches/%s", PathEscape(project), url.PathEscape(branch))

	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	b := new(Branch)
	resp, err := s.client.Do(req, b)
	if err != nil {
		return nil, resp, err
	}

	return b, resp, nil
}

// ProtectBranchOptions represents the available ProtectBranch() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/branches.html#protect-repository-branch
type ProtectBranchOptions struct {
	DevelopersCanPush  *bool `url:"developers_can_push,omitempty" json:"developers_can_push,omitempty"`
	DevelopersCanMerge *bool `url:"developers_can_merge,omitempty" json:"developers_can_merge,omitempty"`
}

// ProtectBranch protects a single project repository branch. This is an
// idempotent function, protecting an already protected repository branch
// still returns a 200 OK status code.
//
// Deprecated: This endpoint has been replaced by
// ProtectedBranchesService.ProtectRepositoryBranches()
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/branches.html#protect-repository-branch
func (s *BranchesService) ProtectBranch(pid interface{}, branch string, opts *ProtectBranchOptions, options ...RequestOptionFunc) (*Branch, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/repository/branches/%s/protect", PathEscape(project), url.PathEscape(branch))

	req, err := s.client.NewRequest(http.MethodPut, u, opts, options)
	if err != nil {
		return nil, nil, err
	}

	b := new(Branch)
	resp, err := s.client.Do(req, b)
	if err != nil {
		return nil, resp, err
	}

	return b, resp, nil
}

// UnprotectBranch unprotects a single project repository branch. This is an
// idempotent function, unprotecting an already unprotected repository branch
// still returns a 200 OK status code.
//
// Deprecated: This endpoint has been replaced by
// ProtectedBranchesService.UnprotectRepositoryBranches()
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/branches.html#unprotect-repository-branch
func (s *BranchesService) UnprotectBranch(pid interface{}, branch string, options ...RequestOptionFunc) (*Branch, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/repository/branches/%s/unprotect", PathEscape(project), url.PathEscape(branch))

	req, err := s.client.NewRequest(http.MethodPut, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	b := new(Branch)
	resp, err := s.client.Do(req, b)
	if err != nil {
		return nil, resp, err
	}

	return b, resp, nil
}

// CreateBranchOptions represents the available CreateBranch() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/branches.html#create-repository-branch
type CreateBranchOptions struct {
	Branch *string `url:"branch,omitempty" json:"branch,omitempty"`
	Ref    *string `url:"ref,omitempty" json:"ref,omitempty"`
}

// CreateBranch creates branch from commit SHA or existing branch.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/branches.html#create-repository-branch
func (s *BranchesService) CreateBranch(pid interface{}, opt *CreateBranchOptions, options ...RequestOptionFunc) (*Branch, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/repository/branches", PathEscape(project))

	req, err := s.client.NewRequest(http.MethodPost, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	b := new(Branch)
	resp, err := s.client.Do(req, b)
	if err != nil {
		return nil, resp, err
	}

	return b, resp, nil
}

// DeleteBranch deletes an existing branch.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/branches.html#delete-repository-branch
func (s *BranchesService) DeleteBranch(pid interface{}, branch string, options ...RequestOptionFunc) (*Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("projects/%s/repository/branches/%s", PathEscape(project), url.PathEscape(branch))

	req, err := s.client.NewRequest(http.MethodDelete, u, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}

// DeleteMergedBranches deletes all branches that are merged into the project's default branch.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/branches.html#delete-merged-branches
func (s *BranchesService) DeleteMergedBranches(pid interface{}, options ...RequestOptionFunc) (*Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("projects/%s/repository/merged_branches", PathEscape(project))

	req, err := s.client.NewRequest(http.MethodDelete, u, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/branches_test.go000066400000000000000000000254441475761473200242430ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"net/http"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestGetBranch(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/repository/branches/master", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		mustWriteHTTPResponse(t, w, "testdata/get_branch.json")
	})

	branch, resp, err := client.Branches.GetBranch(1, "master")
	if err != nil {
		t.Fatalf("Branches.GetBranch returned error: %v, response %v", err, resp)
	}

	authoredDate := time.Date(2012, 6, 27, 5, 51, 39, 0, time.UTC)
	committedDate := time.Date(2012, 6, 28, 3, 44, 20, 0, time.UTC)
	want := &Branch{
		Name:               "master",
		Merged:             false,
		Protected:          true,
		Default:            true,
		DevelopersCanPush:  false,
		DevelopersCanMerge: false,
		CanPush:            true,
		Commit: &Commit{
			AuthorEmail:    "john@example.com",
			AuthorName:     exampleEventUserName,
			AuthoredDate:   &authoredDate,
			CommittedDate:  &committedDate,
			CommitterEmail: "john@example.com",
			CommitterName:  exampleEventUserName,
			ID:             "7b5c3cc8be40ee161ae89a06bba6229da1032a0c",
			ShortID:        "7b5c3cc",
			Title:          "add projects API",
			Message:        "add projects API",
			ParentIDs:      []string{"4ad91d3c1144c406e50c7b33bae684bd6837faf8"},
		},
	}

	assert.Equal(t, want, branch)
}

func TestBranchesService_ListBranches(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/5/repository/branches", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		mustWriteHTTPResponse(t, w, "testdata/list_branches.json")
	})

	authoredDate := time.Date(2012, 6, 27, 5, 51, 39, 0, time.UTC)
	committedDate := time.Date(2012, 6, 28, 3, 44, 20, 0, time.UTC)
	want := []*Branch{{
		Name:               "master",
		Merged:             false,
		Protected:          true,
		Default:            true,
		DevelopersCanPush:  false,
		DevelopersCanMerge: false,
		CanPush:            true,
		WebURL:             "https://gitlab.example.com/my-group/my-project/-/tree/master",
		Commit: &Commit{
			AuthorEmail:    "john@example.com",
			AuthorName:     exampleEventUserName,
			AuthoredDate:   &authoredDate,
			CommittedDate:  &committedDate,
			CommitterEmail: "john@example.com",
			CommitterName:  exampleEventUserName,
			ID:             "7b5c3cc8be40ee161ae89a06bba6229da1032a0c",
			ShortID:        "7b5c3cc",
			Title:          "add projects API",
			Message:        "add projects API",
			ParentIDs:      []string{"4ad91d3c1144c406e50c7b33bae684bd6837faf8"},
		},
	}}

	b, resp, err := client.Branches.ListBranches(5, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, b)

	b, resp, err = client.Branches.ListBranches(5.01, nil)
	require.EqualError(t, err, "invalid ID type 5.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, b)

	b, resp, err = client.Branches.ListBranches(5, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, b)

	b, resp, err = client.Branches.ListBranches(3, nil)
	require.Error(t, err)
	require.Nil(t, b)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestBranchesService_ProtectBranch(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/repository/branches/master/protect", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPut)
		mustWriteHTTPResponse(t, w, "testdata/get_branch.json")
	})

	authoredDate := time.Date(2012, 6, 27, 5, 51, 39, 0, time.UTC)
	committedDate := time.Date(2012, 6, 28, 3, 44, 20, 0, time.UTC)
	want := &Branch{
		Name:               "master",
		Merged:             false,
		Protected:          true,
		Default:            true,
		DevelopersCanPush:  false,
		DevelopersCanMerge: false,
		CanPush:            true,
		Commit: &Commit{
			AuthorEmail:    "john@example.com",
			AuthorName:     exampleEventUserName,
			AuthoredDate:   &authoredDate,
			CommittedDate:  &committedDate,
			CommitterEmail: "john@example.com",
			CommitterName:  exampleEventUserName,
			ID:             "7b5c3cc8be40ee161ae89a06bba6229da1032a0c",
			ShortID:        "7b5c3cc",
			Title:          "add projects API",
			Message:        "add projects API",
			ParentIDs:      []string{"4ad91d3c1144c406e50c7b33bae684bd6837faf8"},
		},
	}

	b, resp, err := client.Branches.ProtectBranch(1, "master", nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, b)

	b, resp, err = client.Branches.ProtectBranch(1.01, "master", nil)
	require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, b)

	b, resp, err = client.Branches.ProtectBranch(1, "master", nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, b)

	b, resp, err = client.Branches.ProtectBranch(3, "master", nil)
	require.Error(t, err)
	require.Nil(t, b)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestBranchesService_UnprotectBranch(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/repository/branches/master/unprotect", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPut)
		mustWriteHTTPResponse(t, w, "testdata/get_branch.json")
	})

	authoredDate := time.Date(2012, 6, 27, 5, 51, 39, 0, time.UTC)
	committedDate := time.Date(2012, 6, 28, 3, 44, 20, 0, time.UTC)
	want := &Branch{
		Name:               "master",
		Merged:             false,
		Protected:          true,
		Default:            true,
		DevelopersCanPush:  false,
		DevelopersCanMerge: false,
		CanPush:            true,
		Commit: &Commit{
			AuthorEmail:    "john@example.com",
			AuthorName:     exampleEventUserName,
			AuthoredDate:   &authoredDate,
			CommittedDate:  &committedDate,
			CommitterEmail: "john@example.com",
			CommitterName:  exampleEventUserName,
			ID:             "7b5c3cc8be40ee161ae89a06bba6229da1032a0c",
			ShortID:        "7b5c3cc",
			Title:          "add projects API",
			Message:        "add projects API",
			ParentIDs:      []string{"4ad91d3c1144c406e50c7b33bae684bd6837faf8"},
		},
	}

	b, resp, err := client.Branches.UnprotectBranch(1, "master", nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, b)

	b, resp, err = client.Branches.UnprotectBranch(1.01, "master", nil)
	require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, b)

	b, resp, err = client.Branches.UnprotectBranch(1, "master", nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, b)

	b, resp, err = client.Branches.UnprotectBranch(3, "master", nil)
	require.Error(t, err)
	require.Nil(t, b)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestBranchesService_CreateBranch(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/repository/branches", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		mustWriteHTTPResponse(t, w, "testdata/get_branch.json")
	})

	authoredDate := time.Date(2012, 6, 27, 5, 51, 39, 0, time.UTC)
	committedDate := time.Date(2012, 6, 28, 3, 44, 20, 0, time.UTC)
	want := &Branch{
		Name:               "master",
		Merged:             false,
		Protected:          true,
		Default:            true,
		DevelopersCanPush:  false,
		DevelopersCanMerge: false,
		CanPush:            true,
		Commit: &Commit{
			AuthorEmail:    "john@example.com",
			AuthorName:     exampleEventUserName,
			AuthoredDate:   &authoredDate,
			CommittedDate:  &committedDate,
			CommitterEmail: "john@example.com",
			CommitterName:  exampleEventUserName,
			ID:             "7b5c3cc8be40ee161ae89a06bba6229da1032a0c",
			ShortID:        "7b5c3cc",
			Title:          "add projects API",
			Message:        "add projects API",
			ParentIDs:      []string{"4ad91d3c1144c406e50c7b33bae684bd6837faf8"},
		},
	}

	b, resp, err := client.Branches.CreateBranch(1, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, b)

	b, resp, err = client.Branches.CreateBranch(1.01, nil)
	require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, b)

	b, resp, err = client.Branches.CreateBranch(1, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, b)

	b, resp, err = client.Branches.CreateBranch(3, nil)
	require.Error(t, err)
	require.Nil(t, b)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestBranchesService_DeleteBranch(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/repository/branches/master", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodDelete)
	})

	resp, err := client.Branches.DeleteBranch(1, "master", nil)
	require.NoError(t, err)
	require.NotNil(t, resp)

	resp, err = client.Branches.DeleteBranch(1.01, "master", nil)
	require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string")
	require.Nil(t, resp)

	resp, err = client.Branches.DeleteBranch(1, "master", nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)

	resp, err = client.Branches.DeleteBranch(3, "master", nil)
	require.Error(t, err)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestBranchesService_DeleteMergedBranches(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/repository/merged_branches", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodDelete)
	})

	resp, err := client.Branches.DeleteMergedBranches(1, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)

	resp, err = client.Branches.DeleteMergedBranches(1.01, nil)
	require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string")
	require.Nil(t, resp)

	resp, err = client.Branches.DeleteMergedBranches(1, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)

	resp, err = client.Branches.DeleteMergedBranches(3, nil)
	require.Error(t, err)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/broadcast_messages.go000066400000000000000000000164241475761473200252460ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"fmt"
	"net/http"
	"time"
)

// BroadcastMessagesService handles communication with the broadcast
// messages methods of the GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/broadcast_messages.html
type BroadcastMessagesService struct {
	client *Client
}

// BroadcastMessage represents a GitLab broadcast message.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/broadcast_messages.html#get-all-broadcast-messages
type BroadcastMessage struct {
	Message            string             `json:"message"`
	StartsAt           *time.Time         `json:"starts_at"`
	EndsAt             *time.Time         `json:"ends_at"`
	Font               string             `json:"font"`
	ID                 int                `json:"id"`
	Active             bool               `json:"active"`
	TargetAccessLevels []AccessLevelValue `json:"target_access_levels"`
	TargetPath         string             `json:"target_path"`
	BroadcastType      string             `json:"broadcast_type"`
	Dismissable        bool               `json:"dismissable"`
	Theme              string             `json:"theme"`

	// Deprecated: This parameter was removed in GitLab 15.6.
	Color string `json:"color"`
}

// ListBroadcastMessagesOptions represents the available ListBroadcastMessages()
// options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/broadcast_messages.html#get-all-broadcast-messages
type ListBroadcastMessagesOptions ListOptions

// ListBroadcastMessages gets a list of all broadcasted messages.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/broadcast_messages.html#get-all-broadcast-messages
func (s *BroadcastMessagesService) ListBroadcastMessages(opt *ListBroadcastMessagesOptions, options ...RequestOptionFunc) ([]*BroadcastMessage, *Response, error) {
	req, err := s.client.NewRequest(http.MethodGet, "broadcast_messages", opt, options)
	if err != nil {
		return nil, nil, err
	}

	var bs []*BroadcastMessage
	resp, err := s.client.Do(req, &bs)
	if err != nil {
		return nil, resp, err
	}

	return bs, resp, nil
}

// GetBroadcastMessage gets a single broadcast message.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/broadcast_messages.html#get-a-specific-broadcast-message
func (s *BroadcastMessagesService) GetBroadcastMessage(broadcast int, options ...RequestOptionFunc) (*BroadcastMessage, *Response, error) {
	u := fmt.Sprintf("broadcast_messages/%d", broadcast)

	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	b := new(BroadcastMessage)
	resp, err := s.client.Do(req, &b)
	if err != nil {
		return nil, resp, err
	}

	return b, resp, nil
}

// CreateBroadcastMessageOptions represents the available CreateBroadcastMessage()
// options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/broadcast_messages.html#create-a-broadcast-message
type CreateBroadcastMessageOptions struct {
	Message            *string            `url:"message" json:"message"`
	StartsAt           *time.Time         `url:"starts_at,omitempty" json:"starts_at,omitempty"`
	EndsAt             *time.Time         `url:"ends_at,omitempty" json:"ends_at,omitempty"`
	Font               *string            `url:"font,omitempty" json:"font,omitempty"`
	TargetAccessLevels []AccessLevelValue `url:"target_access_levels,omitempty" json:"target_access_levels,omitempty"`
	TargetPath         *string            `url:"target_path,omitempty" json:"target_path,omitempty"`
	BroadcastType      *string            `url:"broadcast_type,omitempty" json:"broadcast_type,omitempty"`
	Dismissable        *bool              `url:"dismissable,omitempty" json:"dismissable,omitempty"`
	Theme              *string            `url:"theme,omitempty" json:"theme,omitempty"`

	// Deprecated: This parameter was removed in GitLab 15.6.
	Color *string `url:"color,omitempty" json:"color,omitempty"`
}

// CreateBroadcastMessage creates a message to broadcast.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/broadcast_messages.html#create-a-broadcast-message
func (s *BroadcastMessagesService) CreateBroadcastMessage(opt *CreateBroadcastMessageOptions, options ...RequestOptionFunc) (*BroadcastMessage, *Response, error) {
	req, err := s.client.NewRequest(http.MethodPost, "broadcast_messages", opt, options)
	if err != nil {
		return nil, nil, err
	}

	b := new(BroadcastMessage)
	resp, err := s.client.Do(req, &b)
	if err != nil {
		return nil, resp, err
	}

	return b, resp, nil
}

// UpdateBroadcastMessageOptions represents the available CreateBroadcastMessage()
// options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/broadcast_messages.html#update-a-broadcast-message
type UpdateBroadcastMessageOptions struct {
	Message            *string            `url:"message,omitempty" json:"message,omitempty"`
	StartsAt           *time.Time         `url:"starts_at,omitempty" json:"starts_at,omitempty"`
	EndsAt             *time.Time         `url:"ends_at,omitempty" json:"ends_at,omitempty"`
	Font               *string            `url:"font,omitempty" json:"font,omitempty"`
	TargetAccessLevels []AccessLevelValue `url:"target_access_levels,omitempty" json:"target_access_levels,omitempty"`
	TargetPath         *string            `url:"target_path,omitempty" json:"target_path,omitempty"`
	BroadcastType      *string            `url:"broadcast_type,omitempty" json:"broadcast_type,omitempty"`
	Dismissable        *bool              `url:"dismissable,omitempty" json:"dismissable,omitempty"`
	Theme              *string            `url:"theme,omitempty" json:"theme,omitempty"`

	// Deprecated: This parameter was removed in GitLab 15.6.
	Color *string `url:"color,omitempty" json:"color,omitempty"`
}

// UpdateBroadcastMessage update a broadcasted message.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/broadcast_messages.html#update-a-broadcast-message
func (s *BroadcastMessagesService) UpdateBroadcastMessage(broadcast int, opt *UpdateBroadcastMessageOptions, options ...RequestOptionFunc) (*BroadcastMessage, *Response, error) {
	u := fmt.Sprintf("broadcast_messages/%d", broadcast)

	req, err := s.client.NewRequest(http.MethodPut, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	b := new(BroadcastMessage)
	resp, err := s.client.Do(req, &b)
	if err != nil {
		return nil, resp, err
	}

	return b, resp, nil
}

// DeleteBroadcastMessage deletes a broadcasted message.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/broadcast_messages.html#delete-a-broadcast-message
func (s *BroadcastMessagesService) DeleteBroadcastMessage(broadcast int, options ...RequestOptionFunc) (*Response, error) {
	u := fmt.Sprintf("broadcast_messages/%d", broadcast)

	req, err := s.client.NewRequest(http.MethodDelete, u, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/broadcast_messages_test.go000066400000000000000000000211421475761473200262760ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"fmt"
	"net/http"
	"reflect"
	"testing"
	"time"
)

func TestListBroadcastMessages(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/broadcast_messages", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `[{
			"message": "Some Message",
			"starts_at": "2017-06-26T06:00:00.000Z",
			"ends_at": "2017-06-27T12:59:00.000Z",
			"color": "#E75E40",
			"font": "#FFFFFF",
			"id": 1,
			"active": false,
			"target_access_levels": [10,30],
			"target_path": "*/welcome",
			"broadcast_type": "banner",
			"dismissable": false,
			"theme": "indigo"
		},{
			"message": "SomeMessage2",
			"starts_at": "2015-04-27T06:43:00.000Z",
			"ends_at": "2015-04-28T20:43:00.000Z",
			"color": "#AA33EE",
			"font": "#224466",
			"id": 2,
			"active": true,
			"target_access_levels": [],
			"target_path": "*/*",
			"broadcast_type": "notification",
			"dismissable": true
		}]`)
	})

	got, _, err := client.BroadcastMessage.ListBroadcastMessages(nil, nil)
	if err != nil {
		t.Errorf("ListBroadcastMessages returned error: %v", err)
	}

	wantedFirstStartsAt := time.Date(2017, 0o6, 26, 6, 0, 0, 0, time.UTC)
	wantedFirstEndsAt := time.Date(2017, 0o6, 27, 12, 59, 0, 0, time.UTC)

	wantedSecondStartsAt := time.Date(2015, 0o4, 27, 6, 43, 0, 0, time.UTC)
	wantedSecondEndsAt := time.Date(2015, 0o4, 28, 20, 43, 0, 0, time.UTC)

	want := []*BroadcastMessage{{
		Message:            "Some Message",
		StartsAt:           &wantedFirstStartsAt,
		EndsAt:             &wantedFirstEndsAt,
		Color:              "#E75E40",
		Font:               "#FFFFFF",
		ID:                 1,
		Active:             false,
		TargetAccessLevels: []AccessLevelValue{GuestPermissions, DeveloperPermissions},
		TargetPath:         "*/welcome",
		BroadcastType:      "banner",
		Dismissable:        false,
		Theme:              "indigo",
	}, {
		Message:            "SomeMessage2",
		StartsAt:           &wantedSecondStartsAt,
		EndsAt:             &wantedSecondEndsAt,
		Color:              "#AA33EE",
		Font:               "#224466",
		ID:                 2,
		Active:             true,
		TargetAccessLevels: []AccessLevelValue{},
		TargetPath:         "*/*",
		BroadcastType:      "notification",
		Dismissable:        true,
	}}

	if !reflect.DeepEqual(got, want) {
		t.Errorf("ListBroadcastMessages returned \ngot:\n%v\nwant:\n%v", Stringify(got), Stringify(want))
	}
}

func TestGetBroadcastMessages(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/broadcast_messages/1/", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `{
			"message": "Some Message",
			"starts_at": "2017-06-26T06:00:00.000Z",
			"ends_at": "2017-06-27T12:59:00.000Z",
			"color": "#E75E40",
			"font": "#FFFFFF",
			"id": 1,
			"active": false,
			"target_access_levels": [10,30],
			"target_path": "*/welcome",
			"broadcast_type": "banner",
			"dismissable": false,
			"theme": "indigo"
		}`)
	})

	got, _, err := client.BroadcastMessage.GetBroadcastMessage(1)
	if err != nil {
		t.Errorf("GetBroadcastMessage returned error: %v", err)
	}

	wantedStartsAt := time.Date(2017, time.June, 26, 6, 0, 0, 0, time.UTC)
	wantedEndsAt := time.Date(2017, time.June, 27, 12, 59, 0, 0, time.UTC)

	want := &BroadcastMessage{
		Message:            "Some Message",
		StartsAt:           &wantedStartsAt,
		EndsAt:             &wantedEndsAt,
		Color:              "#E75E40",
		Font:               "#FFFFFF",
		ID:                 1,
		Active:             false,
		TargetAccessLevels: []AccessLevelValue{GuestPermissions, DeveloperPermissions},
		TargetPath:         "*/welcome",
		BroadcastType:      "banner",
		Dismissable:        false,
		Theme:              "indigo",
	}
	if !reflect.DeepEqual(got, want) {
		t.Errorf("GetBroadcastMessage returned \ngot:\n%v\nwant:\n%v", Stringify(got), Stringify(want))
	}
}

func TestCreateBroadcastMessages(t *testing.T) {
	mux, client := setup(t)

	wantedStartsAt := time.Date(2017, time.June, 26, 6, 0, 0, 0, time.UTC)
	wantedEndsAt := time.Date(2017, time.June, 27, 12, 59, 0, 0, time.UTC)

	mux.HandleFunc("/api/v4/broadcast_messages", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		fmt.Fprintf(w, `{
			"message": "Some Message",
			"starts_at": "2017-06-26T06:00:00.000Z",
			"ends_at": "2017-06-27T12:59:00.000Z",
			"color": "#E75E40",
			"font": "#FFFFFF",
			"id": 42,
			"active": false,
			"target_access_levels": [10,30],
			"target_path": "*/welcome",
			"broadcast_type": "banner",
			"dismissable": false,
			"theme": "indigo"
		}`)
	})

	opt := &CreateBroadcastMessageOptions{
		Message:            Ptr("Some Message"),
		StartsAt:           &wantedStartsAt,
		EndsAt:             &wantedEndsAt,
		Color:              Ptr("#E75E40"),
		Font:               Ptr("#FFFFFF"),
		TargetAccessLevels: []AccessLevelValue{GuestPermissions, DeveloperPermissions},
		TargetPath:         Ptr("*/welcome"),
		BroadcastType:      Ptr("banner"),
		Dismissable:        Ptr(false),
		Theme:              Ptr("indigo"),
	}

	got, _, err := client.BroadcastMessage.CreateBroadcastMessage(opt)
	if err != nil {
		t.Errorf("CreateBroadcastMessage returned error: %v", err)
	}

	want := &BroadcastMessage{
		Message:            "Some Message",
		StartsAt:           &wantedStartsAt,
		EndsAt:             &wantedEndsAt,
		Color:              "#E75E40",
		Font:               "#FFFFFF",
		ID:                 42,
		Active:             false,
		TargetAccessLevels: []AccessLevelValue{GuestPermissions, DeveloperPermissions},
		TargetPath:         "*/welcome",
		BroadcastType:      "banner",
		Dismissable:        false,
		Theme:              "indigo",
	}

	if !reflect.DeepEqual(got, want) {
		t.Errorf("CreateBroadcastMessage returned \ngot:\n%v\nwant:\n%v", Stringify(got), Stringify(want))
	}
}

func TestUpdateBroadcastMessages(t *testing.T) {
	mux, client := setup(t)

	wantedStartsAt := time.Date(2017, time.June, 26, 6, 0, 0, 0, time.UTC)
	wantedEndsAt := time.Date(2017, time.June, 27, 12, 59, 0, 0, time.UTC)

	mux.HandleFunc("/api/v4/broadcast_messages/1", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPut)
		fmt.Fprintf(w, `{
			"message": "Some Message Updated",
			"starts_at": "2017-06-26T06:00:00.000Z",
			"ends_at": "2017-06-27T12:59:00.000Z",
			"color": "#E75E40",
			"font": "#FFFFFF",
			"id": 42,
			"active": false,
			"target_access_levels": [10,30],
			"target_path": "*/welcome",
			"broadcast_type": "banner",
			"dismissable": false,
			"theme": "indigo"
		}`)
	})

	opt := &UpdateBroadcastMessageOptions{
		Message:            Ptr("Some Message Updated"),
		StartsAt:           &wantedStartsAt,
		EndsAt:             &wantedEndsAt,
		Color:              Ptr("#E75E40"),
		Font:               Ptr("#FFFFFF"),
		TargetAccessLevels: []AccessLevelValue{GuestPermissions, DeveloperPermissions},
		TargetPath:         Ptr("*/welcome"),
		BroadcastType:      Ptr("banner"),
		Dismissable:        Ptr(false),
		Theme:              Ptr("indigo"),
	}

	got, _, err := client.BroadcastMessage.UpdateBroadcastMessage(1, opt)
	if err != nil {
		t.Errorf("UpdateBroadcastMessage returned error: %v", err)
	}

	want := &BroadcastMessage{
		Message:            "Some Message Updated",
		StartsAt:           &wantedStartsAt,
		EndsAt:             &wantedEndsAt,
		Color:              "#E75E40",
		Font:               "#FFFFFF",
		ID:                 42,
		Active:             false,
		TargetAccessLevels: []AccessLevelValue{GuestPermissions, DeveloperPermissions},
		TargetPath:         "*/welcome",
		BroadcastType:      "banner",
		Dismissable:        false,
		Theme:              "indigo",
	}

	if !reflect.DeepEqual(got, want) {
		t.Errorf("UpdateBroadcastMessage returned \ngot:\n%v\nwant:\n%v", Stringify(got), Stringify(want))
	}
}

func TestDeleteBroadcastMessages(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/broadcast_messages/1", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodDelete)
	})

	_, err := client.BroadcastMessage.DeleteBroadcastMessage(1)
	if err != nil {
		t.Errorf("UpdateBroadcastMessage returned error: %v", err)
	}
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/bulk_imports.go000066400000000000000000000055601475761473200241260ustar00rootroot00000000000000package gitlab

import (
	"net/http"
	"time"
)

// BulkImportsService handles communication with GitLab's direct transfer API.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/bulk_imports.html
type BulkImportsService struct {
	client *Client
}

// BulkImportStartMigrationConfiguration represents the available configuration options to start a migration.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/bulk_imports.html#start-a-new-group-or-project-migration
type BulkImportStartMigrationConfiguration struct {
	URL         *string `json:"url,omitempty"`
	AccessToken *string `json:"access_token,omitempty"`
}

// BulkImportStartMigrationEntity represents the available entity options to start a migration.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/bulk_imports.html#start-a-new-group-or-project-migration
type BulkImportStartMigrationEntity struct {
	SourceType           *string `json:"source_type,omitempty"`
	SourceFullPath       *string `json:"source_full_path,omitempty"`
	DestinationSlug      *string `json:"destination_slug,omitempty"`
	DestinationNamespace *string `json:"destination_namespace,omitempty"`
	MigrateProjects      *bool   `json:"migrate_projects,omitempty"`
	MigrateMemberships   *bool   `json:"migrate_memberships,omitempty"`
}

// BulkImportStartMigrationOptions represents the available start migration options.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/bulk_imports.html#start-a-new-group-or-project-migration
type BulkImportStartMigrationOptions struct {
	Configuration *BulkImportStartMigrationConfiguration `json:"configuration,omitempty"`
	Entities      []BulkImportStartMigrationEntity       `json:"entities,omitempty"`
}

// BulkImportStartMigrationResponse represents the start migration response.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/bulk_imports.html#start-a-new-group-or-project-migration
type BulkImportStartMigrationResponse struct {
	ID          int       `json:"id"`
	Status      string    `json:"status"`
	SourceType  string    `json:"source_type"`
	SourceURL   string    `json:"source_url"`
	CreatedAt   time.Time `json:"created_at"`
	UpdatedAt   time.Time `json:"updated_at"`
	HasFailures bool      `json:"has_failures"`
}

// StartMigration starts a migration.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/bulk_imports.html#start-a-new-group-or-project-migration
func (b *BulkImportsService) StartMigration(startMigrationOptions *BulkImportStartMigrationOptions, options ...RequestOptionFunc) (*BulkImportStartMigrationResponse, *Response, error) {
	request, err := b.client.NewRequest(http.MethodPost, "bulk_imports", startMigrationOptions, options)
	if err != nil {
		return nil, nil, err
	}

	startMigrationResponse := new(BulkImportStartMigrationResponse)
	response, err := b.client.Do(request, startMigrationResponse)
	if err != nil {
		return nil, response, err
	}

	return startMigrationResponse, response, nil
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/bulk_imports_test.go000066400000000000000000000034551475761473200251660ustar00rootroot00000000000000package gitlab

import (
	"io"
	"net/http"
	"os"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestBulkImportsService_StartMigration(t *testing.T) {
	startMigrationOptions := &BulkImportStartMigrationOptions{
		Configuration: &BulkImportStartMigrationConfiguration{
			URL:         Ptr("https://source-gitlab-instance.example.com"),
			AccessToken: Ptr("source-gitlab-instance-access-token"),
		},
		Entities: []BulkImportStartMigrationEntity{
			{
				SourceType:           Ptr("group_entity"),
				SourceFullPath:       Ptr("gitlab-org/gitlab"),
				DestinationSlug:      Ptr("destination_slug"),
				DestinationNamespace: Ptr("destination/namespace/path"),
				MigrateProjects:      Ptr(true),
				MigrateMemberships:   Ptr(true),
			},
		},
	}
	wantStartMigrationResponse := &BulkImportStartMigrationResponse{
		ID:          1337,
		Status:      "created",
		SourceType:  "group_entity",
		SourceURL:   "https://source-gitlab-instance.example.com",
		CreatedAt:   time.Date(2021, time.June, 18, 9, 45, 55, 358000000, time.UTC),
		UpdatedAt:   time.Date(2021, time.June, 18, 9, 46, 27, 3000000, time.UTC),
		HasFailures: false,
	}
	mux, client := setup(t)
	mux.HandleFunc("/api/v4/bulk_imports", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		gotBody, err := io.ReadAll(r.Body)
		require.NoError(t, err)
		wantBody, err := os.ReadFile("testdata/post_bulk_imports_request.json")
		require.NoError(t, err)
		assert.JSONEq(t, string(wantBody), string(gotBody))
		mustWriteHTTPResponse(t, w, "testdata/post_bulk_imports_response.json")
	})

	gotStartMigrationResponse, _, err := client.BulkImports.StartMigration(startMigrationOptions, nil)

	require.NoError(t, err)
	assert.Equal(t, wantStartMigrationResponse, gotStartMigrationResponse)
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/ci_yml_templates.go000066400000000000000000000054411475761473200247440ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"fmt"
	"net/http"
)

// CIYMLTemplatesService handles communication with the gitlab
// CI YML templates related methods of the GitLab API.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/templates/gitlab_ci_ymls.html
type CIYMLTemplatesService struct {
	client *Client
}

// CIYMLTemplate represents a GitLab CI YML template.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/templates/gitlab_ci_ymls.html
type CIYMLTemplate struct {
	Name    string `json:"name"`
	Content string `json:"content"`
}

// CIYMLTemplateListItem represents a GitLab CI YML template from the list.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/templates/gitlab_ci_ymls.html
type CIYMLTemplateListItem struct {
	Key  string `json:"key"`
	Name string `json:"name"`
}

// ListCIYMLTemplatesOptions represents the available ListAllTemplates() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/templates/gitlab_ci_ymls.html#list-gitlab-ci-yaml-templates
type ListCIYMLTemplatesOptions ListOptions

// ListAllTemplates get all GitLab CI YML templates.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/templates/gitlab_ci_ymls.html#list-gitlab-ci-yaml-templates
func (s *CIYMLTemplatesService) ListAllTemplates(opt *ListCIYMLTemplatesOptions, options ...RequestOptionFunc) ([]*CIYMLTemplateListItem, *Response, error) {
	req, err := s.client.NewRequest(http.MethodGet, "templates/gitlab_ci_ymls", opt, options)
	if err != nil {
		return nil, nil, err
	}

	var cts []*CIYMLTemplateListItem
	resp, err := s.client.Do(req, &cts)
	if err != nil {
		return nil, resp, err
	}

	return cts, resp, nil
}

// GetTemplate get a single GitLab CI YML template.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/templates/gitlab_ci_ymls.html#single-gitlab-ci-yaml-template
func (s *CIYMLTemplatesService) GetTemplate(key string, options ...RequestOptionFunc) (*CIYMLTemplate, *Response, error) {
	u := fmt.Sprintf("templates/gitlab_ci_ymls/%s", PathEscape(key))

	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	ct := new(CIYMLTemplate)
	resp, err := s.client.Do(req, ct)
	if err != nil {
		return nil, resp, err
	}

	return ct, resp, nil
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/ci_yml_templates_test.go000066400000000000000000000050121475761473200257750ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"fmt"
	"net/http"
	"reflect"
	"testing"
)

func TestListAllTemplates(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/templates/gitlab_ci_ymls", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `[
			{
			   "key":"5-Minute-Production-App",
			   "name":"5-Minute-Production-App"
			},
			{
			   "key":"Android",
			   "name":"Android"
			},
			{
			   "key":"Android-Fastlane",
			   "name":"Android-Fastlane"
			},
			{
			   "key":"Auto-DevOps",
			   "name":"Auto-DevOps"
			}
		 ]`)
	})

	templates, _, err := client.CIYMLTemplate.ListAllTemplates(&ListCIYMLTemplatesOptions{})
	if err != nil {
		t.Errorf("CIYMLTemplates.ListAllTemplates returned error: %v", err)
	}

	want := []*CIYMLTemplateListItem{
		{
			Key:  "5-Minute-Production-App",
			Name: "5-Minute-Production-App",
		},
		{
			Key:  "Android",
			Name: "Android",
		},
		{
			Key:  "Android-Fastlane",
			Name: "Android-Fastlane",
		},
		{
			Key:  "Auto-DevOps",
			Name: "Auto-DevOps",
		},
	}
	if !reflect.DeepEqual(want, templates) {
		t.Errorf("CIYMLTemplates.ListAllTemplates returned %+v, want %+v", templates, want)
	}
}

func TestGetTemplate(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/templates/gitlab_ci_ymls/Ruby", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `{
			"name": "Ruby",
			"content": "# This file is a template, and might need editing before it works on your project."
		  }`)
	})

	template, _, err := client.CIYMLTemplate.GetTemplate("Ruby")
	if err != nil {
		t.Errorf("CIYMLTemplates.GetTemplate returned error: %v", err)
	}

	want := &CIYMLTemplate{
		Name:    "Ruby",
		Content: "# This file is a template, and might need editing before it works on your project.",
	}
	if !reflect.DeepEqual(want, template) {
		t.Errorf("CIYMLTemplates.GetTemplate returned %+v, want %+v", template, want)
	}
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/client_options.go000066400000000000000000000100351475761473200244360ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"net/http"
	"time"

	retryablehttp "github.com/hashicorp/go-retryablehttp"
)

// ClientOptionFunc can be used to customize a new GitLab API client.
type ClientOptionFunc func(*Client) error

// WithBaseURL sets the base URL for API requests to a custom endpoint.
func WithBaseURL(urlStr string) ClientOptionFunc {
	return func(c *Client) error {
		return c.setBaseURL(urlStr)
	}
}

// WithCustomBackoff can be used to configure a custom backoff policy.
func WithCustomBackoff(backoff retryablehttp.Backoff) ClientOptionFunc {
	return func(c *Client) error {
		c.client.Backoff = backoff
		return nil
	}
}

// WithCustomLeveledLogger can be used to configure a custom retryablehttp
// leveled logger.
func WithCustomLeveledLogger(leveledLogger retryablehttp.LeveledLogger) ClientOptionFunc {
	return func(c *Client) error {
		c.client.Logger = leveledLogger
		return nil
	}
}

// WithCustomLimiter injects a custom rate limiter to the client.
func WithCustomLimiter(limiter RateLimiter) ClientOptionFunc {
	return func(c *Client) error {
		c.configureLimiterOnce.Do(func() {})
		c.limiter = limiter
		return nil
	}
}

// WithCustomLogger can be used to configure a custom retryablehttp logger.
func WithCustomLogger(logger retryablehttp.Logger) ClientOptionFunc {
	return func(c *Client) error {
		c.client.Logger = logger
		return nil
	}
}

// WithCustomRetry can be used to configure a custom retry policy.
func WithCustomRetry(checkRetry retryablehttp.CheckRetry) ClientOptionFunc {
	return func(c *Client) error {
		c.client.CheckRetry = checkRetry
		return nil
	}
}

// WithCustomRetryMax can be used to configure a custom maximum number of retries.
func WithCustomRetryMax(retryMax int) ClientOptionFunc {
	return func(c *Client) error {
		c.client.RetryMax = retryMax
		return nil
	}
}

// WithCustomRetryWaitMinMax can be used to configure a custom minimum and
// maximum time to wait between retries.
func WithCustomRetryWaitMinMax(waitMin, waitMax time.Duration) ClientOptionFunc {
	return func(c *Client) error {
		c.client.RetryWaitMin = waitMin
		c.client.RetryWaitMax = waitMax
		return nil
	}
}

// WithErrorHandler can be used to configure a custom error handler.
func WithErrorHandler(handler retryablehttp.ErrorHandler) ClientOptionFunc {
	return func(c *Client) error {
		c.client.ErrorHandler = handler
		return nil
	}
}

// WithHTTPClient can be used to configure a custom HTTP client.
func WithHTTPClient(httpClient *http.Client) ClientOptionFunc {
	return func(c *Client) error {
		c.client.HTTPClient = httpClient
		return nil
	}
}

// WithRequestLogHook can be used to configure a custom request log hook.
func WithRequestLogHook(hook retryablehttp.RequestLogHook) ClientOptionFunc {
	return func(c *Client) error {
		c.client.RequestLogHook = hook
		return nil
	}
}

// WithResponseLogHook can be used to configure a custom response log hook.
func WithResponseLogHook(hook retryablehttp.ResponseLogHook) ClientOptionFunc {
	return func(c *Client) error {
		c.client.ResponseLogHook = hook
		return nil
	}
}

// WithoutRetries disables the default retry logic.
func WithoutRetries() ClientOptionFunc {
	return func(c *Client) error {
		c.disableRetries = true
		return nil
	}
}

// WithRequestOptions can be used to configure default request options applied to every request.
func WithRequestOptions(options ...RequestOptionFunc) ClientOptionFunc {
	return func(c *Client) error {
		c.defaultRequestOptions = append(c.defaultRequestOptions, options...)
		return nil
	}
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/cluster_agents.go000066400000000000000000000211631475761473200244330ustar00rootroot00000000000000//
// Copyright 2022, Timo Furrer 
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"fmt"
	"net/http"
	"time"
)

// ClusterAgentsService handles communication with the cluster agents related
// methods of the GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/cluster_agents.html
type ClusterAgentsService struct {
	client *Client
}

// Agent represents a GitLab agent for Kubernetes.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/cluster_agents.html
type Agent struct {
	ID              int           `json:"id"`
	Name            string        `json:"name"`
	CreatedAt       *time.Time    `json:"created_at"`
	CreatedByUserID int           `json:"created_by_user_id"`
	ConfigProject   ConfigProject `json:"config_project"`
}

type ConfigProject struct {
	ID                int        `json:"id"`
	Description       string     `json:"description"`
	Name              string     `json:"name"`
	NameWithNamespace string     `json:"name_with_namespace"`
	Path              string     `json:"path"`
	PathWithNamespace string     `json:"path_with_namespace"`
	CreatedAt         *time.Time `json:"created_at"`
}

func (a Agent) String() string {
	return Stringify(a)
}

// AgentToken represents a GitLab agent token.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/cluster_agents.html#list-tokens-for-an-agent
type AgentToken struct {
	ID              int        `json:"id"`
	Name            string     `json:"name"`
	Description     string     `json:"description"`
	AgentID         int        `json:"agent_id"`
	Status          string     `json:"status"`
	CreatedAt       *time.Time `json:"created_at"`
	CreatedByUserID int        `json:"created_by_user_id"`
	LastUsedAt      *time.Time `json:"last_used_at"`
	Token           string     `json:"token"`
}

func (a AgentToken) String() string {
	return Stringify(a)
}

// ListAgentsOptions represents the available ListAgents() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/cluster_agents.html#list-the-agents-for-a-project
type ListAgentsOptions ListOptions

// ListAgents returns a list of agents registered for the project.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/cluster_agents.html#list-the-agents-for-a-project
func (s *ClusterAgentsService) ListAgents(pid interface{}, opt *ListAgentsOptions, options ...RequestOptionFunc) ([]*Agent, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	uri := fmt.Sprintf("projects/%s/cluster_agents", PathEscape(project))

	req, err := s.client.NewRequest(http.MethodGet, uri, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var as []*Agent
	resp, err := s.client.Do(req, &as)
	if err != nil {
		return nil, resp, err
	}

	return as, resp, nil
}

// GetAgent gets a single agent details.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/cluster_agents.html#get-details-about-an-agent
func (s *ClusterAgentsService) GetAgent(pid interface{}, id int, options ...RequestOptionFunc) (*Agent, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	uri := fmt.Sprintf("projects/%s/cluster_agents/%d", PathEscape(project), id)

	req, err := s.client.NewRequest(http.MethodGet, uri, nil, options)
	if err != nil {
		return nil, nil, err
	}

	a := new(Agent)
	resp, err := s.client.Do(req, a)
	if err != nil {
		return nil, resp, err
	}

	return a, resp, nil
}

// RegisterAgentOptions represents the available RegisterAgent()
// options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/cluster_agents.html#register-an-agent-with-a-project
type RegisterAgentOptions struct {
	Name *string `url:"name,omitempty" json:"name,omitempty"`
}

// RegisterAgent registers an agent to the project.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/cluster_agents.html#register-an-agent-with-a-project
func (s *ClusterAgentsService) RegisterAgent(pid interface{}, opt *RegisterAgentOptions, options ...RequestOptionFunc) (*Agent, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	uri := fmt.Sprintf("projects/%s/cluster_agents", PathEscape(project))

	req, err := s.client.NewRequest(http.MethodPost, uri, opt, options)
	if err != nil {
		return nil, nil, err
	}

	a := new(Agent)
	resp, err := s.client.Do(req, a)
	if err != nil {
		return nil, resp, err
	}

	return a, resp, nil
}

// DeleteAgent deletes an existing agent registration.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/cluster_agents.html#delete-a-registered-agent
func (s *ClusterAgentsService) DeleteAgent(pid interface{}, id int, options ...RequestOptionFunc) (*Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, err
	}
	uri := fmt.Sprintf("projects/%s/cluster_agents/%d", PathEscape(project), id)

	req, err := s.client.NewRequest(http.MethodDelete, uri, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}

// ListAgentTokensOptions represents the available ListAgentTokens() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/cluster_agents.html#list-tokens-for-an-agent
type ListAgentTokensOptions ListOptions

// ListAgentTokens returns a list of tokens for an agent.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/cluster_agents.html#list-tokens-for-an-agent
func (s *ClusterAgentsService) ListAgentTokens(pid interface{}, aid int, opt *ListAgentTokensOptions, options ...RequestOptionFunc) ([]*AgentToken, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	uri := fmt.Sprintf("projects/%s/cluster_agents/%d/tokens", PathEscape(project), aid)

	req, err := s.client.NewRequest(http.MethodGet, uri, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var ats []*AgentToken
	resp, err := s.client.Do(req, &ats)
	if err != nil {
		return nil, resp, err
	}

	return ats, resp, nil
}

// GetAgentToken gets a single agent token.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/cluster_agents.html#get-a-single-agent-token
func (s *ClusterAgentsService) GetAgentToken(pid interface{}, aid int, id int, options ...RequestOptionFunc) (*AgentToken, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	uri := fmt.Sprintf("projects/%s/cluster_agents/%d/tokens/%d", PathEscape(project), aid, id)

	req, err := s.client.NewRequest(http.MethodGet, uri, nil, options)
	if err != nil {
		return nil, nil, err
	}

	at := new(AgentToken)
	resp, err := s.client.Do(req, at)
	if err != nil {
		return nil, resp, err
	}

	return at, resp, nil
}

// CreateAgentTokenOptions represents the available CreateAgentToken() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/cluster_agents.html#create-an-agent-token
type CreateAgentTokenOptions struct {
	Name        *string `url:"name,omitempty" json:"name,omitempty"`
	Description *string `url:"description,omitempty" json:"description,omitempty"`
}

// CreateAgentToken creates a new token for an agent.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/cluster_agents.html#create-an-agent-token
func (s *ClusterAgentsService) CreateAgentToken(pid interface{}, aid int, opt *CreateAgentTokenOptions, options ...RequestOptionFunc) (*AgentToken, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	uri := fmt.Sprintf("projects/%s/cluster_agents/%d/tokens", PathEscape(project), aid)

	req, err := s.client.NewRequest(http.MethodPost, uri, opt, options)
	if err != nil {
		return nil, nil, err
	}

	at := new(AgentToken)
	resp, err := s.client.Do(req, at)
	if err != nil {
		return nil, resp, err
	}

	return at, resp, nil
}

// RevokeAgentToken revokes an agent token.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/cluster_agents.html#revoke-an-agent-token
func (s *ClusterAgentsService) RevokeAgentToken(pid interface{}, aid int, id int, options ...RequestOptionFunc) (*Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, err
	}
	uri := fmt.Sprintf("projects/%s/cluster_agents/%d/tokens/%d", PathEscape(project), aid, id)

	req, err := s.client.NewRequest(http.MethodDelete, uri, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/cluster_agents_test.go000066400000000000000000000227541475761473200255010ustar00rootroot00000000000000//
// Copyright 2022, Timo Furrer 
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"fmt"
	"net/http"
	"reflect"
	"testing"
	"time"
)

func ListClusterAgents(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/20/cluster_agents", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprint(w, `
		[
			{
			  "id": 1,
			  "name": "agent-1",
			  "config_project": {
				"id": 20,
				"description": "",
				"name": "test",
				"name_with_namespace": "Administrator / test",
				"path": "test",
				"path_with_namespace": "root/test",
				"created_at": "2022-03-20T20:42:40.221Z"
			  },
			  "created_at": "2022-04-20T20:42:40.221Z",
			  "created_by_user_id": 42
			},
			{
			  "id": 2,
			  "name": "agent-2",
			  "config_project": {
				"id": 20,
				"description": "",
				"name": "test",
				"name_with_namespace": "Administrator / test",
				"path": "test",
				"path_with_namespace": "root/test",
				"created_at": "2022-03-20T20:42:40.221Z"
			  },
			  "created_at": "2022-04-20T20:42:40.221Z",
			  "created_by_user_id": 42
			}
		  ]
		`)
	})

	opt := &ListAgentsOptions{}
	clusterAgents, _, err := client.ClusterAgents.ListAgents(20, opt)
	if err != nil {
		t.Errorf("ClusterAgents.ListClusterAgents returned error: %v", err)
	}

	want := []*Agent{
		{
			ID:   1,
			Name: "agent-1",
			ConfigProject: ConfigProject{
				ID:                20,
				Description:       "",
				Name:              "test",
				NameWithNamespace: "Administrator / test",
				Path:              "test",
				PathWithNamespace: "root/test",
				CreatedAt:         Ptr(time.Date(2022, time.March, 20, 20, 42, 40, 221000000, time.UTC)),
			},
			CreatedAt:       Ptr(time.Date(2022, time.April, 20, 20, 42, 40, 221000000, time.UTC)),
			CreatedByUserID: 42,
		},
		{
			ID:   2,
			Name: "agent-2",
			ConfigProject: ConfigProject{
				ID:                20,
				Description:       "",
				Name:              "test",
				NameWithNamespace: "Administrator / test",
				Path:              "test",
				PathWithNamespace: "root/test",
				CreatedAt:         Ptr(time.Date(2022, time.March, 20, 20, 42, 40, 221000000, time.UTC)),
			},
			CreatedAt:       Ptr(time.Date(2022, time.April, 20, 20, 42, 40, 221000000, time.UTC)),
			CreatedByUserID: 42,
		},
	}

	if !reflect.DeepEqual(want, clusterAgents) {
		t.Errorf("ClusterAgents.ListClusterAgents returned %+v, want %+v", clusterAgents, want)
	}
}

func GetClusterAgent(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/20/cluster_agents/1", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprint(w, `
			{
				"id": 1,
				"name": "agent-1",
				"config_project": {
				"id": 20,
				"description": "",
				"name": "test",
				"name_with_namespace": "Administrator / test",
				"path": "test",
				"path_with_namespace": "root/test",
				"created_at": "2022-03-20T20:42:40.221Z"
				},
				"created_at": "2022-04-20T20:42:40.221Z",
				"created_by_user_id": 42
			}
    	`)
	})

	clusterAgent, _, err := client.ClusterAgents.GetAgent(20, 1)
	if err != nil {
		t.Errorf("ClusterAgents.GetClusterAgent returned error: %v", err)
	}

	want := &Agent{
		ID:   1,
		Name: "agent-1",
		ConfigProject: ConfigProject{
			ID:                20,
			Description:       "",
			Name:              "test",
			NameWithNamespace: "Administrator / test",
			Path:              "test",
			PathWithNamespace: "root/test",
			CreatedAt:         Ptr(time.Date(2022, time.March, 20, 20, 42, 40, 221000000, time.UTC)),
		},
		CreatedAt:       Ptr(time.Date(2022, time.April, 20, 20, 42, 40, 221000000, time.UTC)),
		CreatedByUserID: 42,
	}
	if !reflect.DeepEqual(want, clusterAgent) {
		t.Errorf("ClusterAgents.GetClusterAgent returned %+v, want %+v", clusterAgent, want)
	}
}

func RegisterClusterAgent(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/20/cluster_agents", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		fmt.Fprint(w, `
			{
				"id": 1,
				"name": "agent-1",
				"config_project": {
				  "id": 20,
				  "description": "",
				  "name": "test",
				  "name_with_namespace": "Administrator / test",
				  "path": "test",
				  "path_with_namespace": "root/test",
				  "created_at": "2022-03-20T20:42:40.221Z"
				},
				"created_at": "2022-04-20T20:42:40.221Z",
				"created_by_user_id": 42
			  }
    	`)
	})

	opt := &RegisterAgentOptions{Name: Ptr("agent-1")}
	clusterAgent, _, err := client.ClusterAgents.RegisterAgent(20, opt)
	if err != nil {
		t.Errorf("ClusterAgents.RegisterClusterAgent returned error: %v", err)
	}

	want := &Agent{
		ID:   1,
		Name: "agent-1",
		ConfigProject: ConfigProject{
			ID:                20,
			Description:       "",
			Name:              "test",
			NameWithNamespace: "Administrator / test",
			Path:              "test",
			PathWithNamespace: "root/test",
			CreatedAt:         Ptr(time.Date(2022, time.March, 20, 20, 42, 40, 221000000, time.UTC)),
		},
		CreatedAt:       Ptr(time.Date(2022, time.April, 20, 20, 42, 40, 221000000, time.UTC)),
		CreatedByUserID: 42,
	}
	if !reflect.DeepEqual(want, clusterAgent) {
		t.Errorf("ClusterAgents.RegisterClusterAgent returned %+v, want %+v", clusterAgent, want)
	}
}

func ListAgentTokens(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/20/cluster_agents/5/tokens", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprint(w, `
		[
			{
			  "id": 1,
			  "name": "abcd",
			  "description": "Some token",
			  "agent_id": 5,
			  "status": "active",
			  "created_at": "2022-03-25T14:12:11.497Z",
			  "created_by_user_id": 1
			},
			{
			  "id": 2,
			  "name": "foobar",
			  "description": null,
			  "agent_id": 5,
			  "status": "active",
			  "created_at": "2022-03-25T14:12:11.497Z",
			  "created_by_user_id": 1
			}
		]
		`)
	})

	opt := &ListAgentTokensOptions{}
	clusterAgentTokens, _, err := client.ClusterAgents.ListAgentTokens(20, 5, opt)
	if err != nil {
		t.Errorf("ClusterAgents.ListAgentTokens returned error: %v", err)
	}

	want := []*AgentToken{
		{
			ID:              1,
			Name:            "abcd",
			Description:     "Some token",
			AgentID:         5,
			Status:          "active",
			CreatedAt:       Ptr(time.Date(2022, time.March, 25, 14, 12, 11, 497000000, time.UTC)),
			CreatedByUserID: 1,
		},
		{
			ID:              2,
			Name:            "foobar",
			Description:     "",
			AgentID:         5,
			Status:          "active",
			CreatedAt:       Ptr(time.Date(2022, time.March, 25, 14, 12, 11, 497000000, time.UTC)),
			CreatedByUserID: 1,
		},
	}

	if !reflect.DeepEqual(want, clusterAgentTokens) {
		t.Errorf("ClusterAgents.ListAgentTokens returned %+v, want %+v", clusterAgentTokens, want)
	}
}

func GetAgentToken(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/20/cluster_agents/5/tokens/1", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprint(w, `
		{
			"id": 1,
			"name": "abcd",
			"description": "Some token",
			"agent_id": 5,
			"status": "active",
			"created_at": "2022-03-25T14:12:11.497Z",
			"created_by_user_id": 1,
			"last_used_at": null
		 }
    	`)
	})

	clusterAgentToken, _, err := client.ClusterAgents.GetAgentToken(20, 5, 1)
	if err != nil {
		t.Errorf("ClusterAgents.GetAgentToken returned error: %v", err)
	}

	want := &AgentToken{
		ID:              1,
		Name:            "abcd",
		Description:     "Some token",
		AgentID:         5,
		Status:          "active",
		CreatedAt:       Ptr(time.Date(2022, time.March, 25, 14, 12, 11, 497000000, time.UTC)),
		CreatedByUserID: 1,
		LastUsedAt:      nil,
	}
	if !reflect.DeepEqual(want, clusterAgentToken) {
		t.Errorf("ClusterAgents.GetAgentToken returned %+v, want %+v", clusterAgentToken, want)
	}
}

func RegisterAgentToken(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/20/cluster_agents/5/tokens", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		fmt.Fprint(w, `
		{
			"id": 1,
			"name": "abcd",
			"description": "Some token",
			"agent_id": 5,
			"status": "active",
			"created_at": "2022-03-25T14:12:11.497Z",
			"created_by_user_id": 1,
			"last_used_at": null,
			"token": "qeY8UVRisx9y3Loxo1scLxFuRxYcgeX3sxsdrpP_fR3Loq4xyg"
		}
    	`)
	})

	opt := &CreateAgentTokenOptions{Name: Ptr("abcd"), Description: Ptr("Some token")}
	clusterAgentToken, _, err := client.ClusterAgents.CreateAgentToken(20, 5, opt)
	if err != nil {
		t.Errorf("ClusterAgents.CreateAgentToken returned error: %v", err)
	}

	want := &AgentToken{
		ID:              1,
		Name:            "abcd",
		Description:     "Some token",
		AgentID:         5,
		Status:          "active",
		CreatedAt:       Ptr(time.Date(2022, time.March, 25, 14, 12, 11, 497000000, time.UTC)),
		CreatedByUserID: 1,
		LastUsedAt:      nil,
		Token:           "qeY8UVRisx9y3Loxo1scLxFuRxYcgeX3sxsdrpP_fR3Loq4xyg",
	}
	if !reflect.DeepEqual(want, clusterAgentToken) {
		t.Errorf("ClusterAgents.CreateAgentToken returned %+v, want %+v", clusterAgentToken, want)
	}
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/commits.go000066400000000000000000000512441475761473200230670ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"fmt"
	"net/http"
	"net/url"
	"time"
)

// CommitsService handles communication with the commit related methods
// of the GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/commits.html
type CommitsService struct {
	client *Client
}

// Commit represents a GitLab commit.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/commits.html
type Commit struct {
	ID               string            `json:"id"`
	ShortID          string            `json:"short_id"`
	Title            string            `json:"title"`
	AuthorName       string            `json:"author_name"`
	AuthorEmail      string            `json:"author_email"`
	AuthoredDate     *time.Time        `json:"authored_date"`
	CommitterName    string            `json:"committer_name"`
	CommitterEmail   string            `json:"committer_email"`
	CommittedDate    *time.Time        `json:"committed_date"`
	CreatedAt        *time.Time        `json:"created_at"`
	Message          string            `json:"message"`
	ParentIDs        []string          `json:"parent_ids"`
	Stats            *CommitStats      `json:"stats"`
	Status           *BuildStateValue  `json:"status"`
	LastPipeline     *PipelineInfo     `json:"last_pipeline"`
	ProjectID        int               `json:"project_id"`
	Trailers         map[string]string `json:"trailers"`
	ExtendedTrailers map[string]string `json:"extended_trailers"`
	WebURL           string            `json:"web_url"`
}

// CommitStats represents the number of added and deleted files in a commit.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/commits.html
type CommitStats struct {
	Additions int `json:"additions"`
	Deletions int `json:"deletions"`
	Total     int `json:"total"`
}

func (c Commit) String() string {
	return Stringify(c)
}

// ListCommitsOptions represents the available ListCommits() options.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/commits.html#list-repository-commits
type ListCommitsOptions struct {
	ListOptions
	RefName     *string    `url:"ref_name,omitempty" json:"ref_name,omitempty"`
	Since       *time.Time `url:"since,omitempty" json:"since,omitempty"`
	Until       *time.Time `url:"until,omitempty" json:"until,omitempty"`
	Path        *string    `url:"path,omitempty" json:"path,omitempty"`
	Author      *string    `url:"author,omitempty" json:"author,omitempty"`
	All         *bool      `url:"all,omitempty" json:"all,omitempty"`
	WithStats   *bool      `url:"with_stats,omitempty" json:"with_stats,omitempty"`
	FirstParent *bool      `url:"first_parent,omitempty" json:"first_parent,omitempty"`
	Trailers    *bool      `url:"trailers,omitempty" json:"trailers,omitempty"`
}

// ListCommits gets a list of repository commits in a project.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/commits.html#list-repository-commits
func (s *CommitsService) ListCommits(pid interface{}, opt *ListCommitsOptions, options ...RequestOptionFunc) ([]*Commit, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/repository/commits", PathEscape(project))

	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var c []*Commit
	resp, err := s.client.Do(req, &c)
	if err != nil {
		return nil, resp, err
	}

	return c, resp, nil
}

// CommitRef represents the reference of branches/tags in a commit.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/commits.html#get-references-a-commit-is-pushed-to
type CommitRef struct {
	Type string `json:"type"`
	Name string `json:"name"`
}

// GetCommitRefsOptions represents the available GetCommitRefs() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/commits.html#get-references-a-commit-is-pushed-to
type GetCommitRefsOptions struct {
	ListOptions
	Type *string `url:"type,omitempty" json:"type,omitempty"`
}

// GetCommitRefs gets all references (from branches or tags) a commit is pushed to
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/commits.html#get-references-a-commit-is-pushed-to
func (s *CommitsService) GetCommitRefs(pid interface{}, sha string, opt *GetCommitRefsOptions, options ...RequestOptionFunc) ([]*CommitRef, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/repository/commits/%s/refs", PathEscape(project), url.PathEscape(sha))

	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var cs []*CommitRef
	resp, err := s.client.Do(req, &cs)
	if err != nil {
		return nil, resp, err
	}

	return cs, resp, nil
}

// GetCommitOptions represents the available GetCommit() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/commits.html#get-a-single-commit
type GetCommitOptions struct {
	Stats *bool `url:"stats,omitempty" json:"stats,omitempty"`
}

// GetCommit gets a specific commit identified by the commit hash or name of a
// branch or tag.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/commits.html#get-a-single-commit
func (s *CommitsService) GetCommit(pid interface{}, sha string, opt *GetCommitOptions, options ...RequestOptionFunc) (*Commit, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	if sha == "" {
		return nil, nil, fmt.Errorf("SHA must be a non-empty string")
	}
	u := fmt.Sprintf("projects/%s/repository/commits/%s", PathEscape(project), url.PathEscape(sha))

	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	c := new(Commit)
	resp, err := s.client.Do(req, c)
	if err != nil {
		return nil, resp, err
	}

	return c, resp, nil
}

// CreateCommitOptions represents the available options for a new commit.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/commits.html#create-a-commit-with-multiple-files-and-actions
type CreateCommitOptions struct {
	Branch        *string                `url:"branch,omitempty" json:"branch,omitempty"`
	CommitMessage *string                `url:"commit_message,omitempty" json:"commit_message,omitempty"`
	StartBranch   *string                `url:"start_branch,omitempty" json:"start_branch,omitempty"`
	StartSHA      *string                `url:"start_sha,omitempty" json:"start_sha,omitempty"`
	StartProject  *string                `url:"start_project,omitempty" json:"start_project,omitempty"`
	Actions       []*CommitActionOptions `url:"actions" json:"actions"`
	AuthorEmail   *string                `url:"author_email,omitempty" json:"author_email,omitempty"`
	AuthorName    *string                `url:"author_name,omitempty" json:"author_name,omitempty"`
	Stats         *bool                  `url:"stats,omitempty" json:"stats,omitempty"`
	Force         *bool                  `url:"force,omitempty" json:"force,omitempty"`
}

// CommitActionOptions represents the available options for a new single
// file action.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/commits.html#create-a-commit-with-multiple-files-and-actions
type CommitActionOptions struct {
	Action          *FileActionValue `url:"action,omitempty" json:"action,omitempty"`
	FilePath        *string          `url:"file_path,omitempty" json:"file_path,omitempty"`
	PreviousPath    *string          `url:"previous_path,omitempty" json:"previous_path,omitempty"`
	Content         *string          `url:"content,omitempty" json:"content,omitempty"`
	Encoding        *string          `url:"encoding,omitempty" json:"encoding,omitempty"`
	LastCommitID    *string          `url:"last_commit_id,omitempty" json:"last_commit_id,omitempty"`
	ExecuteFilemode *bool            `url:"execute_filemode,omitempty" json:"execute_filemode,omitempty"`
}

// CreateCommit creates a commit with multiple files and actions.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/commits.html#create-a-commit-with-multiple-files-and-actions
func (s *CommitsService) CreateCommit(pid interface{}, opt *CreateCommitOptions, options ...RequestOptionFunc) (*Commit, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/repository/commits", PathEscape(project))

	req, err := s.client.NewRequest(http.MethodPost, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	c := new(Commit)
	resp, err := s.client.Do(req, &c)
	if err != nil {
		return nil, resp, err
	}

	return c, resp, nil
}

// Diff represents a GitLab diff.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/commits.html
type Diff struct {
	Diff        string `json:"diff"`
	NewPath     string `json:"new_path"`
	OldPath     string `json:"old_path"`
	AMode       string `json:"a_mode"`
	BMode       string `json:"b_mode"`
	NewFile     bool   `json:"new_file"`
	RenamedFile bool   `json:"renamed_file"`
	DeletedFile bool   `json:"deleted_file"`
}

func (d Diff) String() string {
	return Stringify(d)
}

// GetCommitDiffOptions represents the available GetCommitDiff() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/commits.html#get-the-diff-of-a-commit
type GetCommitDiffOptions struct {
	ListOptions
	Unidiff *bool `url:"unidiff,omitempty" json:"unidiff,omitempty"`
}

// GetCommitDiff gets the diff of a commit in a project..
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/commits.html#get-the-diff-of-a-commit
func (s *CommitsService) GetCommitDiff(pid interface{}, sha string, opt *GetCommitDiffOptions, options ...RequestOptionFunc) ([]*Diff, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/repository/commits/%s/diff", PathEscape(project), url.PathEscape(sha))

	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var d []*Diff
	resp, err := s.client.Do(req, &d)
	if err != nil {
		return nil, resp, err
	}

	return d, resp, nil
}

// CommitComment represents a GitLab commit comment.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/commits.html
type CommitComment struct {
	Note     string `json:"note"`
	Path     string `json:"path"`
	Line     int    `json:"line"`
	LineType string `json:"line_type"`
	Author   Author `json:"author"`
}

// Author represents a GitLab commit author
type Author struct {
	ID        int        `json:"id"`
	Username  string     `json:"username"`
	Email     string     `json:"email"`
	Name      string     `json:"name"`
	State     string     `json:"state"`
	Blocked   bool       `json:"blocked"`
	CreatedAt *time.Time `json:"created_at"`
}

func (c CommitComment) String() string {
	return Stringify(c)
}

// GetCommitCommentsOptions represents the available GetCommitComments() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/commits.html#get-the-comments-of-a-commit
type GetCommitCommentsOptions ListOptions

// GetCommitComments gets the comments of a commit in a project.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/commits.html#get-the-comments-of-a-commit
func (s *CommitsService) GetCommitComments(pid interface{}, sha string, opt *GetCommitCommentsOptions, options ...RequestOptionFunc) ([]*CommitComment, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/repository/commits/%s/comments", PathEscape(project), url.PathEscape(sha))

	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var c []*CommitComment
	resp, err := s.client.Do(req, &c)
	if err != nil {
		return nil, resp, err
	}

	return c, resp, nil
}

// PostCommitCommentOptions represents the available PostCommitComment()
// options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/commits.html#post-comment-to-commit
type PostCommitCommentOptions struct {
	Note     *string `url:"note,omitempty" json:"note,omitempty"`
	Path     *string `url:"path" json:"path"`
	Line     *int    `url:"line" json:"line"`
	LineType *string `url:"line_type" json:"line_type"`
}

// PostCommitComment adds a comment to a commit. Optionally you can post
// comments on a specific line of a commit. Therefor both path, line_new and
// line_old are required.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/commits.html#post-comment-to-commit
func (s *CommitsService) PostCommitComment(pid interface{}, sha string, opt *PostCommitCommentOptions, options ...RequestOptionFunc) (*CommitComment, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/repository/commits/%s/comments", PathEscape(project), url.PathEscape(sha))

	req, err := s.client.NewRequest(http.MethodPost, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	c := new(CommitComment)
	resp, err := s.client.Do(req, c)
	if err != nil {
		return nil, resp, err
	}

	return c, resp, nil
}

// GetCommitStatusesOptions represents the available GetCommitStatuses() options.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/commits.html#list-the-statuses-of-a-commit
type GetCommitStatusesOptions struct {
	ListOptions
	Ref   *string `url:"ref,omitempty" json:"ref,omitempty"`
	Stage *string `url:"stage,omitempty" json:"stage,omitempty"`
	Name  *string `url:"name,omitempty" json:"name,omitempty"`
	All   *bool   `url:"all,omitempty" json:"all,omitempty"`
}

// CommitStatus represents a GitLab commit status.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/commits.html#commit-status
type CommitStatus struct {
	ID           int        `json:"id"`
	SHA          string     `json:"sha"`
	Ref          string     `json:"ref"`
	Status       string     `json:"status"`
	CreatedAt    *time.Time `json:"created_at"`
	StartedAt    *time.Time `json:"started_at"`
	FinishedAt   *time.Time `json:"finished_at"`
	Name         string     `json:"name"`
	AllowFailure bool       `json:"allow_failure"`
	Coverage     float64    `json:"coverage"`
	PipelineId   int        `json:"pipeline_id"`
	Author       Author     `json:"author"`
	Description  string     `json:"description"`
	TargetURL    string     `json:"target_url"`
}

// GetCommitStatuses gets the statuses of a commit in a project.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/commits.html#list-the-statuses-of-a-commit
func (s *CommitsService) GetCommitStatuses(pid interface{}, sha string, opt *GetCommitStatusesOptions, options ...RequestOptionFunc) ([]*CommitStatus, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/repository/commits/%s/statuses", PathEscape(project), url.PathEscape(sha))

	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var cs []*CommitStatus
	resp, err := s.client.Do(req, &cs)
	if err != nil {
		return nil, resp, err
	}

	return cs, resp, nil
}

// SetCommitStatusOptions represents the available SetCommitStatus() options.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/commits.html#set-the-pipeline-status-of-a-commit
type SetCommitStatusOptions struct {
	State       BuildStateValue `url:"state" json:"state"`
	Ref         *string         `url:"ref,omitempty" json:"ref,omitempty"`
	Name        *string         `url:"name,omitempty" json:"name,omitempty"`
	Context     *string         `url:"context,omitempty" json:"context,omitempty"`
	TargetURL   *string         `url:"target_url,omitempty" json:"target_url,omitempty"`
	Description *string         `url:"description,omitempty" json:"description,omitempty"`
	Coverage    *float64        `url:"coverage,omitempty" json:"coverage,omitempty"`
	PipelineID  *int            `url:"pipeline_id,omitempty" json:"pipeline_id,omitempty"`
}

// SetCommitStatus sets the status of a commit in a project.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/commits.html#set-the-pipeline-status-of-a-commit
func (s *CommitsService) SetCommitStatus(pid interface{}, sha string, opt *SetCommitStatusOptions, options ...RequestOptionFunc) (*CommitStatus, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/statuses/%s", PathEscape(project), url.PathEscape(sha))

	req, err := s.client.NewRequest(http.MethodPost, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	cs := new(CommitStatus)
	resp, err := s.client.Do(req, &cs)
	if err != nil {
		return nil, resp, err
	}

	return cs, resp, nil
}

// ListMergeRequestsByCommit gets merge request associated with a commit.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/commits.html#list-merge-requests-associated-with-a-commit
func (s *CommitsService) ListMergeRequestsByCommit(pid interface{}, sha string, options ...RequestOptionFunc) ([]*MergeRequest, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/repository/commits/%s/merge_requests", PathEscape(project), url.PathEscape(sha))

	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	var mrs []*MergeRequest
	resp, err := s.client.Do(req, &mrs)
	if err != nil {
		return nil, resp, err
	}

	return mrs, resp, nil
}

// CherryPickCommitOptions represents the available CherryPickCommit() options.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/commits.html#cherry-pick-a-commit
type CherryPickCommitOptions struct {
	Branch  *string `url:"branch,omitempty" json:"branch,omitempty"`
	DryRun  *bool   `url:"dry_run,omitempty" json:"dry_run,omitempty"`
	Message *string `url:"message,omitempty" json:"message,omitempty"`
}

// CherryPickCommit cherry picks a commit to a given branch.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/commits.html#cherry-pick-a-commit
func (s *CommitsService) CherryPickCommit(pid interface{}, sha string, opt *CherryPickCommitOptions, options ...RequestOptionFunc) (*Commit, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/repository/commits/%s/cherry_pick", PathEscape(project), url.PathEscape(sha))

	req, err := s.client.NewRequest(http.MethodPost, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	c := new(Commit)
	resp, err := s.client.Do(req, &c)
	if err != nil {
		return nil, resp, err
	}

	return c, resp, nil
}

// RevertCommitOptions represents the available RevertCommit() options.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/commits.html#revert-a-commit
type RevertCommitOptions struct {
	Branch *string `url:"branch,omitempty" json:"branch,omitempty"`
}

// RevertCommit reverts a commit in a given branch.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/commits.html#revert-a-commit
func (s *CommitsService) RevertCommit(pid interface{}, sha string, opt *RevertCommitOptions, options ...RequestOptionFunc) (*Commit, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/repository/commits/%s/revert", PathEscape(project), url.PathEscape(sha))

	req, err := s.client.NewRequest(http.MethodPost, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	c := new(Commit)
	resp, err := s.client.Do(req, &c)
	if err != nil {
		return nil, resp, err
	}

	return c, resp, nil
}

// GPGSignature represents a Gitlab commit's GPG Signature.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/commits.html#get-gpg-signature-of-a-commit
type GPGSignature struct {
	KeyID              int    `json:"gpg_key_id"`
	KeyPrimaryKeyID    string `json:"gpg_key_primary_keyid"`
	KeyUserName        string `json:"gpg_key_user_name"`
	KeyUserEmail       string `json:"gpg_key_user_email"`
	VerificationStatus string `json:"verification_status"`
	KeySubkeyID        int    `json:"gpg_key_subkey_id"`
}

// GetGPGSignature gets a GPG signature of a commit.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/commits.html#get-gpg-signature-of-a-commit
func (s *CommitsService) GetGPGSignature(pid interface{}, sha string, options ...RequestOptionFunc) (*GPGSignature, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/repository/commits/%s/signature", PathEscape(project), url.PathEscape(sha))

	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	sig := new(GPGSignature)
	resp, err := s.client.Do(req, &sig)
	if err != nil {
		return nil, resp, err
	}

	return sig, resp, nil
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/commits_test.go000066400000000000000000000723471475761473200241350ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"reflect"
	"testing"
	"time"

	"github.com/stretchr/testify/require"

	"github.com/stretchr/testify/assert"
)

var testRevertCommitTargetBranch = "release"

func TestGetCommit(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/repository/commits/b0b3a907f41409829b307a28b82fdbd552ee5a27", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		mustWriteHTTPResponse(t, w, "testdata/get_commit.json")
	})

	commit, resp, err := client.Commits.GetCommit("1", "b0b3a907f41409829b307a28b82fdbd552ee5a27", nil)
	if err != nil {
		t.Fatalf("Commits.GetCommit returned error: %v, response: %v", err, resp)
	}

	updatedAt := time.Date(2019, 11, 4, 15, 39, 0o3, 935000000, time.UTC)
	createdAt := time.Date(2019, 11, 4, 15, 38, 53, 154000000, time.UTC)
	want := &Commit{
		ID:             "6104942438c14ec7bd21c6cd5bd995272b3faff6",
		ShortID:        "6104942438c",
		Title:          "Sanitize for network graph",
		AuthorName:     "randx",
		AuthorEmail:    "dmitriy.zaporozhets@gmail.com",
		CommitterName:  "Dmitriy",
		CommitterEmail: "dmitriy.zaporozhets@gmail.com",
		Message:        "Sanitize for network graph",
		ParentIDs:      []string{"ae1d9fb46aa2b07ee9836d49862ec4e2c46fbbba"},
		Stats:          &CommitStats{Additions: 15, Deletions: 10, Total: 25},
		Status:         Ptr(Running),
		LastPipeline: &PipelineInfo{
			ID:        8,
			Ref:       "master",
			SHA:       "2dc6aa325a317eda67812f05600bdf0fcdc70ab0",
			Status:    "created",
			WebURL:    "https://gitlab.com/gitlab-org/gitlab-ce/pipelines/54268416",
			UpdatedAt: &updatedAt,
			CreatedAt: &createdAt,
		},
		ProjectID: 13083,
	}

	assert.Equal(t, want, commit)
}

func TestGetCommitStatuses(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/repository/commits/b0b3a907f41409829b307a28b82fdbd552ee5a27/statuses", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprint(w, `[{"id":1}]`)
	})

	opt := &GetCommitStatusesOptions{
		Ref:   Ptr("master"),
		Stage: Ptr("test"),
		Name:  Ptr("ci/jenkins"),
		All:   Ptr(true),
	}
	statuses, _, err := client.Commits.GetCommitStatuses("1", "b0b3a907f41409829b307a28b82fdbd552ee5a27", opt)
	if err != nil {
		t.Errorf("Commits.GetCommitStatuses returned error: %v", err)
	}

	want := []*CommitStatus{{ID: 1}}
	if !reflect.DeepEqual(want, statuses) {
		t.Errorf("Commits.GetCommitStatuses returned %+v, want %+v", statuses, want)
	}
}

func TestSetCommitStatus(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/statuses/b0b3a907f41409829b307a28b82fdbd552ee5a27", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		body, err := io.ReadAll(r.Body)
		require.NoError(t, err)

		var content SetCommitStatusOptions
		err = json.Unmarshal(body, &content)
		require.NoError(t, err)

		assert.Equal(t, "ci/jenkins", *content.Name)
		assert.Equal(t, 99.9, *content.Coverage)
		fmt.Fprint(w, `{"id":1}`)
	})

	cov := 99.9
	opt := &SetCommitStatusOptions{
		State:       Running,
		Ref:         Ptr("master"),
		Name:        Ptr("ci/jenkins"),
		Context:     Ptr(""),
		TargetURL:   Ptr("http://abc"),
		Description: Ptr("build"),
		Coverage:    &cov,
	}
	status, _, err := client.Commits.SetCommitStatus("1", "b0b3a907f41409829b307a28b82fdbd552ee5a27", opt)
	if err != nil {
		t.Errorf("Commits.SetCommitStatus returned error: %v", err)
	}

	want := &CommitStatus{ID: 1}
	if !reflect.DeepEqual(want, status) {
		t.Errorf("Commits.SetCommitStatus returned %+v, want %+v", status, want)
	}
}

func TestRevertCommit_NoOptions(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/repository/commits/b0b3a907f41409829b307a28b82fdbd552ee5a27/revert", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		mustWriteHTTPResponse(t, w, "testdata/get_commit.json")
	})

	commit, resp, err := client.Commits.RevertCommit("1", "b0b3a907f41409829b307a28b82fdbd552ee5a27", nil)
	if err != nil {
		t.Fatalf("Commits.RevertCommit returned error: %v, response: %v", err, resp)
	}

	updatedAt := time.Date(2019, 11, 4, 15, 39, 0o3, 935000000, time.UTC)
	createdAt := time.Date(2019, 11, 4, 15, 38, 53, 154000000, time.UTC)
	want := &Commit{
		ID:             "6104942438c14ec7bd21c6cd5bd995272b3faff6",
		ShortID:        "6104942438c",
		Title:          "Sanitize for network graph",
		AuthorName:     "randx",
		AuthorEmail:    "dmitriy.zaporozhets@gmail.com",
		CommitterName:  "Dmitriy",
		CommitterEmail: "dmitriy.zaporozhets@gmail.com",
		Message:        "Sanitize for network graph",
		ParentIDs:      []string{"ae1d9fb46aa2b07ee9836d49862ec4e2c46fbbba"},
		Stats:          &CommitStats{Additions: 15, Deletions: 10, Total: 25},
		Status:         Ptr(Running),
		LastPipeline: &PipelineInfo{
			ID:        8,
			Ref:       "master",
			SHA:       "2dc6aa325a317eda67812f05600bdf0fcdc70ab0",
			Status:    "created",
			WebURL:    "https://gitlab.com/gitlab-org/gitlab-ce/pipelines/54268416",
			UpdatedAt: &updatedAt,
			CreatedAt: &createdAt,
		},
		ProjectID: 13083,
	}

	assert.Equal(t, want, commit)
}

func TestRevertCommit_WithOptions(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/repository/commits/b0b3a907f41409829b307a28b82fdbd552ee5a27/revert", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		testBody(t, r, `{"branch":"release"}`)
		mustWriteHTTPResponse(t, w, "testdata/get_commit.json")
	})

	commit, resp, err := client.Commits.RevertCommit("1", "b0b3a907f41409829b307a28b82fdbd552ee5a27", &RevertCommitOptions{
		Branch: &testRevertCommitTargetBranch,
	})
	if err != nil {
		t.Fatalf("Commits.RevertCommit returned error: %v, response: %v", err, resp)
	}

	updatedAt := time.Date(2019, 11, 4, 15, 39, 0o3, 935000000, time.UTC)
	createdAt := time.Date(2019, 11, 4, 15, 38, 53, 154000000, time.UTC)
	want := &Commit{
		ID:             "6104942438c14ec7bd21c6cd5bd995272b3faff6",
		ShortID:        "6104942438c",
		Title:          "Sanitize for network graph",
		AuthorName:     "randx",
		AuthorEmail:    "dmitriy.zaporozhets@gmail.com",
		CommitterName:  "Dmitriy",
		CommitterEmail: "dmitriy.zaporozhets@gmail.com",
		Message:        "Sanitize for network graph",
		ParentIDs:      []string{"ae1d9fb46aa2b07ee9836d49862ec4e2c46fbbba"},
		Stats:          &CommitStats{Additions: 15, Deletions: 10, Total: 25},
		Status:         Ptr(Running),
		LastPipeline: &PipelineInfo{
			ID:        8,
			Ref:       "master",
			SHA:       "2dc6aa325a317eda67812f05600bdf0fcdc70ab0",
			Status:    "created",
			WebURL:    "https://gitlab.com/gitlab-org/gitlab-ce/pipelines/54268416",
			UpdatedAt: &updatedAt,
			CreatedAt: &createdAt,
		},
		ProjectID: 13083,
	}

	assert.Equal(t, want, commit)
}

func TestGetGPGSignature(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/repository/commits/b0b3a907f41409829b307a28b82fdbd552ee5a27/signature", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		mustWriteHTTPResponse(t, w, "testdata/get_signature.json")
	})

	sig, resp, err := client.Commits.GetGPGSignature("1", "b0b3a907f41409829b307a28b82fdbd552ee5a27", nil)
	if err != nil {
		t.Fatalf("Commits.GetGPGSignature returned error: %v, response: %v", err, resp)
	}

	want := &GPGSignature{
		KeyID:              7977,
		KeyPrimaryKeyID:    "627C5F589F467F17",
		KeyUserName:        "Dmitriy Zaporozhets",
		KeyUserEmail:       "dmitriy.zaporozhets@gmail.com",
		VerificationStatus: "verified",
		KeySubkeyID:        0,
	}

	assert.Equal(t, want, sig)
}

func TestCommitsService_ListCommits(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/repository/commits", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
			[
				{
					"id": "6104942438c14ec7bd21c6cd5bd995272b3faff6",
					"short_id": "6104942438c",
					"title": "Sanitize for network graph",
					"author_name": "randx",
					"author_email": "venkateshthalluri123@gmail.com",
					"committer_name": "Venkatesh",
					"committer_email": "venkateshthalluri123@gmail.com",
					"message": "Sanitize for network graph",
					"parent_ids": [
						"ae1d9fb46aa2b07ee9836d49862ec4e2c46fbbba"
					],
					"last_pipeline": {
						"id": 8,
						"ref": "master",
						"sha": "2dc6aa325a317eda67812f05600bdf0fcdc70ab0",
						"status": "created",
						"web_url": "https://gitlab.com/gitlab-org/gitlab-ce/pipelines/54268416",
						"created_at": "2019-11-04T15:38:53.154Z",
						"updated_at": "2019-11-04T15:39:03.935Z"
					},
					"stats": {
						"additions": 15,
						"deletions": 10,
						"total": 25
					},
					"status": "running",
					"project_id": 13083
				}
			]
		`)
	})

	updatedAt := time.Date(2019, 11, 4, 15, 39, 0o3, 935000000, time.UTC)
	createdAt := time.Date(2019, 11, 4, 15, 38, 53, 154000000, time.UTC)
	want := []*Commit{{
		ID:             "6104942438c14ec7bd21c6cd5bd995272b3faff6",
		ShortID:        "6104942438c",
		Title:          "Sanitize for network graph",
		AuthorName:     "randx",
		AuthorEmail:    "venkateshthalluri123@gmail.com",
		CommitterName:  "Venkatesh",
		CommitterEmail: "venkateshthalluri123@gmail.com",
		Message:        "Sanitize for network graph",
		ParentIDs:      []string{"ae1d9fb46aa2b07ee9836d49862ec4e2c46fbbba"},
		Stats:          &CommitStats{Additions: 15, Deletions: 10, Total: 25},
		Status:         Ptr(Running),
		LastPipeline: &PipelineInfo{
			ID:        8,
			Ref:       "master",
			SHA:       "2dc6aa325a317eda67812f05600bdf0fcdc70ab0",
			Status:    "created",
			WebURL:    "https://gitlab.com/gitlab-org/gitlab-ce/pipelines/54268416",
			UpdatedAt: &updatedAt,
			CreatedAt: &createdAt,
		},
		ProjectID: 13083,
	}}

	cs, resp, err := client.Commits.ListCommits(1, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, cs)

	cs, resp, err = client.Commits.ListCommits(1.01, nil)
	require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, cs)

	cs, resp, err = client.Commits.ListCommits(1, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, cs)

	cs, resp, err = client.Commits.ListCommits(3, nil)
	require.Error(t, err)
	require.Nil(t, cs)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestCommitsService_GetCommitRefs(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/repository/commits/5937ac0a7beb003549fc5fd26fc247adbce4a52e/refs", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
			[
			  {"type": "branch", "name": "test"},
			  {"type": "branch", "name": "add-balsamiq-file"},
			  {"type": "branch", "name": "wip"},
			  {"type": "tag", "name": "v1.1.0"}
			 ]
		`)
	})

	want := []*CommitRef{
		{
			Type: "branch",
			Name: "test",
		},
		{
			Type: "branch",
			Name: "add-balsamiq-file",
		},
		{
			Type: "branch",
			Name: "wip",
		},
		{
			Type: "tag",
			Name: "v1.1.0",
		},
	}

	crs, resp, err := client.Commits.GetCommitRefs(1, "5937ac0a7beb003549fc5fd26fc247adbce4a52e", nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, crs)

	crs, resp, err = client.Commits.GetCommitRefs(1.01, "5937ac0a7beb003549fc5fd26fc247adbce4a52e", nil)
	require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, crs)

	crs, resp, err = client.Commits.GetCommitRefs(1, "5937ac0a7beb003549fc5fd26fc247adbce4a52e", nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, crs)

	crs, resp, err = client.Commits.GetCommitRefs(3, "5937ac0a7beb003549fc5fd26fc247adbce4a52e", nil)
	require.Error(t, err)
	require.Nil(t, crs)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestCommitsService_CreateCommit(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/repository/commits", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		fmt.Fprintf(w, `
			{
				"id": "6104942438c14ec7bd21c6cd5bd995272b3faff6",
				"short_id": "6104942438c",
				"title": "Sanitize for network graph",
				"author_name": "randx",
				"author_email": "venkateshthalluri123@gmail.com",
				"committer_name": "Venkatesh",
				"committer_email": "venkateshthalluri123@gmail.com",
				"message": "Sanitize for network graph",
				"parent_ids": [
					"ae1d9fb46aa2b07ee9836d49862ec4e2c46fbbba"
				],
				"last_pipeline": {
					"id": 8,
					"ref": "master",
					"sha": "2dc6aa325a317eda67812f05600bdf0fcdc70ab0",
					"status": "created",
					"web_url": "https://gitlab.com/gitlab-org/gitlab-ce/pipelines/54268416",
					"created_at": "2019-11-04T15:38:53.154Z",
					"updated_at": "2019-11-04T15:39:03.935Z"
				},
				"stats": {
					"additions": 15,
					"deletions": 10,
					"total": 25
				},
				"status": "running",
				"project_id": 13083
			}
		`)
	})

	updatedAt := time.Date(2019, 11, 4, 15, 39, 0o3, 935000000, time.UTC)
	createdAt := time.Date(2019, 11, 4, 15, 38, 53, 154000000, time.UTC)
	want := &Commit{
		ID:             "6104942438c14ec7bd21c6cd5bd995272b3faff6",
		ShortID:        "6104942438c",
		Title:          "Sanitize for network graph",
		AuthorName:     "randx",
		AuthorEmail:    "venkateshthalluri123@gmail.com",
		CommitterName:  "Venkatesh",
		CommitterEmail: "venkateshthalluri123@gmail.com",
		Message:        "Sanitize for network graph",
		ParentIDs:      []string{"ae1d9fb46aa2b07ee9836d49862ec4e2c46fbbba"},
		Stats:          &CommitStats{Additions: 15, Deletions: 10, Total: 25},
		Status:         Ptr(Running),
		LastPipeline: &PipelineInfo{
			ID:        8,
			Ref:       "master",
			SHA:       "2dc6aa325a317eda67812f05600bdf0fcdc70ab0",
			Status:    "created",
			WebURL:    "https://gitlab.com/gitlab-org/gitlab-ce/pipelines/54268416",
			UpdatedAt: &updatedAt,
			CreatedAt: &createdAt,
		},
		ProjectID: 13083,
	}

	c, resp, err := client.Commits.CreateCommit(1, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, c)

	c, resp, err = client.Commits.CreateCommit(1.01, nil)
	require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, c)

	c, resp, err = client.Commits.CreateCommit(1, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, c)

	c, resp, err = client.Commits.CreateCommit(3, nil)
	require.Error(t, err)
	require.Nil(t, c)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestCommitsService_GetCommitDiff(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/repository/commits/master/diff", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
			[
			  {
				"diff": "--- a/doc/update/5.4-to-6.0.md\n+++ b/doc/update/5.4-to-6.0.md\n@@ -71,6 +71,8 @@\n sudo -u git -H bundle exec rake migrate_keys RAILS_ENV=production\n sudo -u git -H bundle exec rake migrate_inline_notes RAILS_ENV=production\n \n+sudo -u git -H bundle exec rake gitlab:assets:compile RAILS_ENV=production\n+\n \n \n ### 6. Update config files",
				"new_path": "doc/update/5.4-to-6.0.md",
				"old_path": "doc/update/5.4-to-6.0.md",
				"a_mode": null,
				"b_mode": "100644",
				"new_file": false,
				"renamed_file": false,
				"deleted_file": false
				}
			]
		`)
	})

	want := []*Diff{{
		Diff:    "--- a/doc/update/5.4-to-6.0.md\n+++ b/doc/update/5.4-to-6.0.md\n@@ -71,6 +71,8 @@\n sudo -u git -H bundle exec rake migrate_keys RAILS_ENV=production\n sudo -u git -H bundle exec rake migrate_inline_notes RAILS_ENV=production\n \n+sudo -u git -H bundle exec rake gitlab:assets:compile RAILS_ENV=production\n+\n \n \n ### 6. Update config files",
		NewPath: "doc/update/5.4-to-6.0.md",
		OldPath: "doc/update/5.4-to-6.0.md",
		BMode:   "100644",
	}}

	ds, resp, err := client.Commits.GetCommitDiff(1, "master", nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, ds)

	ds, resp, err = client.Commits.GetCommitDiff(1.01, "master", nil)
	require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, ds)

	ds, resp, err = client.Commits.GetCommitDiff(1, "master", nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, ds)

	ds, resp, err = client.Commits.GetCommitDiff(3, "master", nil)
	require.Error(t, err)
	require.Nil(t, ds)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestCommitsService_GetCommitComments(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/repository/commits/master/comments", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
			[
			  {
				"note": "this code is really nice",
				"author": {
				  "id": 11,
				  "username": "venky333",
				  "email": "venkateshthalluri123@gmail.com",
				  "name": "Venkatesh Thalluri",
				  "state": "active"
				}
			  }
			]
		`)
	})

	want := []*CommitComment{{
		Note: "this code is really nice",
		Author: Author{
			ID:       11,
			Username: "venky333",
			Email:    "venkateshthalluri123@gmail.com",
			Name:     "Venkatesh Thalluri",
			State:    "active",
		},
	}}

	ccs, resp, err := client.Commits.GetCommitComments(1, "master", nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, ccs)

	ccs, resp, err = client.Commits.GetCommitComments(1.01, "master", nil)
	require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, ccs)

	ccs, resp, err = client.Commits.GetCommitComments(1, "master", nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, ccs)

	ccs, resp, err = client.Commits.GetCommitComments(3, "master", nil)
	require.Error(t, err)
	require.Nil(t, ccs)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestCommitsService_PostCommitComment(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/repository/commits/master/comments", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		fmt.Fprintf(w, `
		  {
			"note": "this code is really nice",
			"author": {
			  "id": 11,
			  "username": "venky333",
			  "email": "venkateshthalluri123@gmail.com",
			  "name": "Venkatesh Thalluri",
			  "state": "active"
			}
		  }
		`)
	})

	want := &CommitComment{
		Note: "this code is really nice",
		Author: Author{
			ID:       11,
			Username: "venky333",
			Email:    "venkateshthalluri123@gmail.com",
			Name:     "Venkatesh Thalluri",
			State:    "active",
		},
	}

	cc, resp, err := client.Commits.PostCommitComment(1, "master", nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, cc)

	cc, resp, err = client.Commits.PostCommitComment(1.01, "master", nil)
	require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, cc)

	cc, resp, err = client.Commits.PostCommitComment(1, "master", nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, cc)

	cc, resp, err = client.Commits.PostCommitComment(3, "master", nil)
	require.Error(t, err)
	require.Nil(t, cc)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestCommitsService_ListMergeRequestsByCommit(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/repository/commits/master/merge_requests", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
			[
			  {
				"id": 1,
				"iid": 1,
				"project_id": 3,
				"title": "test1",
				"description": "fixed login page css paddings",
				"state": "merged",
				"merged_by": {
				  "id": 87854,
				  "name": "Douwe Maan",
				  "username": "DouweM",
				  "state": "active",
				  "avatar_url": "https://gitlab.example.com/uploads/-/system/user/avatar/87854/avatar.png",
				  "web_url": "https://gitlab.com/DouweM"
				},
				"closed_by": null,
				"closed_at": null,
				"target_branch": "master",
				"source_branch": "test1",
				"upvotes": 0,
				"downvotes": 0,
				"author": {
				  "id": 1,
				  "name": "Administrator",
				  "username": "admin",
				  "state": "active",
				  "avatar_url": null,
				  "web_url" : "https://gitlab.example.com/admin"
				},
				"assignee": {
				  "id": 1,
				  "name": "Administrator",
				  "username": "admin",
				  "state": "active",
				  "avatar_url": null,
				  "web_url" : "https://gitlab.example.com/admin"
				},
				"assignees": [{
				  "name": "Venkatesh Thalluri",
				  "username": "venkatesh.thalluri",
				  "id": 12,
				  "state": "active",
				  "avatar_url": "http://www.gravatar.com/avatar/46f6f7dc858ada7be1853f7fb96e81da?s=80&d=identicon",
				  "web_url": "https://gitlab.example.com/axel.block"
				}],
				"reviewers": [{
				  "id": 2,
				  "name": "Sam Bauch",
				  "username": "kenyatta_oconnell",
				  "state": "active",
				  "avatar_url": "https://www.gravatar.com/avatar/956c92487c6f6f7616b536927e22c9a0?s=80&d=identicon",
				  "web_url": "http://gitlab.example.com//kenyatta_oconnell"
				}],
				"source_project_id": 2,
				"target_project_id": 3,
				"draft": false,
				"work_in_progress": false,
				"milestone": {
				  "id": 5,
				  "iid": 1,
				  "project_id": 3,
				  "title": "v2.0",
				  "description": "Assumenda aut placeat expedita exercitationem labore sunt enim earum.",
				  "state": "closed",
				  "web_url": "https://gitlab.example.com/my-group/my-project/milestones/1"
				},
				"merge_when_pipeline_succeeds": true,
				"detailed_merge_status": "mergeable",
				"sha": "8888888888888888888888888888888888888888",
				"merge_commit_sha": null,
				"squash_commit_sha": null,
				"user_notes_count": 1,
				"discussion_locked": null,
				"should_remove_source_branch": true,
				"force_remove_source_branch": false,
				"allow_collaboration": false,
				"allow_maintainer_to_push": false,
				"web_url": "http://gitlab.example.com/my-group/my-project/merge_requests/1",
				"references": {
				  "short": "!1",
				  "relative": "my-group/my-project!1",
				  "full": "my-group/my-project!1"
				},
				"squash": false,
				"task_completion_status":{
				  "count":0,
				  "completed_count":0
				}
			  }
			]
		`)
	})

	want := []*MergeRequest{{
		ID:           1,
		IID:          1,
		TargetBranch: "master",
		SourceBranch: "test1",
		ProjectID:    3,
		Title:        "test1",
		State:        "merged",
		Upvotes:      0,
		Downvotes:    0,
		Author: &BasicUser{
			ID:        1,
			Username:  "admin",
			Name:      "Administrator",
			State:     "active",
			CreatedAt: nil,
			AvatarURL: "",
			WebURL:    "https://gitlab.example.com/admin",
		},
		Assignee: &BasicUser{
			ID: 1, Username: "admin",
			Name:      "Administrator",
			State:     "active",
			AvatarURL: "",
			WebURL:    "https://gitlab.example.com/admin",
		},
		Assignees: []*BasicUser{{
			ID:        12,
			Username:  "venkatesh.thalluri",
			Name:      "Venkatesh Thalluri",
			State:     "active",
			AvatarURL: "http://www.gravatar.com/avatar/46f6f7dc858ada7be1853f7fb96e81da?s=80&d=identicon", WebURL: "https://gitlab.example.com/axel.block",
		}},
		Reviewers: []*BasicUser{{
			ID:        2,
			Username:  "kenyatta_oconnell",
			Name:      "Sam Bauch",
			State:     "active",
			AvatarURL: "https://www.gravatar.com/avatar/956c92487c6f6f7616b536927e22c9a0?s=80&d=identicon", WebURL: "http://gitlab.example.com//kenyatta_oconnell",
		}},
		SourceProjectID: 2,
		TargetProjectID: 3,
		Description:     "fixed login page css paddings",
		WorkInProgress:  false,
		Milestone: &Milestone{
			ID:          5,
			IID:         1,
			ProjectID:   3,
			Title:       "v2.0",
			Description: "Assumenda aut placeat expedita exercitationem labore sunt enim earum.",
			State:       "closed",
			WebURL:      "https://gitlab.example.com/my-group/my-project/milestones/1",
		},
		MergeWhenPipelineSucceeds: true,
		DetailedMergeStatus:       "mergeable",
		MergeError:                "",
		MergedBy: &BasicUser{
			ID:        87854,
			Username:  "DouweM",
			Name:      "Douwe Maan",
			State:     "active",
			AvatarURL: "https://gitlab.example.com/uploads/-/system/user/avatar/87854/avatar.png",
			WebURL:    "https://gitlab.com/DouweM",
		},
		Subscribed:               false,
		SHA:                      "8888888888888888888888888888888888888888",
		MergeCommitSHA:           "",
		SquashCommitSHA:          "",
		UserNotesCount:           1,
		ChangesCount:             "",
		ShouldRemoveSourceBranch: true,
		ForceRemoveSourceBranch:  false,
		AllowCollaboration:       false,
		WebURL:                   "http://gitlab.example.com/my-group/my-project/merge_requests/1",
		References: &IssueReferences{
			Short:    "!1",
			Relative: "my-group/my-project!1",
			Full:     "my-group/my-project!1",
		},
		DiscussionLocked:     false,
		Squash:               false,
		DivergedCommitsCount: 0,
		RebaseInProgress:     false,
		ApprovalsBeforeMerge: 0,
		Reference:            "",
		FirstContribution:    false,
		TaskCompletionStatus: &TasksCompletionStatus{
			Count:          0,
			CompletedCount: 0,
		},
		HasConflicts:                false,
		BlockingDiscussionsResolved: false,
		Overflow:                    false,
	}}

	mrs, resp, err := client.Commits.ListMergeRequestsByCommit(1, "master", nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, mrs)

	mrs, resp, err = client.Commits.ListMergeRequestsByCommit(1.01, "master", nil)
	require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, mrs)

	mrs, resp, err = client.Commits.ListMergeRequestsByCommit(1, "master", nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, mrs)

	mrs, resp, err = client.Commits.ListMergeRequestsByCommit(3, "master", nil)
	require.Error(t, err)
	require.Nil(t, mrs)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestCommitsService_CherryPickCommit(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/repository/commits/master/cherry_pick", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		fmt.Fprintf(w, `
			{
				"id": "6104942438c14ec7bd21c6cd5bd995272b3faff6",
				"short_id": "6104942438c",
				"title": "Sanitize for network graph",
				"author_name": "randx",
				"author_email": "venkateshthalluri123@gmail.com",
				"committer_name": "Venkatesh",
				"committer_email": "venkateshthalluri123@gmail.com",
				"message": "Sanitize for network graph",
				"parent_ids": [
					"ae1d9fb46aa2b07ee9836d49862ec4e2c46fbbba"
				],
				"last_pipeline": {
					"id": 8,
					"ref": "master",
					"sha": "2dc6aa325a317eda67812f05600bdf0fcdc70ab0",
					"status": "created",
					"web_url": "https://gitlab.com/gitlab-org/gitlab-ce/pipelines/54268416",
					"created_at": "2019-11-04T15:38:53.154Z",
					"updated_at": "2019-11-04T15:39:03.935Z"
				},
				"stats": {
					"additions": 15,
					"deletions": 10,
					"total": 25
				},
				"status": "running",
				"project_id": 13083
			}
		`)
	})

	updatedAt := time.Date(2019, 11, 4, 15, 39, 0o3, 935000000, time.UTC)
	createdAt := time.Date(2019, 11, 4, 15, 38, 53, 154000000, time.UTC)
	want := &Commit{
		ID:             "6104942438c14ec7bd21c6cd5bd995272b3faff6",
		ShortID:        "6104942438c",
		Title:          "Sanitize for network graph",
		AuthorName:     "randx",
		AuthorEmail:    "venkateshthalluri123@gmail.com",
		CommitterName:  "Venkatesh",
		CommitterEmail: "venkateshthalluri123@gmail.com",
		Message:        "Sanitize for network graph",
		ParentIDs:      []string{"ae1d9fb46aa2b07ee9836d49862ec4e2c46fbbba"},
		Stats:          &CommitStats{Additions: 15, Deletions: 10, Total: 25},
		Status:         Ptr(Running),
		LastPipeline: &PipelineInfo{
			ID:        8,
			Ref:       "master",
			SHA:       "2dc6aa325a317eda67812f05600bdf0fcdc70ab0",
			Status:    "created",
			WebURL:    "https://gitlab.com/gitlab-org/gitlab-ce/pipelines/54268416",
			UpdatedAt: &updatedAt,
			CreatedAt: &createdAt,
		},
		ProjectID: 13083,
	}

	c, resp, err := client.Commits.CherryPickCommit(1, "master", nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, c)

	c, resp, err = client.Commits.CherryPickCommit(1.01, "master", nil)
	require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, c)

	c, resp, err = client.Commits.CherryPickCommit(1, "master", nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, c)

	c, resp, err = client.Commits.CherryPickCommit(3, "master", nil)
	require.Error(t, err)
	require.Nil(t, c)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/container_registry.go000066400000000000000000000242661475761473200253320ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"fmt"
	"net/http"
	"time"
)

// ContainerRegistryService handles communication with the container registry
// related methods of the GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/container_registry.html
type ContainerRegistryService struct {
	client *Client
}

// RegistryRepository represents a GitLab content registry repository.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/container_registry.html
type RegistryRepository struct {
	ID                     int                      `json:"id"`
	Name                   string                   `json:"name"`
	Path                   string                   `json:"path"`
	ProjectID              int                      `json:"project_id"`
	Location               string                   `json:"location"`
	CreatedAt              *time.Time               `json:"created_at"`
	CleanupPolicyStartedAt *time.Time               `json:"cleanup_policy_started_at"`
	Status                 *ContainerRegistryStatus `json:"status"`
	TagsCount              int                      `json:"tags_count"`
	Tags                   []*RegistryRepositoryTag `json:"tags"`
}

func (s RegistryRepository) String() string {
	return Stringify(s)
}

// RegistryRepositoryTag represents a GitLab registry image tag.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/container_registry.html
type RegistryRepositoryTag struct {
	Name          string     `json:"name"`
	Path          string     `json:"path"`
	Location      string     `json:"location"`
	Revision      string     `json:"revision"`
	ShortRevision string     `json:"short_revision"`
	Digest        string     `json:"digest"`
	CreatedAt     *time.Time `json:"created_at"`
	TotalSize     int        `json:"total_size"`
}

func (s RegistryRepositoryTag) String() string {
	return Stringify(s)
}

// ListRegistryRepositoriesOptions represents the available
// ListRegistryRepositories() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/container_registry.html#list-registry-repositories
type ListRegistryRepositoriesOptions struct {
	ListOptions

	// Deprecated: These options are deprecated for ListGroupRegistryRepositories calls. (Removed in GitLab 15.0)
	Tags      *bool `url:"tags,omitempty" json:"tags,omitempty"`
	TagsCount *bool `url:"tags_count,omitempty" json:"tags_count,omitempty"`
}

// ListProjectRegistryRepositories gets a list of registry repositories in a project.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/container_registry.html#within-a-project
func (s *ContainerRegistryService) ListProjectRegistryRepositories(pid interface{}, opt *ListRegistryRepositoriesOptions, options ...RequestOptionFunc) ([]*RegistryRepository, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/registry/repositories", PathEscape(project))

	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var repos []*RegistryRepository
	resp, err := s.client.Do(req, &repos)
	if err != nil {
		return nil, resp, err
	}

	return repos, resp, nil
}

// ListGroupRegistryRepositories gets a list of registry repositories in a group.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/container_registry.html#within-a-group
func (s *ContainerRegistryService) ListGroupRegistryRepositories(gid interface{}, opt *ListRegistryRepositoriesOptions, options ...RequestOptionFunc) ([]*RegistryRepository, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/registry/repositories", PathEscape(group))

	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var repos []*RegistryRepository
	resp, err := s.client.Do(req, &repos)
	if err != nil {
		return nil, resp, err
	}

	return repos, resp, nil
}

// GetSingleRegistryRepositoryOptions represents the available
// GetSingleRegistryRepository() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/container_registry.html#get-details-of-a-single-repository
type GetSingleRegistryRepositoryOptions struct {
	Tags      *bool `url:"tags,omitempty" json:"tags,omitempty"`
	TagsCount *bool `url:"tags_count,omitempty" json:"tags_count,omitempty"`
}

// GetSingleRegistryRepository gets the details of single registry repository.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/container_registry.html#get-details-of-a-single-repository
func (s *ContainerRegistryService) GetSingleRegistryRepository(pid interface{}, opt *GetSingleRegistryRepositoryOptions, options ...RequestOptionFunc) (*RegistryRepository, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("registry/repositories/%s", PathEscape(project))

	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	repo := new(RegistryRepository)
	resp, err := s.client.Do(req, repo)
	if err != nil {
		return nil, resp, err
	}

	return repo, resp, nil
}

// DeleteRegistryRepository deletes a repository in a registry.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/container_registry.html#delete-registry-repository
func (s *ContainerRegistryService) DeleteRegistryRepository(pid interface{}, repository int, options ...RequestOptionFunc) (*Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("projects/%s/registry/repositories/%d", PathEscape(project), repository)

	req, err := s.client.NewRequest(http.MethodDelete, u, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}

// ListRegistryRepositoryTagsOptions represents the available
// ListRegistryRepositoryTags() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/container_registry.html#list-registry-repository-tags
type ListRegistryRepositoryTagsOptions ListOptions

// ListRegistryRepositoryTags gets a list of tags for given registry repository.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/container_registry.html#list-registry-repository-tags
func (s *ContainerRegistryService) ListRegistryRepositoryTags(pid interface{}, repository int, opt *ListRegistryRepositoryTagsOptions, options ...RequestOptionFunc) ([]*RegistryRepositoryTag, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/registry/repositories/%d/tags",
		PathEscape(project),
		repository,
	)

	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var tags []*RegistryRepositoryTag
	resp, err := s.client.Do(req, &tags)
	if err != nil {
		return nil, resp, err
	}

	return tags, resp, nil
}

// GetRegistryRepositoryTagDetail get details of a registry repository tag
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/container_registry.html#get-details-of-a-registry-repository-tag
func (s *ContainerRegistryService) GetRegistryRepositoryTagDetail(pid interface{}, repository int, tagName string, options ...RequestOptionFunc) (*RegistryRepositoryTag, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/registry/repositories/%d/tags/%s",
		PathEscape(project),
		repository,
		tagName,
	)

	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	tag := new(RegistryRepositoryTag)
	resp, err := s.client.Do(req, &tag)
	if err != nil {
		return nil, resp, err
	}

	return tag, resp, nil
}

// DeleteRegistryRepositoryTag deletes a registry repository tag.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/container_registry.html#delete-a-registry-repository-tag
func (s *ContainerRegistryService) DeleteRegistryRepositoryTag(pid interface{}, repository int, tagName string, options ...RequestOptionFunc) (*Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("projects/%s/registry/repositories/%d/tags/%s",
		PathEscape(project),
		repository,
		tagName,
	)

	req, err := s.client.NewRequest(http.MethodDelete, u, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}

// DeleteRegistryRepositoryTagsOptions represents the available
// DeleteRegistryRepositoryTags() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/container_registry.html#delete-registry-repository-tags-in-bulk
type DeleteRegistryRepositoryTagsOptions struct {
	NameRegexpDelete *string `url:"name_regex_delete,omitempty" json:"name_regex_delete,omitempty"`
	NameRegexpKeep   *string `url:"name_regex_keep,omitempty" json:"name_regex_keep,omitempty"`
	KeepN            *int    `url:"keep_n,omitempty" json:"keep_n,omitempty"`
	OlderThan        *string `url:"older_than,omitempty" json:"older_than,omitempty"`

	// Deprecated: NameRegexp is deprecated in favor of NameRegexpDelete.
	NameRegexp *string `url:"name_regex,omitempty" json:"name_regex,omitempty"`
}

// DeleteRegistryRepositoryTags deletes repository tags in bulk based on
// given criteria.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/container_registry.html#delete-registry-repository-tags-in-bulk
func (s *ContainerRegistryService) DeleteRegistryRepositoryTags(pid interface{}, repository int, opt *DeleteRegistryRepositoryTagsOptions, options ...RequestOptionFunc) (*Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("projects/%s/registry/repositories/%d/tags",
		PathEscape(project),
		repository,
	)

	req, err := s.client.NewRequest(http.MethodDelete, u, opt, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/container_registry_test.go000066400000000000000000000303011475761473200263540ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"fmt"
	"net/http"
	"reflect"
	"testing"
	"time"
)

func TestListProjectRegistryRepositories(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/5/registry/repositories", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `[
			{
			  "id": 1,
			  "name": "",
			  "path": "group/project",
			  "project_id": 9,
			  "location": "gitlab.example.com:5000/group/project",
			  "created_at": "2019-01-10T13:38:57.391Z",
			  "cleanup_policy_started_at": "2020-01-10T15:40:57.391Z"
			},
			{
			  "id": 2,
			  "name": "releases",
			  "path": "group/project/releases",
			  "project_id": 9,
			  "location": "gitlab.example.com:5000/group/project/releases",
			  "created_at": "2019-01-10T13:39:08.229Z",
			  "cleanup_policy_started_at": "2020-08-17T03:12:35.489Z"
			}
		  ]`)
	})

	repositories, _, err := client.ContainerRegistry.ListProjectRegistryRepositories(5, &ListRegistryRepositoriesOptions{})
	if err != nil {
		t.Errorf("ContainerRegistry.ListProjectRegistryRepositories returned error: %v", err)
	}

	createdAt1, err := time.Parse(time.RFC3339, "2019-01-10T13:38:57.391Z")
	if err != nil {
		t.Errorf("ContainerRepository.ListProjectRegistryRepositories error while parsing time: %v", err)
	}

	createdAt2, err := time.Parse(time.RFC3339, "2019-01-10T13:39:08.229Z")
	if err != nil {
		t.Errorf("ContainerRepository.ListProjectRegistryRepositories error while parsing time: %v", err)
	}

	cleanupPolicyStartedAt1, err := time.Parse(time.RFC3339, "2020-01-10T15:40:57.391Z")
	if err != nil {
		t.Errorf("ContainerRepository.ListProjectRegistryRepositories error while parsing time: %v", err)
	}

	cleanupPolicyStartedAt2, err := time.Parse(time.RFC3339, "2020-08-17T03:12:35.489Z")
	if err != nil {
		t.Errorf("ContainerRepository.ListProjectRegistryRepositories error while parsing time: %v", err)
	}

	want := []*RegistryRepository{
		{
			ID:                     1,
			Name:                   "",
			Path:                   "group/project",
			ProjectID:              9,
			Location:               "gitlab.example.com:5000/group/project",
			CreatedAt:              &createdAt1,
			CleanupPolicyStartedAt: &cleanupPolicyStartedAt1,
		},
		{
			ID:                     2,
			Name:                   "releases",
			Path:                   "group/project/releases",
			ProjectID:              9,
			Location:               "gitlab.example.com:5000/group/project/releases",
			CreatedAt:              &createdAt2,
			CleanupPolicyStartedAt: &cleanupPolicyStartedAt2,
		},
	}
	if !reflect.DeepEqual(want, repositories) {
		t.Errorf("ContainerRepository.ListProjectRegistryRepositories returned %+v, want %+v", repositories, want)
	}
}

func TestListGroupRegistryRepositories(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/5/registry/repositories", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `[
			{
			  "id": 1,
			  "name": "",
			  "path": "group/project",
			  "project_id": 9,
			  "location": "gitlab.example.com:5000/group/project",
			  "created_at": "2019-01-10T13:38:57.391Z",
			  "cleanup_policy_started_at": "2020-01-10T15:40:57.391Z"
			},
			{
			  "id": 2,
			  "name": "releases",
			  "path": "group/project/releases",
			  "project_id": 9,
			  "location": "gitlab.example.com:5000/group/project/releases",
			  "created_at": "2019-01-10T13:39:08.229Z",
			  "cleanup_policy_started_at": "2020-08-17T03:12:35.489Z"
			}
		  ]`)
	})

	repositories, _, err := client.ContainerRegistry.ListGroupRegistryRepositories(5, &ListRegistryRepositoriesOptions{})
	if err != nil {
		t.Errorf("ContainerRegistry.ListGroupRegistryRepositories returned error: %v", err)
	}

	createdAt1, err := time.Parse(time.RFC3339, "2019-01-10T13:38:57.391Z")
	if err != nil {
		t.Errorf("ContainerRepository.ListGroupRegistryRepositories error while parsing time: %v", err)
	}

	createdAt2, err := time.Parse(time.RFC3339, "2019-01-10T13:39:08.229Z")
	if err != nil {
		t.Errorf("ContainerRepository.ListGroupRegistryRepositories error while parsing time: %v", err)
	}

	cleanupPolicyStartedAt1, err := time.Parse(time.RFC3339, "2020-01-10T15:40:57.391Z")
	if err != nil {
		t.Errorf("ContainerRepository.ListGroupRegistryRepositories error while parsing time: %v", err)
	}

	cleanupPolicyStartedAt2, err := time.Parse(time.RFC3339, "2020-08-17T03:12:35.489Z")
	if err != nil {
		t.Errorf("ContainerRepository.ListGroupRegistryRepositories error while parsing time: %v", err)
	}

	want := []*RegistryRepository{
		{
			ID:                     1,
			Name:                   "",
			Path:                   "group/project",
			ProjectID:              9,
			Location:               "gitlab.example.com:5000/group/project",
			CreatedAt:              &createdAt1,
			CleanupPolicyStartedAt: &cleanupPolicyStartedAt1,
		},
		{
			ID:                     2,
			Name:                   "releases",
			Path:                   "group/project/releases",
			ProjectID:              9,
			Location:               "gitlab.example.com:5000/group/project/releases",
			CreatedAt:              &createdAt2,
			CleanupPolicyStartedAt: &cleanupPolicyStartedAt2,
		},
	}
	if !reflect.DeepEqual(want, repositories) {
		t.Errorf("ContainerRepository.ListGroupRegistryRepositories returned %+v, want %+v", repositories, want)
	}
}

func TestGetSingleRegistryRepository(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/registry/repositories/5", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `{
			  "id": 1,
			  "name": "",
			  "path": "group/project",
			  "project_id": 9,
			  "location": "gitlab.example.com:5000/group/project",
			  "created_at": "2019-01-10T13:38:57.391Z",
			  "cleanup_policy_started_at": "2020-01-10T15:40:57.391Z"
		  }`)
	})

	repository, _, err := client.ContainerRegistry.GetSingleRegistryRepository(5, &GetSingleRegistryRepositoryOptions{})
	if err != nil {
		t.Errorf("ContainerRegistry.GetSingleRegistryRepository returned error: %v", err)
	}

	createdAt, err := time.Parse(time.RFC3339, "2019-01-10T13:38:57.391Z")
	if err != nil {
		t.Errorf("ContainerRepository.GetSingleRegistryRepository error while parsing time: %v", err)
	}
	cleanupPolicyStartedAt, err := time.Parse(time.RFC3339, "2020-01-10T15:40:57.391Z")
	if err != nil {
		t.Errorf("ContainerRepository.GetSingleRegistryRepository error while parsing time: %v", err)
	}

	want := &RegistryRepository{
		ID:                     1,
		Name:                   "",
		Path:                   "group/project",
		ProjectID:              9,
		Location:               "gitlab.example.com:5000/group/project",
		CreatedAt:              &createdAt,
		CleanupPolicyStartedAt: &cleanupPolicyStartedAt,
	}
	if !reflect.DeepEqual(want, repository) {
		t.Errorf("ContainerRepository.GetSingleRegistryRepository returned %+v, want %+v", repository, want)
	}
}

func TestDeleteRegistryRepository(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/5/registry/repositories/2", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodDelete)
	})

	_, err := client.ContainerRegistry.DeleteRegistryRepository(5, 2)
	if err != nil {
		t.Errorf("ContainerRegistry.DeleteRegistryRepository returned error: %v", err)
	}
}

func TestListRegistryRepositoryTags(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/5/registry/repositories/2/tags", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `[
			{
			  "name": "A",
			  "path": "group/project:A",
			  "location": "gitlab.example.com:5000/group/project:A"
			},
			{
			  "name": "latest",
			  "path": "group/project:latest",
			  "location": "gitlab.example.com:5000/group/project:latest"
			}
		  ]`)
	})

	opt := &ListRegistryRepositoryTagsOptions{}
	registryRepositoryTags, _, err := client.ContainerRegistry.ListRegistryRepositoryTags(5, 2, opt)
	if err != nil {
		t.Errorf("ContainerRegistry.ListRegistryRepositoryTags returned error: %v", err)
	}

	want := []*RegistryRepositoryTag{
		{
			Name:     "A",
			Path:     "group/project:A",
			Location: "gitlab.example.com:5000/group/project:A",
		},
		{
			Name:     "latest",
			Path:     "group/project:latest",
			Location: "gitlab.example.com:5000/group/project:latest",
		},
	}
	if !reflect.DeepEqual(want, registryRepositoryTags) {
		t.Errorf("ContainerRepository.ListRegistryRepositoryTags returned %+v, want %+v", registryRepositoryTags, want)
	}
}

func TestGetRegistryRepositoryTagDetail(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/5/registry/repositories/2/tags/v10.0.0", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `{
			"name": "v10.0.0",
			"path": "group/project:latest",
			"location": "gitlab.example.com:5000/group/project:latest",
			"revision": "e9ed9d87c881d8c2fd3a31b41904d01ba0b836e7fd15240d774d811a1c248181",
			"short_revision": "e9ed9d87c",
			"digest": "sha256:c3490dcf10ffb6530c1303522a1405dfaf7daecd8f38d3e6a1ba19ea1f8a1751",
			"created_at": "2019-01-06T16:49:51.272+00:00",
			"total_size": 350224384
		  }`)
	})

	repositoryTag, _, err := client.ContainerRegistry.GetRegistryRepositoryTagDetail(5, 2, "v10.0.0")
	if err != nil {
		t.Errorf("ContainerRegistry.GetRegistryRepositoryTagDetail returned error: %v", err)
	}

	createdAt, err := time.Parse(timeLayout, "2019-01-06T16:49:51.272+00:00")
	if err != nil {
		t.Errorf("ContainerRepository.ListRegistryRepositories error while parsing time: %v", err)
	}

	want := &RegistryRepositoryTag{
		Name:          "v10.0.0",
		Path:          "group/project:latest",
		Location:      "gitlab.example.com:5000/group/project:latest",
		Revision:      "e9ed9d87c881d8c2fd3a31b41904d01ba0b836e7fd15240d774d811a1c248181",
		ShortRevision: "e9ed9d87c",
		Digest:        "sha256:c3490dcf10ffb6530c1303522a1405dfaf7daecd8f38d3e6a1ba19ea1f8a1751",
		CreatedAt:     &createdAt,
		TotalSize:     350224384,
	}
	if !reflect.DeepEqual(want, repositoryTag) {
		t.Errorf("ContainerRepository.ListRegistryRepositories returned %+v, want %+v", repositoryTag, want)
	}
}

func TestDeleteRegistryRepositoryTag(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/5/registry/repositories/2/tags/v10.0.0", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodDelete)
	})

	_, err := client.ContainerRegistry.DeleteRegistryRepositoryTag(5, 2, "v10.0.0")
	if err != nil {
		t.Errorf("ContainerRepository.DeleteRegistryRepositoryTag returned error: %v", err)
	}
}

func TestDeleteRegistryRepositoryTags(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/5/registry/repositories/2/tags", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodDelete)
	})

	tests := []struct {
		event           string
		nameRegexDelete string
		keepN           int
		nameRegexKeep   string
		olderThan       string
	}{
		{
			"keep_atleast_5_remove_2_days_old",
			"[0-9a-z]{40}",
			0,
			"",
			"2d",
		},
		{
			"remove_all_keep_5",
			".*",
			5,
			"",
			"",
		},
		{
			"remove_all_tags_keep_tags_beginning_with_stable",
			".*",
			0,
			"stable.*",
			"",
		},
		{
			"remove_all_tags_older_than_1_month",
			".*",
			0,
			"",
			"1month",
		},
	}
	for _, tc := range tests {
		t.Run(tc.event, func(t *testing.T) {
			opt := &DeleteRegistryRepositoryTagsOptions{
				NameRegexpDelete: &tc.nameRegexDelete,
				NameRegexpKeep:   &tc.nameRegexKeep,
				KeepN:            &tc.keepN,
				OlderThan:        &tc.olderThan,
			}
			_, err := client.ContainerRegistry.DeleteRegistryRepositoryTags(5, 2, opt)
			if err != nil {
				t.Errorf("ContainerRegistry.DeleteRegistryRepositoryTags returned error: %v", err)
			}
		})
	}
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/custom_attributes.go000066400000000000000000000167511475761473200252000ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"fmt"
	"net/http"
)

// CustomAttributesService handles communication with the group, project and
// user custom attributes related methods of the GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/custom_attributes.html
type CustomAttributesService struct {
	client *Client
}

// CustomAttribute struct is used to unmarshal response to api calls.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/custom_attributes.html
type CustomAttribute struct {
	Key   string `json:"key"`
	Value string `json:"value"`
}

// ListCustomUserAttributes lists the custom attributes of the specified user.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/custom_attributes.html#list-custom-attributes
func (s *CustomAttributesService) ListCustomUserAttributes(user int, options ...RequestOptionFunc) ([]*CustomAttribute, *Response, error) {
	return s.listCustomAttributes("users", user, options...)
}

// ListCustomGroupAttributes lists the custom attributes of the specified group.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/custom_attributes.html#list-custom-attributes
func (s *CustomAttributesService) ListCustomGroupAttributes(group int, options ...RequestOptionFunc) ([]*CustomAttribute, *Response, error) {
	return s.listCustomAttributes("groups", group, options...)
}

// ListCustomProjectAttributes lists the custom attributes of the specified project.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/custom_attributes.html#list-custom-attributes
func (s *CustomAttributesService) ListCustomProjectAttributes(project int, options ...RequestOptionFunc) ([]*CustomAttribute, *Response, error) {
	return s.listCustomAttributes("projects", project, options...)
}

func (s *CustomAttributesService) listCustomAttributes(resource string, id int, options ...RequestOptionFunc) ([]*CustomAttribute, *Response, error) {
	u := fmt.Sprintf("%s/%d/custom_attributes", resource, id)
	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	var cas []*CustomAttribute
	resp, err := s.client.Do(req, &cas)
	if err != nil {
		return nil, resp, err
	}
	return cas, resp, nil
}

// GetCustomUserAttribute returns the user attribute with a speciifc key.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/custom_attributes.html#single-custom-attribute
func (s *CustomAttributesService) GetCustomUserAttribute(user int, key string, options ...RequestOptionFunc) (*CustomAttribute, *Response, error) {
	return s.getCustomAttribute("users", user, key, options...)
}

// GetCustomGroupAttribute returns the group attribute with a speciifc key.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/custom_attributes.html#single-custom-attribute
func (s *CustomAttributesService) GetCustomGroupAttribute(group int, key string, options ...RequestOptionFunc) (*CustomAttribute, *Response, error) {
	return s.getCustomAttribute("groups", group, key, options...)
}

// GetCustomProjectAttribute returns the project attribute with a speciifc key.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/custom_attributes.html#single-custom-attribute
func (s *CustomAttributesService) GetCustomProjectAttribute(project int, key string, options ...RequestOptionFunc) (*CustomAttribute, *Response, error) {
	return s.getCustomAttribute("projects", project, key, options...)
}

func (s *CustomAttributesService) getCustomAttribute(resource string, id int, key string, options ...RequestOptionFunc) (*CustomAttribute, *Response, error) {
	u := fmt.Sprintf("%s/%d/custom_attributes/%s", resource, id, key)
	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	var ca *CustomAttribute
	resp, err := s.client.Do(req, &ca)
	if err != nil {
		return nil, resp, err
	}
	return ca, resp, nil
}

// SetCustomUserAttribute sets the custom attributes of the specified user.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/custom_attributes.html#set-custom-attribute
func (s *CustomAttributesService) SetCustomUserAttribute(user int, c CustomAttribute, options ...RequestOptionFunc) (*CustomAttribute, *Response, error) {
	return s.setCustomAttribute("users", user, c, options...)
}

// SetCustomGroupAttribute sets the custom attributes of the specified group.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/custom_attributes.html#set-custom-attribute
func (s *CustomAttributesService) SetCustomGroupAttribute(group int, c CustomAttribute, options ...RequestOptionFunc) (*CustomAttribute, *Response, error) {
	return s.setCustomAttribute("groups", group, c, options...)
}

// SetCustomProjectAttribute sets the custom attributes of the specified project.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/custom_attributes.html#set-custom-attribute
func (s *CustomAttributesService) SetCustomProjectAttribute(project int, c CustomAttribute, options ...RequestOptionFunc) (*CustomAttribute, *Response, error) {
	return s.setCustomAttribute("projects", project, c, options...)
}

func (s *CustomAttributesService) setCustomAttribute(resource string, id int, c CustomAttribute, options ...RequestOptionFunc) (*CustomAttribute, *Response, error) {
	u := fmt.Sprintf("%s/%d/custom_attributes/%s", resource, id, c.Key)
	req, err := s.client.NewRequest(http.MethodPut, u, c, options)
	if err != nil {
		return nil, nil, err
	}

	ca := new(CustomAttribute)
	resp, err := s.client.Do(req, ca)
	if err != nil {
		return nil, resp, err
	}
	return ca, resp, nil
}

// DeleteCustomUserAttribute removes the custom attribute of the specified user.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/custom_attributes.html#delete-custom-attribute
func (s *CustomAttributesService) DeleteCustomUserAttribute(user int, key string, options ...RequestOptionFunc) (*Response, error) {
	return s.deleteCustomAttribute("users", user, key, options...)
}

// DeleteCustomGroupAttribute removes the custom attribute of the specified group.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/custom_attributes.html#delete-custom-attribute
func (s *CustomAttributesService) DeleteCustomGroupAttribute(group int, key string, options ...RequestOptionFunc) (*Response, error) {
	return s.deleteCustomAttribute("groups", group, key, options...)
}

// DeleteCustomProjectAttribute removes the custom attribute of the specified project.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/custom_attributes.html#delete-custom-attribute
func (s *CustomAttributesService) DeleteCustomProjectAttribute(project int, key string, options ...RequestOptionFunc) (*Response, error) {
	return s.deleteCustomAttribute("projects", project, key, options...)
}

func (s *CustomAttributesService) deleteCustomAttribute(resource string, id int, key string, options ...RequestOptionFunc) (*Response, error) {
	u := fmt.Sprintf("%s/%d/custom_attributes/%s", resource, id, key)
	req, err := s.client.NewRequest(http.MethodDelete, u, nil, options)
	if err != nil {
		return nil, err
	}
	return s.client.Do(req, nil)
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/custom_attributes_test.go000066400000000000000000000203301475761473200262230ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"fmt"
	"net/http"
	"reflect"
	"testing"
)

func TestListCustomUserAttributes(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/users/2/custom_attributes", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprint(w, `[{"key":"testkey1", "value":"testvalue1"}, {"key":"testkey2", "value":"testvalue2"}]`)
	})

	customAttributes, _, err := client.CustomAttribute.ListCustomUserAttributes(2)
	if err != nil {
		t.Errorf("CustomAttribute.ListCustomUserAttributes returned error: %v", err)
	}

	want := []*CustomAttribute{{Key: "testkey1", Value: "testvalue1"}, {Key: "testkey2", Value: "testvalue2"}}
	if !reflect.DeepEqual(want, customAttributes) {
		t.Errorf("CustomAttribute.ListCustomUserAttributes returned %+v, want %+v", customAttributes, want)
	}
}

func TestListCustomGroupAttributes(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/2/custom_attributes", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprint(w, `[{"key":"testkey1", "value":"testvalue1"}, {"key":"testkey2", "value":"testvalue2"}]`)
	})

	customAttributes, _, err := client.CustomAttribute.ListCustomGroupAttributes(2)
	if err != nil {
		t.Errorf("CustomAttribute.ListCustomGroupAttributes returned error: %v", err)
	}

	want := []*CustomAttribute{{Key: "testkey1", Value: "testvalue1"}, {Key: "testkey2", Value: "testvalue2"}}
	if !reflect.DeepEqual(want, customAttributes) {
		t.Errorf("CustomAttribute.ListCustomGroupAttributes returned %+v, want %+v", customAttributes, want)
	}
}

func TestListCustomProjectAttributes(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/2/custom_attributes", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprint(w, `[{"key":"testkey1", "value":"testvalue1"}, {"key":"testkey2", "value":"testvalue2"}]`)
	})

	customAttributes, _, err := client.CustomAttribute.ListCustomProjectAttributes(2)
	if err != nil {
		t.Errorf("CustomAttribute.ListCustomProjectAttributes returned error: %v", err)
	}

	want := []*CustomAttribute{{Key: "testkey1", Value: "testvalue1"}, {Key: "testkey2", Value: "testvalue2"}}
	if !reflect.DeepEqual(want, customAttributes) {
		t.Errorf("CustomAttribute.ListCustomProjectAttributes returned %+v, want %+v", customAttributes, want)
	}
}

func TestGetCustomUserAttribute(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/users/2/custom_attributes/testkey1", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprint(w, `{"key":"testkey1", "value":"testvalue1"}`)
	})

	customAttribute, _, err := client.CustomAttribute.GetCustomUserAttribute(2, "testkey1")
	if err != nil {
		t.Errorf("CustomAttribute.GetCustomUserAttribute returned error: %v", err)
	}

	want := &CustomAttribute{Key: "testkey1", Value: "testvalue1"}
	if !reflect.DeepEqual(want, customAttribute) {
		t.Errorf("CustomAttribute.GetCustomUserAttribute returned %+v, want %+v", customAttribute, want)
	}
}

func TestGetCustomGropupAttribute(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/2/custom_attributes/testkey1", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprint(w, `{"key":"testkey1", "value":"testvalue1"}`)
	})

	customAttribute, _, err := client.CustomAttribute.GetCustomGroupAttribute(2, "testkey1")
	if err != nil {
		t.Errorf("CustomAttribute.GetCustomGroupAttribute returned error: %v", err)
	}

	want := &CustomAttribute{Key: "testkey1", Value: "testvalue1"}
	if !reflect.DeepEqual(want, customAttribute) {
		t.Errorf("CustomAttribute.GetCustomGroupAttribute returned %+v, want %+v", customAttribute, want)
	}
}

func TestGetCustomProjectAttribute(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/2/custom_attributes/testkey1", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprint(w, `{"key":"testkey1", "value":"testvalue1"}`)
	})

	customAttribute, _, err := client.CustomAttribute.GetCustomProjectAttribute(2, "testkey1")
	if err != nil {
		t.Errorf("CustomAttribute.GetCustomProjectAttribute returned error: %v", err)
	}

	want := &CustomAttribute{Key: "testkey1", Value: "testvalue1"}
	if !reflect.DeepEqual(want, customAttribute) {
		t.Errorf("CustomAttribute.GetCustomProjectAttribute returned %+v, want %+v", customAttribute, want)
	}
}

func TestSetCustomUserAttribute(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/users/2/custom_attributes/testkey1", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPut)
		fmt.Fprint(w, `{"key":"testkey1", "value":"testvalue1"}`)
	})

	customAttribute, _, err := client.CustomAttribute.SetCustomUserAttribute(2, CustomAttribute{
		Key:   "testkey1",
		Value: "testvalue1",
	})
	if err != nil {
		t.Errorf("CustomAttribute.SetCustomUserAttributes returned error: %v", err)
	}

	want := &CustomAttribute{Key: "testkey1", Value: "testvalue1"}
	if !reflect.DeepEqual(want, customAttribute) {
		t.Errorf("CustomAttribute.SetCustomUserAttributes returned %+v, want %+v", customAttribute, want)
	}
}

func TestSetCustomGroupAttribute(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/2/custom_attributes/testkey1", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPut)
		fmt.Fprint(w, `{"key":"testkey1", "value":"testvalue1"}`)
	})

	customAttribute, _, err := client.CustomAttribute.SetCustomGroupAttribute(2, CustomAttribute{
		Key:   "testkey1",
		Value: "testvalue1",
	})
	if err != nil {
		t.Errorf("CustomAttribute.SetCustomGroupAttributes returned error: %v", err)
	}

	want := &CustomAttribute{Key: "testkey1", Value: "testvalue1"}
	if !reflect.DeepEqual(want, customAttribute) {
		t.Errorf("CustomAttribute.SetCustomGroupAttributes returned %+v, want %+v", customAttribute, want)
	}
}

func TestDeleteCustomUserAttribute(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/users/2/custom_attributes/testkey1", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodDelete)
		w.WriteHeader(http.StatusAccepted)
	})

	resp, err := client.CustomAttribute.DeleteCustomUserAttribute(2, "testkey1")
	if err != nil {
		t.Errorf("CustomAttribute.DeleteCustomUserAttribute returned error: %v", err)
	}

	want := http.StatusAccepted
	got := resp.StatusCode
	if got != want {
		t.Errorf("CustomAttribute.DeleteCustomUserAttribute returned %d, want %d", got, want)
	}
}

func TestDeleteCustomGroupAttribute(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/2/custom_attributes/testkey1", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodDelete)
		w.WriteHeader(http.StatusAccepted)
	})

	resp, err := client.CustomAttribute.DeleteCustomGroupAttribute(2, "testkey1")
	if err != nil {
		t.Errorf("CustomAttribute.DeleteCustomGroupAttribute returned error: %v", err)
	}

	want := http.StatusAccepted
	got := resp.StatusCode
	if got != want {
		t.Errorf("CustomAttribute.DeleteCustomGroupAttribute returned %d, want %d", got, want)
	}
}

func TestDeleteCustomProjectAttribute(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/2/custom_attributes/testkey1", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodDelete)
		w.WriteHeader(http.StatusAccepted)
	})

	resp, err := client.CustomAttribute.DeleteCustomProjectAttribute(2, "testkey1")
	if err != nil {
		t.Errorf("CustomAttribute.DeleteCustomProjectAttribute returned error: %v", err)
	}

	want := http.StatusAccepted
	got := resp.StatusCode
	if got != want {
		t.Errorf("CustomAttribute.DeleteCustomProjectAttribute returned %d, want %d", got, want)
	}
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/dependency_list_export.go000066400000000000000000000073421475761473200261660ustar00rootroot00000000000000package gitlab

import (
	"bytes"
	"fmt"
	"io"
	"net/http"
)

type DependencyListExportService struct {
	client *Client
}

// CreateDependencyListExportOptions represents the available CreateDependencyListExport()
// options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/dependency_list_export.html#create-a-pipeline-level-dependency-list-export
type CreateDependencyListExportOptions struct {
	ExportType *string `url:"export_type" json:"export_type"`
}

// DependencyListExport represents a request for a GitLab project's dependency list.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/dependency_list_export.html#create-a-pipeline-level-dependency-list-export
type DependencyListExport struct {
	ID          int    `json:"id"`
	HasFinished bool   `json:"has_finished"`
	Self        string `json:"self"`
	Download    string `json:"download"`
}

const defaultExportType = "sbom"

// CreateDependencyListExport creates a new CycloneDX JSON export for all the project dependencies
// detected in a pipeline.
//
// If an authenticated user does not have permission to read_dependency, this request returns a 403
// Forbidden status code.
//
// SBOM exports can be only accessed by the export’s author.
//
// GitLab docs:
// https://docs.gitlab.com/ee/api/dependency_list_export.html#create-a-pipeline-level-dependency-list-export
func (s *DependencyListExportService) CreateDependencyListExport(pipelineID int, opt *CreateDependencyListExportOptions, options ...RequestOptionFunc) (*DependencyListExport, *Response, error) {
	// POST /pipelines/:id/dependency_list_exports
	createExportPath := fmt.Sprintf("pipelines/%d/dependency_list_exports", pipelineID)

	if opt == nil {
		opt = &CreateDependencyListExportOptions{}
	}
	if opt.ExportType == nil {
		opt.ExportType = Ptr(defaultExportType)
	}

	req, err := s.client.NewRequest(http.MethodPost, createExportPath, opt, options)
	if err != nil {
		return nil, nil, err
	}

	export := new(DependencyListExport)
	resp, err := s.client.Do(req, &export)
	if err != nil {
		return nil, resp, err
	}

	return export, resp, nil
}

// GetDependencyListExport gets metadata about a single dependency list export.
//
// GitLab docs:
// https://docs.gitlab.com/ee/api/dependency_list_export.html#get-single-dependency-list-export
func (s *DependencyListExportService) GetDependencyListExport(id int, options ...RequestOptionFunc) (*DependencyListExport, *Response, error) {
	// GET /dependency_list_exports/:id
	getExportPath := fmt.Sprintf("dependency_list_exports/%d", id)

	req, err := s.client.NewRequest(http.MethodGet, getExportPath, nil, options)
	if err != nil {
		return nil, nil, err
	}

	export := new(DependencyListExport)
	resp, err := s.client.Do(req, &export)
	if err != nil {
		return nil, resp, err
	}

	return export, resp, nil
}

// DownloadDependencyListExport downloads a single dependency list export.
//
// The github.com/CycloneDX/cyclonedx-go package can be used to parse the data from the returned io.Reader.
//
//	sbom := new(cdx.BOM)
//	decoder := cdx.NewBOMDecoder(reader, cdx.BOMFileFormatJSON)
//
//	if err = decoder.Decode(sbom); err != nil {
//		panic(err)
//	}
//
// GitLab docs:
// https://docs.gitlab.com/ee/api/dependency_list_export.html#download-dependency-list-export
func (s *DependencyListExportService) DownloadDependencyListExport(id int, options ...RequestOptionFunc) (io.Reader, *Response, error) {
	// GET /dependency_list_exports/:id/download
	downloadExportPath := fmt.Sprintf("dependency_list_exports/%d/download", id)

	req, err := s.client.NewRequest(http.MethodGet, downloadExportPath, nil, options)
	if err != nil {
		return nil, nil, err
	}

	var sbomBuffer bytes.Buffer
	resp, err := s.client.Do(req, &sbomBuffer)
	if err != nil {
		return nil, resp, err
	}

	return &sbomBuffer, resp, nil
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/dependency_list_export_test.go000066400000000000000000000046331475761473200272250ustar00rootroot00000000000000package gitlab

import (
	"bytes"
	"encoding/json"
	"io"
	"net/http"
	"os"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestCreateDependencyListExport(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/pipelines/1234/dependency_list_exports", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		body, err := io.ReadAll(r.Body)
		require.NoError(t, err)

		var content CreateDependencyListExportOptions
		err = json.Unmarshal(body, &content)
		require.NoError(t, err)

		assert.Equal(t, "sbom", *content.ExportType)
		mustWriteHTTPResponse(t, w, "testdata/create_dependency_list_export.json")
	})

	d := &CreateDependencyListExportOptions{
		ExportType: Ptr("sbom"),
	}

	export, _, err := client.DependencyListExport.CreateDependencyListExport(1234, d)
	require.NoError(t, err)

	want := &DependencyListExport{
		ID:          5678,
		HasFinished: false,
		Self:        "http://gitlab.example.com/api/v4/dependency_list_exports/5678",
		Download:    "http://gitlab.example.com/api/v4/dependency_list_exports/5678/download",
	}
	require.Equal(t, want, export)
}

func TestGetDependencyListExport(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/dependency_list_exports/5678", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		mustWriteHTTPResponse(t, w, "testdata/get_dependency_list_export.json")
	})

	export, _, err := client.DependencyListExport.GetDependencyListExport(5678)
	require.NoError(t, err)

	want := &DependencyListExport{
		ID:          5678,
		HasFinished: true,
		Self:        "http://gitlab.example.com/api/v4/dependency_list_exports/5678",
		Download:    "http://gitlab.example.com/api/v4/dependency_list_exports/5678/download",
	}
	require.Equal(t, want, export)
}

func TestDownloadDependencyListExport(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/dependency_list_exports/5678/download", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		mustWriteHTTPResponse(t, w, "testdata/download_dependency_list_export.json")
	})

	sbomReader, _, err := client.DependencyListExport.DownloadDependencyListExport(5678)
	require.NoError(t, err)

	expectedSbom, err := os.ReadFile("testdata/download_dependency_list_export.json")
	require.NoError(t, err)

	var want bytes.Buffer
	want.Write(expectedSbom)

	require.Equal(t, &want, sbomReader)
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/deploy_keys.go000066400000000000000000000210131475761473200237320ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"fmt"
	"net/http"
	"time"
)

// DeployKeysService handles communication with the keys related methods
// of the GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/deploy_keys.html
type DeployKeysService struct {
	client *Client
}

// InstanceDeployKey represents a GitLab deploy key with the associated
// projects it has write access to.
type InstanceDeployKey struct {
	ID                      int                 `json:"id"`
	Title                   string              `json:"title"`
	CreatedAt               *time.Time          `json:"created_at"`
	Key                     string              `json:"key"`
	Fingerprint             string              `json:"fingerprint"`
	ProjectsWithWriteAccess []*DeployKeyProject `json:"projects_with_write_access"`
}

func (k InstanceDeployKey) String() string {
	return Stringify(k)
}

// DeployKeyProject refers to a project an InstanceDeployKey has write access to.
type DeployKeyProject struct {
	ID                int        `json:"id"`
	Description       string     `json:"description"`
	Name              string     `json:"name"`
	NameWithNamespace string     `json:"name_with_namespace"`
	Path              string     `json:"path"`
	PathWithNamespace string     `json:"path_with_namespace"`
	CreatedAt         *time.Time `json:"created_at"`
}

func (k DeployKeyProject) String() string {
	return Stringify(k)
}

// ProjectDeployKey represents a GitLab project deploy key.
type ProjectDeployKey struct {
	ID                int        `json:"id"`
	Title             string     `json:"title"`
	Key               string     `json:"key"`
	Fingerprint       string     `json:"fingerprint"`
	FingerprintSHA256 string     `json:"fingerprint_sha256"`
	CreatedAt         *time.Time `json:"created_at"`
	CanPush           bool       `json:"can_push"`
	ExpiresAt         *time.Time `json:"expires_at"`
}

func (k ProjectDeployKey) String() string {
	return Stringify(k)
}

// ListProjectDeployKeysOptions represents the available ListAllDeployKeys()
// options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/deploy_keys.html#list-all-deploy-keys
type ListInstanceDeployKeysOptions struct {
	ListOptions
	Public *bool `url:"public,omitempty" json:"public,omitempty"`
}

// ListAllDeployKeys gets a list of all deploy keys
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/deploy_keys.html#list-all-deploy-keys
func (s *DeployKeysService) ListAllDeployKeys(opt *ListInstanceDeployKeysOptions, options ...RequestOptionFunc) ([]*InstanceDeployKey, *Response, error) {
	req, err := s.client.NewRequest(http.MethodGet, "deploy_keys", opt, options)
	if err != nil {
		return nil, nil, err
	}

	var ks []*InstanceDeployKey
	resp, err := s.client.Do(req, &ks)
	if err != nil {
		return nil, resp, err
	}

	return ks, resp, nil
}

// ListProjectDeployKeysOptions represents the available ListProjectDeployKeys()
// options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/deploy_keys.html#list-deploy-keys-for-project
type ListProjectDeployKeysOptions ListOptions

// ListProjectDeployKeys gets a list of a project's deploy keys
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/deploy_keys.html#list-deploy-keys-for-project
func (s *DeployKeysService) ListProjectDeployKeys(pid interface{}, opt *ListProjectDeployKeysOptions, options ...RequestOptionFunc) ([]*ProjectDeployKey, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/deploy_keys", PathEscape(project))

	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var ks []*ProjectDeployKey
	resp, err := s.client.Do(req, &ks)
	if err != nil {
		return nil, resp, err
	}

	return ks, resp, nil
}

// GetDeployKey gets a single deploy key.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/deploy_keys.html#get-a-single-deploy-key
func (s *DeployKeysService) GetDeployKey(pid interface{}, deployKey int, options ...RequestOptionFunc) (*ProjectDeployKey, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/deploy_keys/%d", PathEscape(project), deployKey)

	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	k := new(ProjectDeployKey)
	resp, err := s.client.Do(req, k)
	if err != nil {
		return nil, resp, err
	}

	return k, resp, nil
}

// AddDeployKeyOptions represents the available ADDDeployKey() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/deploy_keys.html#add-deploy-key-for-a-project
type AddDeployKeyOptions struct {
	Key       *string    `url:"key,omitempty" json:"key,omitempty"`
	Title     *string    `url:"title,omitempty" json:"title,omitempty"`
	CanPush   *bool      `url:"can_push,omitempty" json:"can_push,omitempty"`
	ExpiresAt *time.Time `url:"expires_at,omitempty" json:"expires_at,omitempty"`
}

// AddDeployKey creates a new deploy key for a project. If deploy key already
// exists in another project - it will be joined to project but only if
// original one was is accessible by same user.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/deploy_keys.html#add-deploy-key-for-a-project
func (s *DeployKeysService) AddDeployKey(pid interface{}, opt *AddDeployKeyOptions, options ...RequestOptionFunc) (*ProjectDeployKey, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/deploy_keys", PathEscape(project))

	req, err := s.client.NewRequest(http.MethodPost, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	k := new(ProjectDeployKey)
	resp, err := s.client.Do(req, k)
	if err != nil {
		return nil, resp, err
	}

	return k, resp, nil
}

// DeleteDeployKey deletes a deploy key from a project.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/deploy_keys.html#delete-deploy-key
func (s *DeployKeysService) DeleteDeployKey(pid interface{}, deployKey int, options ...RequestOptionFunc) (*Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("projects/%s/deploy_keys/%d", PathEscape(project), deployKey)

	req, err := s.client.NewRequest(http.MethodDelete, u, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}

// EnableDeployKey enables a deploy key.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/deploy_keys.html#enable-a-deploy-key
func (s *DeployKeysService) EnableDeployKey(pid interface{}, deployKey int, options ...RequestOptionFunc) (*ProjectDeployKey, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/deploy_keys/%d/enable", PathEscape(project), deployKey)

	req, err := s.client.NewRequest(http.MethodPost, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	k := new(ProjectDeployKey)
	resp, err := s.client.Do(req, k)
	if err != nil {
		return nil, resp, err
	}

	return k, resp, nil
}

// UpdateDeployKeyOptions represents the available UpdateDeployKey() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/deploy_keys.html#update-deploy-key
type UpdateDeployKeyOptions struct {
	Title   *string `url:"title,omitempty" json:"title,omitempty"`
	CanPush *bool   `url:"can_push,omitempty" json:"can_push,omitempty"`
}

// UpdateDeployKey updates a deploy key for a project.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/deploy_keys.html#update-deploy-key
func (s *DeployKeysService) UpdateDeployKey(pid interface{}, deployKey int, opt *UpdateDeployKeyOptions, options ...RequestOptionFunc) (*ProjectDeployKey, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/deploy_keys/%d", PathEscape(project), deployKey)

	req, err := s.client.NewRequest(http.MethodPut, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	k := new(ProjectDeployKey)
	resp, err := s.client.Do(req, k)
	if err != nil {
		return nil, resp, err
	}

	return k, resp, nil
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/deploy_keys_test.go000066400000000000000000000274011475761473200250000ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"fmt"
	"net/http"
	"reflect"
	"testing"
	"time"
)

func TestListAllDeployKeys(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/deploy_keys", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `[
		{
			"id": 1,
			"title": "Public key",
			"key": "ssh-rsa AAAA...",
			"fingerprint": "7f:72:08:7d:0e:47:48:ec:37:79:b2:76:68:b5:87:65",
			"created_at": "2013-10-02T10:12:29Z",
			"projects_with_write_access": [
			{
				"id": 73,
				"description": null,
				"name": "project2",
				"name_with_namespace": "Sidney Jones / project2",
				"path": "project2",
				"path_with_namespace": "sidney_jones/project2",
				"created_at": "2021-10-25T18:33:17.550Z"
			},
			{
				"id": 74,
				"description": null,
				"name": "project3",
				"name_with_namespace": "Sidney Jones / project3",
				"path": "project3",
				"path_with_namespace": "sidney_jones/project3",
				"created_at": "2021-10-25T18:33:17.666Z"
			}
			]
		},
			{
				"id": 3,
				"title": "Another Public key",
				"key": "ssh-rsa AAAA...",
				"fingerprint": "64:d3:73:d4:83:70:ab:41:96:68:d5:3d:a5:b0:34:ea",
				"created_at": "2013-10-02T11:12:29Z",
				"projects_with_write_access": []
			}
		  ]`)
	})

	deployKeys, _, err := client.DeployKeys.ListAllDeployKeys(&ListInstanceDeployKeysOptions{})
	if err != nil {
		t.Errorf("DeployKeys.ListAllDeployKeys returned error: %v", err)
	}

	createdAtKey1, _ := time.Parse(timeLayout, "2013-10-02T10:12:29Z")
	createdAtKey1Enable1, _ := time.Parse(timeLayout, "2021-10-25T18:33:17.550Z")
	createdAtKey1Enable2, _ := time.Parse(timeLayout, "2021-10-25T18:33:17.666Z")
	createdAtKey2, _ := time.Parse(timeLayout, "2013-10-02T11:12:29Z")

	want := []*InstanceDeployKey{
		{
			ID:          1,
			Title:       "Public key",
			Key:         "ssh-rsa AAAA...",
			CreatedAt:   &createdAtKey1,
			Fingerprint: "7f:72:08:7d:0e:47:48:ec:37:79:b2:76:68:b5:87:65",
			ProjectsWithWriteAccess: []*DeployKeyProject{
				{
					ID:                73,
					Description:       "",
					Name:              "project2",
					NameWithNamespace: "Sidney Jones / project2",
					Path:              "project2",
					PathWithNamespace: "sidney_jones/project2",
					CreatedAt:         &createdAtKey1Enable1,
				},
				{
					ID:                74,
					Description:       "",
					Name:              "project3",
					NameWithNamespace: "Sidney Jones / project3",
					Path:              "project3",
					PathWithNamespace: "sidney_jones/project3",
					CreatedAt:         &createdAtKey1Enable2,
				},
			},
		},
		{
			ID:                      3,
			Title:                   "Another Public key",
			Key:                     "ssh-rsa AAAA...",
			Fingerprint:             "64:d3:73:d4:83:70:ab:41:96:68:d5:3d:a5:b0:34:ea",
			CreatedAt:               &createdAtKey2,
			ProjectsWithWriteAccess: []*DeployKeyProject{},
		},
	}
	if !reflect.DeepEqual(want, deployKeys) {
		t.Errorf("DeployKeys.ListAllDeployKeys returned %+v, want %+v", deployKeys, want)
	}
}

func TestListProjectDeployKeys(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/5/deploy_keys", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `[
			{
			  "id": 1,
			  "title": "Public key",
			  "key": "ssh-rsa AAAA...",
			  "fingerprint": "4a:9d:64:15:ed:3a:e6:07:6e:89:36:b3:3b:03:05:d9",
			  "fingerprint_sha256": "SHA256:Jrs3LD1Ji30xNLtTVf9NDCj7kkBgPBb2pjvTZ3HfIgU",
			  "created_at": "2013-10-02T10:12:29Z",
			  "can_push": false
			},
			{
			  "id": 3,
			  "title": "Another Public key",
			  "key": "ssh-rsa AAAA...",
			  "fingerprint": "0b:cf:58:40:b9:23:96:c7:ba:44:df:0e:9e:87:5e:75",
			  "fingerprint_sha256": "SHA256:lGI/Ys/Wx7PfMhUO1iuBH92JQKYN+3mhJZvWO4Q5ims",
			  "created_at": "2013-10-02T11:12:29Z",
			  "can_push": false
			}
		  ]`)
	})

	deployKeys, _, err := client.DeployKeys.ListProjectDeployKeys(5, &ListProjectDeployKeysOptions{})
	if err != nil {
		t.Errorf("DeployKeys.ListProjectDeployKeys returned error: %v", err)
	}

	createdAt, err := time.Parse(timeLayout, "2013-10-02T10:12:29Z")
	if err != nil {
		t.Errorf("DeployKeys.ListAllDeployKeys returned an error while parsing time: %v", err)
	}

	createdAt2, err := time.Parse(timeLayout, "2013-10-02T11:12:29Z")
	if err != nil {
		t.Errorf("DeployKeys.ListAllDeployKeys returned an error while parsing time: %v", err)
	}

	want := []*ProjectDeployKey{
		{
			ID:                1,
			Title:             "Public key",
			Key:               "ssh-rsa AAAA...",
			Fingerprint:       "4a:9d:64:15:ed:3a:e6:07:6e:89:36:b3:3b:03:05:d9",
			FingerprintSHA256: "SHA256:Jrs3LD1Ji30xNLtTVf9NDCj7kkBgPBb2pjvTZ3HfIgU",
			CreatedAt:         &createdAt,
			CanPush:           false,
		},
		{
			ID:                3,
			Title:             "Another Public key",
			Key:               "ssh-rsa AAAA...",
			Fingerprint:       "0b:cf:58:40:b9:23:96:c7:ba:44:df:0e:9e:87:5e:75",
			FingerprintSHA256: "SHA256:lGI/Ys/Wx7PfMhUO1iuBH92JQKYN+3mhJZvWO4Q5ims",
			CreatedAt:         &createdAt2,
			CanPush:           false,
		},
	}
	if !reflect.DeepEqual(want, deployKeys) {
		t.Errorf("DeployKeys.ListProjectDeployKeys returned %+v, want %+v", deployKeys, want)
	}
}

func TestGetDeployKey(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/5/deploy_keys/11", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `{
			"id": 1,
			"title": "Public key",
			"key": "ssh-rsa AAAA...",
			"fingerprint": "4a:9d:64:15:ed:3a:e6:07:6e:89:36:b3:3b:03:05:d9",
			"fingerprint_sha256": "SHA256:Jrs3LD1Ji30xNLtTVf9NDCj7kkBgPBb2pjvTZ3HfIgU",
			"created_at": "2013-10-02T10:12:29Z",
			"can_push": false
		  }`)
	})

	deployKey, _, err := client.DeployKeys.GetDeployKey(5, 11)
	if err != nil {
		t.Errorf("DeployKeys.GetDeployKey returned error: %v", err)
	}

	createdAt, err := time.Parse(timeLayout, "2013-10-02T10:12:29Z")
	if err != nil {
		t.Errorf("DeployKeys.ListAllDeployKeys returned an error while parsing time: %v", err)
	}

	want := &ProjectDeployKey{
		ID:                1,
		Title:             "Public key",
		Key:               "ssh-rsa AAAA...",
		Fingerprint:       "4a:9d:64:15:ed:3a:e6:07:6e:89:36:b3:3b:03:05:d9",
		FingerprintSHA256: "SHA256:Jrs3LD1Ji30xNLtTVf9NDCj7kkBgPBb2pjvTZ3HfIgU",
		CreatedAt:         &createdAt,
		CanPush:           false,
	}
	if !reflect.DeepEqual(want, deployKey) {
		t.Errorf("DeployKeys.GetDeployKey returned %+v, want %+v", deployKey, want)
	}
}

func TestAddDeployKey(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/5/deploy_keys", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		fmt.Fprintf(w, `{
			"key" : "ssh-rsa AAAA...",
			"id" : 12,
			"title" : "My deploy key",
			"can_push": true,
			"created_at" : "2015-08-29T12:44:31.550Z",
			"expires_at": null
		 }`)
	})

	opt := &AddDeployKeyOptions{
		Key:     Ptr("ssh-rsa AAAA..."),
		Title:   Ptr("My deploy key"),
		CanPush: Ptr(true),
	}
	deployKey, _, err := client.DeployKeys.AddDeployKey(5, opt)
	if err != nil {
		t.Errorf("DeployKey.AddDeployKey returned error: %v", err)
	}

	createdAt, err := time.Parse(timeLayout, "2015-08-29T12:44:31.550Z")
	if err != nil {
		t.Errorf("DeployKeys.ListAllDeployKeys returned an error while parsing time: %v", err)
	}

	want := &ProjectDeployKey{
		Title:     "My deploy key",
		ID:        12,
		Key:       "ssh-rsa AAAA...",
		CreatedAt: &createdAt,
		CanPush:   true,
	}
	if !reflect.DeepEqual(want, deployKey) {
		t.Errorf("DeployKeys.AddDeployKey returned %+v, want %+v", deployKey, want)
	}
}

func TestAddDeployKey_withExpiresAt(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/5/deploy_keys", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		fmt.Fprintf(w, `{
			"key" : "ssh-rsa AAAA...",
			"id" : 12,
			"title" : "My deploy key",
			"can_push": true,
			"created_at" : "2015-08-29T12:44:31.550Z",
			"expires_at": "2999-03-01T00:00:00.000Z"
		 }`)
	})

	expiresAt, err := time.Parse(timeLayout, "2999-03-01T00:00:00.000Z")
	if err != nil {
		t.Errorf("DeployKeys.AddDeployKey returned an error while parsing time: %v", err)
	}

	opt := &AddDeployKeyOptions{
		Key:       Ptr("ssh-rsa AAAA..."),
		Title:     Ptr("My deploy key"),
		CanPush:   Ptr(true),
		ExpiresAt: &expiresAt,
	}
	deployKey, _, err := client.DeployKeys.AddDeployKey(5, opt)
	if err != nil {
		t.Errorf("DeployKey.AddDeployKey returned error: %v", err)
	}

	createdAt, err := time.Parse(timeLayout, "2015-08-29T12:44:31.550Z")
	if err != nil {
		t.Errorf("DeployKeys.AddDeployKey returned an error while parsing time: %v", err)
	}

	want := &ProjectDeployKey{
		Title:     "My deploy key",
		ID:        12,
		Key:       "ssh-rsa AAAA...",
		CreatedAt: &createdAt,
		CanPush:   true,
		ExpiresAt: &expiresAt,
	}
	if !reflect.DeepEqual(want, deployKey) {
		t.Errorf("DeployKeys.AddDeployKey returned %+v, want %+v", deployKey, want)
	}
}

func TestDeleteDeployKey(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/5/deploy_keys/13", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodDelete)
	})

	_, err := client.DeployKeys.DeleteDeployKey(5, 13)
	if err != nil {
		t.Errorf("Deploykeys.DeleteDeployKey returned error: %v", err)
	}
}

func TestEnableDeployKey(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/5/deploy_keys/13/enable", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		fmt.Fprintf(w, `{
			"key" : "ssh-rsa AAAA...",
			"id" : 12,
			"title" : "My deploy key",
			"created_at" : "2015-08-29T12:44:31.550Z"
		 }`)
	})

	deployKey, _, err := client.DeployKeys.EnableDeployKey(5, 13)
	if err != nil {
		t.Errorf("DeployKeys.EnableDeployKey returned error: %v", err)
	}

	createdAt, err := time.Parse(timeLayout, "2015-08-29T12:44:31.550Z")
	if err != nil {
		t.Errorf("DeployKeys.ListAllDeployKeys returned an error while parsing time: %v", err)
	}

	want := &ProjectDeployKey{
		ID:        12,
		Title:     "My deploy key",
		Key:       "ssh-rsa AAAA...",
		CreatedAt: &createdAt,
	}
	if !reflect.DeepEqual(want, deployKey) {
		t.Errorf("DeployKeys.EnableDeployKey returned %+v, want %+v", deployKey, want)
	}
}

func TestUpdateDeployKey(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/5/deploy_keys/11", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPut)
		fmt.Fprintf(w, `{
			"id": 11,
			"title": "New deploy key",
			"key": "ssh-rsa AAAA...",
			"created_at": "2015-08-29T12:44:31.550Z",
			"can_push": true
		 }`)
	})

	opt := &UpdateDeployKeyOptions{
		Title:   Ptr("New deploy key"),
		CanPush: Ptr(true),
	}
	deployKey, _, err := client.DeployKeys.UpdateDeployKey(5, 11, opt)
	if err != nil {
		t.Errorf("DeployKeys.UpdateDeployKey returned error: %v", err)
	}

	createdAt, err := time.Parse(timeLayout, "2015-08-29T12:44:31.550Z")
	if err != nil {
		t.Errorf("DeployKeys.ListAllDeployKeys returned an error while parsing time: %v", err)
	}

	want := &ProjectDeployKey{
		ID:        11,
		Title:     "New deploy key",
		Key:       "ssh-rsa AAAA...",
		CreatedAt: &createdAt,
		CanPush:   true,
	}
	if !reflect.DeepEqual(want, deployKey) {
		t.Errorf("DeployKeys.UpdateDeployKey returned %+v, want %+v", deployKey, want)
	}
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/deploy_tokens.go000066400000000000000000000213301475761473200242640ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"fmt"
	"net/http"
	"time"
)

// DeployTokensService handles communication with the deploy tokens related methods
// of the GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/deploy_tokens.html
type DeployTokensService struct {
	client *Client
}

// DeployToken represents a GitLab deploy token.
type DeployToken struct {
	ID        int        `json:"id"`
	Name      string     `json:"name"`
	Username  string     `json:"username"`
	ExpiresAt *time.Time `json:"expires_at"`
	Revoked   bool       `json:"revoked"`
	Expired   bool       `json:"expired"`
	Token     string     `json:"token,omitempty"`
	Scopes    []string   `json:"scopes"`
}

func (k DeployToken) String() string {
	return Stringify(k)
}

// ListAllDeployTokens gets a list of all deploy tokens.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/deploy_tokens.html#list-all-deploy-tokens
func (s *DeployTokensService) ListAllDeployTokens(options ...RequestOptionFunc) ([]*DeployToken, *Response, error) {
	req, err := s.client.NewRequest(http.MethodGet, "deploy_tokens", nil, options)
	if err != nil {
		return nil, nil, err
	}

	var ts []*DeployToken
	resp, err := s.client.Do(req, &ts)
	if err != nil {
		return nil, resp, err
	}

	return ts, resp, nil
}

// ListProjectDeployTokensOptions represents the available ListProjectDeployTokens()
// options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/deploy_tokens.html#list-project-deploy-tokens
type ListProjectDeployTokensOptions ListOptions

// ListProjectDeployTokens gets a list of a project's deploy tokens.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/deploy_tokens.html#list-project-deploy-tokens
func (s *DeployTokensService) ListProjectDeployTokens(pid interface{}, opt *ListProjectDeployTokensOptions, options ...RequestOptionFunc) ([]*DeployToken, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/deploy_tokens", PathEscape(project))

	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var ts []*DeployToken
	resp, err := s.client.Do(req, &ts)
	if err != nil {
		return nil, resp, err
	}

	return ts, resp, nil
}

// GetProjectDeployToken gets a single deploy token.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/deploy_tokens.html#get-a-project-deploy-token
func (s *DeployTokensService) GetProjectDeployToken(pid interface{}, deployToken int, options ...RequestOptionFunc) (*DeployToken, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/deploy_tokens/%d", PathEscape(project), deployToken)

	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	t := new(DeployToken)
	resp, err := s.client.Do(req, t)
	if err != nil {
		return nil, resp, err
	}

	return t, resp, nil
}

// CreateProjectDeployTokenOptions represents the available CreateProjectDeployToken() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/deploy_tokens.html#create-a-project-deploy-token
type CreateProjectDeployTokenOptions struct {
	Name      *string    `url:"name,omitempty" json:"name,omitempty"`
	ExpiresAt *time.Time `url:"expires_at,omitempty" json:"expires_at,omitempty"`
	Username  *string    `url:"username,omitempty" json:"username,omitempty"`
	Scopes    *[]string  `url:"scopes,omitempty" json:"scopes,omitempty"`
}

// CreateProjectDeployToken creates a new deploy token for a project.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/deploy_tokens.html#create-a-project-deploy-token
func (s *DeployTokensService) CreateProjectDeployToken(pid interface{}, opt *CreateProjectDeployTokenOptions, options ...RequestOptionFunc) (*DeployToken, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/deploy_tokens", PathEscape(project))

	req, err := s.client.NewRequest(http.MethodPost, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	t := new(DeployToken)
	resp, err := s.client.Do(req, t)
	if err != nil {
		return nil, resp, err
	}

	return t, resp, nil
}

// DeleteProjectDeployToken removes a deploy token from the project.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/deploy_tokens.html#delete-a-project-deploy-token
func (s *DeployTokensService) DeleteProjectDeployToken(pid interface{}, deployToken int, options ...RequestOptionFunc) (*Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("projects/%s/deploy_tokens/%d", PathEscape(project), deployToken)

	req, err := s.client.NewRequest(http.MethodDelete, u, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}

// ListGroupDeployTokensOptions represents the available ListGroupDeployTokens()
// options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/deploy_tokens.html#list-group-deploy-tokens
type ListGroupDeployTokensOptions ListOptions

// ListGroupDeployTokens gets a list of a group’s deploy tokens.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/deploy_tokens.html#list-group-deploy-tokens
func (s *DeployTokensService) ListGroupDeployTokens(gid interface{}, opt *ListGroupDeployTokensOptions, options ...RequestOptionFunc) ([]*DeployToken, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/deploy_tokens", PathEscape(group))

	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var ts []*DeployToken
	resp, err := s.client.Do(req, &ts)
	if err != nil {
		return nil, resp, err
	}

	return ts, resp, nil
}

// GetGroupDeployToken gets a single deploy token.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/deploy_tokens.html#get-a-group-deploy-token
func (s *DeployTokensService) GetGroupDeployToken(gid interface{}, deployToken int, options ...RequestOptionFunc) (*DeployToken, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/deploy_tokens/%d", PathEscape(group), deployToken)

	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	t := new(DeployToken)
	resp, err := s.client.Do(req, t)
	if err != nil {
		return nil, resp, err
	}

	return t, resp, nil
}

// CreateGroupDeployTokenOptions represents the available CreateGroupDeployToken() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/deploy_tokens.html#create-a-group-deploy-token
type CreateGroupDeployTokenOptions struct {
	Name      *string    `url:"name,omitempty" json:"name,omitempty"`
	ExpiresAt *time.Time `url:"expires_at,omitempty" json:"expires_at,omitempty"`
	Username  *string    `url:"username,omitempty" json:"username,omitempty"`
	Scopes    *[]string  `url:"scopes,omitempty" json:"scopes,omitempty"`
}

// CreateGroupDeployToken creates a new deploy token for a group.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/deploy_tokens.html#create-a-group-deploy-token
func (s *DeployTokensService) CreateGroupDeployToken(gid interface{}, opt *CreateGroupDeployTokenOptions, options ...RequestOptionFunc) (*DeployToken, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/deploy_tokens", PathEscape(group))

	req, err := s.client.NewRequest(http.MethodPost, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	t := new(DeployToken)
	resp, err := s.client.Do(req, t)
	if err != nil {
		return nil, resp, err
	}

	return t, resp, nil
}

// DeleteGroupDeployToken removes a deploy token from the group.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/deploy_tokens.html#delete-a-group-deploy-token
func (s *DeployTokensService) DeleteGroupDeployToken(gid interface{}, deployToken int, options ...RequestOptionFunc) (*Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("groups/%s/deploy_tokens/%d", PathEscape(group), deployToken)

	req, err := s.client.NewRequest(http.MethodDelete, u, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/deploy_tokens_test.go000066400000000000000000000222031475761473200253230ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"fmt"
	"net/http"
	"reflect"
	"testing"
	"time"
)

func TestListAllDeployTokens(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/deploy_tokens", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprint(w, `
[
	{
		"id": 1,
		"name": "MyToken",
		"username": "gitlab+deploy-token-1",
		"expires_at": "2020-02-14T00:00:00.000Z",
		"revoked": true,
		"expired": true,
		"scopes": [
			"read_repository",
			"read_registry"
		]
	}
]
`)
	})

	deployTokens, _, err := client.DeployTokens.ListAllDeployTokens()
	if err != nil {
		t.Errorf("DeployTokens.ListAllDeployTokens returned an error: %v", err)
	}

	wantExpiresAt := time.Date(2020, 0o2, 14, 0, 0, 0, 0, time.UTC)

	want := []*DeployToken{
		{
			ID:        1,
			Name:      "MyToken",
			Username:  "gitlab+deploy-token-1",
			ExpiresAt: &wantExpiresAt,
			Revoked:   true,
			Expired:   true,
			Scopes: []string{
				"read_repository",
				"read_registry",
			},
		},
	}

	if !reflect.DeepEqual(want, deployTokens) {
		t.Errorf("DeployTokens.ListAllDeployTokens returned %+v, want %+v", deployTokens, want)
	}
}

func TestListProjectDeployTokens(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/deploy_tokens", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprint(w, `
[
  {
    "id": 1,
    "name": "MyToken",
    "username": "gitlab+deploy-token-1",
    "expires_at": "2020-02-14T00:00:00.000Z",
    "scopes": [
      "read_repository",
      "read_registry"
    ]
  }
]
`)
	})

	deployTokens, _, err := client.DeployTokens.ListProjectDeployTokens(1, nil)
	if err != nil {
		t.Errorf("DeployTokens.ListProjectDeployTokens returned an error: %v", err)
	}

	wantExpiresAt := time.Date(2020, 0o2, 14, 0, 0, 0, 0, time.UTC)

	want := []*DeployToken{
		{
			ID:        1,
			Name:      "MyToken",
			Username:  "gitlab+deploy-token-1",
			ExpiresAt: &wantExpiresAt,
			Scopes: []string{
				"read_repository",
				"read_registry",
			},
		},
	}

	if !reflect.DeepEqual(want, deployTokens) {
		t.Errorf("DeployTokens.ListProjectDeployTokens returned %+v, want %+v", deployTokens, want)
	}
}

func TestGetProjectDeployTokens(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/deploy_tokens/1", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprint(w, `
{
  "id": 1,
  "name": "MyToken",
  "username": "gitlab+deploy-token-1",
  "expires_at": "2020-02-14T00:00:00.000Z",
  "scopes": [
    "read_repository",
    "read_registry"
  ]
}
`)
	})

	deployToken, _, err := client.DeployTokens.GetProjectDeployToken(1, 1)
	if err != nil {
		t.Errorf("DeployTokens.GetProjectDeployToken returned an error: %v", err)
	}

	wantExpiresAt := time.Date(2020, 0o2, 14, 0, 0, 0, 0, time.UTC)

	want := &DeployToken{
		ID:        1,
		Name:      "MyToken",
		Username:  "gitlab+deploy-token-1",
		ExpiresAt: &wantExpiresAt,
		Scopes: []string{
			"read_repository",
			"read_registry",
		},
	}

	if !reflect.DeepEqual(want, deployToken) {
		t.Errorf("DeployTokens.GetProjectDeployToken returned %+v, want %+v", deployToken, want)
	}
}

func TestCreateProjectDeployToken(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/5/deploy_tokens", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		fmt.Fprint(w, `
{
	"id": 1,
	"name": "My deploy token",
	"username": "custom-user",
	"expires_at": "2021-01-01T00:00:00.000Z",
	"token": "jMRvtPNxrn3crTAGukpZ",
	"scopes": [
		"read_repository"
	]
}
`)
	})

	expiresAt := time.Date(2021, 0o1, 0o1, 0, 0, 0, 0, time.UTC)

	deployToken, _, err := client.DeployTokens.CreateProjectDeployToken(5, &CreateProjectDeployTokenOptions{
		Name:      Ptr("My deploy token"),
		Username:  Ptr("custom-user"),
		ExpiresAt: &expiresAt,
		Scopes: &[]string{
			"read_repository",
		},
	})
	if err != nil {
		t.Errorf("DeployTokens.CreateProjectDeployToken returned an error: %v", err)
	}

	want := &DeployToken{
		ID:        1,
		Name:      "My deploy token",
		Username:  "custom-user",
		ExpiresAt: &expiresAt,
		Token:     "jMRvtPNxrn3crTAGukpZ",
		Scopes: []string{
			"read_repository",
		},
	}

	if !reflect.DeepEqual(want, deployToken) {
		t.Errorf("DeployTokens.CreateProjectDeployToken returned %+v, want %+v", deployToken, want)
	}
}

func TestDeleteProjectDeployToken(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/5/deploy_tokens/13", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodDelete)
		w.WriteHeader(http.StatusAccepted)
	})

	resp, err := client.DeployTokens.DeleteProjectDeployToken(5, 13)
	if err != nil {
		t.Errorf("DeployTokens.DeleteProjectDeployToken returned an error: %v", err)
	}

	want := http.StatusAccepted
	got := resp.StatusCode

	if want != got {
		t.Errorf("DeployTokens.DeleteProjectDeployToken returned %+v, want %+v", got, want)
	}
}

func TestListGroupDeployTokens(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1/deploy_tokens", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprint(w, `
[
  {
    "id": 1,
    "name": "MyToken",
    "username": "gitlab+deploy-token-1",
    "expires_at": "2020-02-14T00:00:00.000Z",
    "scopes": [
      "read_repository",
      "read_registry"
    ]
  }
]
`)
	})

	deployTokens, _, err := client.DeployTokens.ListGroupDeployTokens(1, nil)
	if err != nil {
		t.Errorf("DeployTokens.ListGroupDeployTokens returned an error: %v", err)
	}

	wantExpiresAt := time.Date(2020, 0o2, 14, 0, 0, 0, 0, time.UTC)

	want := []*DeployToken{
		{
			ID:        1,
			Name:      "MyToken",
			Username:  "gitlab+deploy-token-1",
			ExpiresAt: &wantExpiresAt,
			Scopes: []string{
				"read_repository",
				"read_registry",
			},
		},
	}

	if !reflect.DeepEqual(want, deployTokens) {
		t.Errorf("DeployTokens.ListGroupDeployTokens returned %+v, want %+v", deployTokens, want)
	}
}

func TestGetGroupDeployTokens(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1/deploy_tokens/1", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprint(w, `
{
  "id": 1,
  "name": "MyToken",
  "username": "gitlab+deploy-token-1",
  "expires_at": "2020-02-14T00:00:00.000Z",
  "scopes": [
    "read_repository",
    "read_registry"
  ]
}
`)
	})

	deployToken, _, err := client.DeployTokens.GetGroupDeployToken(1, 1)
	if err != nil {
		t.Errorf("DeployTokens.GetGroupDeployToken returned an error: %v", err)
	}

	wantExpiresAt := time.Date(2020, 0o2, 14, 0, 0, 0, 0, time.UTC)

	want := &DeployToken{
		ID:        1,
		Name:      "MyToken",
		Username:  "gitlab+deploy-token-1",
		ExpiresAt: &wantExpiresAt,
		Scopes: []string{
			"read_repository",
			"read_registry",
		},
	}

	if !reflect.DeepEqual(want, deployToken) {
		t.Errorf("DeployTokens.GetGroupDeployToken returned %+v, want %+v", deployToken, want)
	}
}

func TestCreateGroupDeployToken(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/5/deploy_tokens", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		fmt.Fprint(w, `
{
	"id": 1,
	"name": "My deploy token",
	"username": "custom-user",
	"expires_at": "2021-01-01T00:00:00.000Z",
	"token": "jMRvtPNxrn3crTAGukpZ",
	"scopes": [
		"read_repository"
	]
}
`)
	})

	expiresAt := time.Date(2021, 0o1, 0o1, 0, 0, 0, 0, time.UTC)

	deployToken, _, err := client.DeployTokens.CreateGroupDeployToken(5, &CreateGroupDeployTokenOptions{
		Name:      Ptr("My deploy token"),
		Username:  Ptr("custom-user"),
		ExpiresAt: &expiresAt,
		Scopes: &[]string{
			"read_repository",
		},
	})
	if err != nil {
		t.Errorf("DeployTokens.CreateGroupDeployToken returned an error: %v", err)
	}

	want := &DeployToken{
		ID:        1,
		Name:      "My deploy token",
		Username:  "custom-user",
		ExpiresAt: &expiresAt,
		Token:     "jMRvtPNxrn3crTAGukpZ",
		Scopes: []string{
			"read_repository",
		},
	}

	if !reflect.DeepEqual(want, deployToken) {
		t.Errorf("DeployTokens.CreateGroupDeployToken returned %+v, want %+v", deployToken, want)
	}
}

func TestDeleteGroupDeployToken(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/5/deploy_tokens/13", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodDelete)
		w.WriteHeader(http.StatusAccepted)
	})

	resp, err := client.DeployTokens.DeleteGroupDeployToken(5, 13)
	if err != nil {
		t.Errorf("DeployTokens.DeleteGroupDeployToken returned an error: %v", err)
	}

	want := http.StatusAccepted
	got := resp.StatusCode

	if want != got {
		t.Errorf("DeployTokens.DeleteGroupDeployToken returned %+v, want %+v", got, want)
	}
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/deployments.go000066400000000000000000000212241475761473200237520ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package gitlab

import (
	"fmt"
	"net/http"
	"time"
)

// DeploymentsService handles communication with the deployment related methods
// of the GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/deployments.html
type DeploymentsService struct {
	client *Client
}

// Deployment represents the Gitlab deployment
type Deployment struct {
	ID          int          `json:"id"`
	IID         int          `json:"iid"`
	Ref         string       `json:"ref"`
	SHA         string       `json:"sha"`
	Status      string       `json:"status"`
	CreatedAt   *time.Time   `json:"created_at"`
	UpdatedAt   *time.Time   `json:"updated_at"`
	User        *ProjectUser `json:"user"`
	Environment *Environment `json:"environment"`
	Deployable  struct {
		ID         int        `json:"id"`
		Status     string     `json:"status"`
		Stage      string     `json:"stage"`
		Name       string     `json:"name"`
		Ref        string     `json:"ref"`
		Tag        bool       `json:"tag"`
		Coverage   float64    `json:"coverage"`
		CreatedAt  *time.Time `json:"created_at"`
		StartedAt  *time.Time `json:"started_at"`
		FinishedAt *time.Time `json:"finished_at"`
		Duration   float64    `json:"duration"`
		User       *User      `json:"user"`
		Commit     *Commit    `json:"commit"`
		Pipeline   struct {
			ID        int        `json:"id"`
			SHA       string     `json:"sha"`
			Ref       string     `json:"ref"`
			Status    string     `json:"status"`
			CreatedAt *time.Time `json:"created_at"`
			UpdatedAt *time.Time `json:"updated_at"`
		} `json:"pipeline"`
		Runner *Runner `json:"runner"`
	} `json:"deployable"`
}

// ListProjectDeploymentsOptions represents the available ListProjectDeployments() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/deployments.html#list-project-deployments
type ListProjectDeploymentsOptions struct {
	ListOptions
	OrderBy     *string `url:"order_by,omitempty" json:"order_by,omitempty"`
	Sort        *string `url:"sort,omitempty" json:"sort,omitempty"`
	Environment *string `url:"environment,omitempty" json:"environment,omitempty"`
	Status      *string `url:"status,omitempty" json:"status,omitempty"`

	// Only for Gitlab versions less than 14
	UpdatedAfter  *time.Time `url:"updated_after,omitempty" json:"updated_after,omitempty"`
	UpdatedBefore *time.Time `url:"updated_before,omitempty" json:"updated_before,omitempty"`

	// Only for Gitlab 14 or higher
	FinishedAfter  *time.Time `url:"finished_after,omitempty" json:"finished_after,omitempty"`
	FinishedBefore *time.Time `url:"finished_before,omitempty" json:"finished_before,omitempty"`
}

// ListProjectDeployments gets a list of deployments in a project.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/deployments.html#list-project-deployments
func (s *DeploymentsService) ListProjectDeployments(pid interface{}, opts *ListProjectDeploymentsOptions, options ...RequestOptionFunc) ([]*Deployment, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/deployments", PathEscape(project))

	req, err := s.client.NewRequest(http.MethodGet, u, opts, options)
	if err != nil {
		return nil, nil, err
	}

	var ds []*Deployment
	resp, err := s.client.Do(req, &ds)
	if err != nil {
		return nil, resp, err
	}

	return ds, resp, nil
}

// GetProjectDeployment get a deployment for a project.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/deployments.html#get-a-specific-deployment
func (s *DeploymentsService) GetProjectDeployment(pid interface{}, deployment int, options ...RequestOptionFunc) (*Deployment, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/deployments/%d", PathEscape(project), deployment)

	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	d := new(Deployment)
	resp, err := s.client.Do(req, d)
	if err != nil {
		return nil, resp, err
	}

	return d, resp, nil
}

// CreateProjectDeploymentOptions represents the available
// CreateProjectDeployment() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/deployments.html#create-a-deployment
type CreateProjectDeploymentOptions struct {
	Environment *string                `url:"environment,omitempty" json:"environment,omitempty"`
	Ref         *string                `url:"ref,omitempty" json:"ref,omitempty"`
	SHA         *string                `url:"sha,omitempty" json:"sha,omitempty"`
	Tag         *bool                  `url:"tag,omitempty" json:"tag,omitempty"`
	Status      *DeploymentStatusValue `url:"status,omitempty" json:"status,omitempty"`
}

// CreateProjectDeployment creates a project deployment.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/deployments.html#create-a-deployment
func (s *DeploymentsService) CreateProjectDeployment(pid interface{}, opt *CreateProjectDeploymentOptions, options ...RequestOptionFunc) (*Deployment, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/deployments", PathEscape(project))

	req, err := s.client.NewRequest(http.MethodPost, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	d := new(Deployment)
	resp, err := s.client.Do(req, &d)
	if err != nil {
		return nil, resp, err
	}

	return d, resp, nil
}

// UpdateProjectDeploymentOptions represents the available
// UpdateProjectDeployment() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/deployments.html#update-a-deployment
type UpdateProjectDeploymentOptions struct {
	Status *DeploymentStatusValue `url:"status,omitempty" json:"status,omitempty"`
}

// UpdateProjectDeployment updates a project deployment.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/deployments.html#update-a-deployment
func (s *DeploymentsService) UpdateProjectDeployment(pid interface{}, deployment int, opt *UpdateProjectDeploymentOptions, options ...RequestOptionFunc) (*Deployment, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/deployments/%d", PathEscape(project), deployment)

	req, err := s.client.NewRequest(http.MethodPut, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	d := new(Deployment)
	resp, err := s.client.Do(req, &d)
	if err != nil {
		return nil, resp, err
	}

	return d, resp, nil
}

// ApproveOrRejectProjectDeploymentOptions represents the available
// ApproveOrRejectProjectDeployment() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/deployments.html#approve-or-reject-a-blocked-deployment
type ApproveOrRejectProjectDeploymentOptions struct {
	Status        *DeploymentApprovalStatus `url:"status,omitempty" json:"status,omitempty"`
	Comment       *string                   `url:"comment,omitempty" json:"comment,omitempty"`
	RepresentedAs *string                   `url:"represented_as,omitempty" json:"represented_as,omitempty"`
}

// ApproveOrRejectProjectDeployment approve or reject a blocked deployment.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/deployments.html#approve-or-reject-a-blocked-deployment
func (s *DeploymentsService) ApproveOrRejectProjectDeployment(pid interface{}, deployment int,
	opt *ApproveOrRejectProjectDeploymentOptions, options ...RequestOptionFunc,
) (*Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("projects/%s/deployments/%d/approval", PathEscape(project), deployment)

	req, err := s.client.NewRequest(http.MethodPost, u, opt, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}

// DeleteProjectDeployment delete a project deployment.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/deployments.html#delete-a-specific-deployment
func (s *DeploymentsService) DeleteProjectDeployment(pid interface{}, deployment int, options ...RequestOptionFunc) (*Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("projects/%s/deployments/%d", PathEscape(project), deployment)

	req, err := s.client.NewRequest(http.MethodDelete, u, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/deployments_merge_requests.go000066400000000000000000000033711475761473200270670ustar00rootroot00000000000000// Copyright 2022, Daniela Filipe Bento
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//	http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package gitlab

import (
	"fmt"
	"net/http"
)

// DeploymentMergeRequestsService handles communication with the deployment's
// merge requests related methods of the GitLab API.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/deployments.html#list-of-merge-requests-associated-with-a-deployment
type DeploymentMergeRequestsService struct {
	client *Client
}

// ListDeploymentMergeRequests get the merge requests associated with deployment.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/deployments.html#list-of-merge-requests-associated-with-a-deployment
func (s *DeploymentMergeRequestsService) ListDeploymentMergeRequests(pid interface{}, deployment int, opts *ListMergeRequestsOptions, options ...RequestOptionFunc) ([]*MergeRequest, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/deployments/%d/merge_requests", PathEscape(project), deployment)

	req, err := s.client.NewRequest(http.MethodGet, u, opts, options)
	if err != nil {
		return nil, nil, err
	}

	var mrs []*MergeRequest
	resp, err := s.client.Do(req, &mrs)
	if err != nil {
		return nil, resp, err
	}

	return mrs, resp, nil
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/deployments_merge_requests_test.go000066400000000000000000000037511475761473200301300ustar00rootroot00000000000000package gitlab

import (
	"net/http"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestDeploymentMergeRequestsService_ListDeploymentMergeRequests(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/278964/deployments/2/merge_requests", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		testParams(t, r, "assignee_id=Any&with_labels_details=true&with_merge_status_recheck=true")
		mustWriteHTTPResponse(t, w, "testdata/get_merge_requests.json")
	})

	opts := ListMergeRequestsOptions{
		AssigneeID:             AssigneeID(UserIDAny),
		WithLabelsDetails:      Ptr(true),
		WithMergeStatusRecheck: Ptr(true),
	}

	mergeRequests, _, err := client.DeploymentMergeRequests.ListDeploymentMergeRequests(278964, 2, &opts)
	require.NoError(t, err)
	require.Equal(t, 3, len(mergeRequests))

	validStates := []string{"opened", "closed", "locked", "merged"}
	detailedMergeStatuses := []string{
		"blocked_status",
		"broken_status",
		"checking",
		"ci_must_pass",
		"ci_still_running",
		"discussions_not_resolved",
		"draft_status",
		"external_status_checks",
		"mergeable",
		"not_approved",
		"not_open",
		"policies_denied",
		"unchecked",
	}
	allCreatedBefore := time.Date(2019, 8, 21, 0, 0, 0, 0, time.UTC)
	allCreatedAfter := time.Date(2019, 8, 17, 0, 0, 0, 0, time.UTC)

	for _, mr := range mergeRequests {
		require.Equal(t, 278964, mr.ProjectID)
		require.Contains(t, validStates, mr.State)
		assert.Less(t, mr.CreatedAt.Unix(), allCreatedBefore.Unix())
		assert.Greater(t, mr.CreatedAt.Unix(), allCreatedAfter.Unix())
		assert.LessOrEqual(t, mr.CreatedAt.Unix(), mr.UpdatedAt.Unix())
		assert.LessOrEqual(t, mr.TaskCompletionStatus.CompletedCount, mr.TaskCompletionStatus.Count)
		require.Contains(t, detailedMergeStatuses, mr.DetailedMergeStatus)

		// list requests do not provide these fields:
		assert.Nil(t, mr.Pipeline)
		assert.Nil(t, mr.HeadPipeline)
		assert.Equal(t, "", mr.DiffRefs.HeadSha)
	}
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/deployments_test.go000066400000000000000000000554711475761473200250240ustar00rootroot00000000000000package gitlab

import (
	"fmt"
	"net/http"
	"testing"
	"time"

	"github.com/stretchr/testify/require"
)

func TestDeploymentsService_ListProjectDeployments(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/deployments", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
			[
			  {
				"status": "created",
				"deployable": {
				  "commit": {
					"author_email": "admin@example.com",
					"author_name": "Administrator",
					"id": "99d03678b90d914dbb1b109132516d71a4a03ea8",
					"message": "Merge branch 'new-title' into 'main'\r\n\r\nUpdate README\r\n\r\n\r\n\r\nSee merge request !1",
					"short_id": "99d03678",
					"title": "Merge branch 'new-title' into 'main'\r"
				  },
				  "coverage": null,
				  "id": 657,
				  "name": "deploy",
				  "ref": "main",
				  "runner": null,
				  "stage": "deploy",
				  "status": "success",
				  "tag": false,
				  "user": {
					"id": 1,
					"name": "Administrator",
					"username": "root",
					"state": "active",
					"avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
					"web_url": "http://gitlab.dev/root",
					"bio": null,
					"location": null,
					"public_email": "",
					"skype": "",
					"linkedin": "",
					"twitter": "",
					"website_url": "",
					"organization": ""
				  },
				  "pipeline": {
					"id": 36,
					"ref": "main",
					"sha": "99d03678b90d914dbb1b109132516d71a4a03ea8",
					"status": "success",
					"web_url": "http://gitlab.dev/root/project/pipelines/12"
				  }
				},
				"environment": {
				  "external_url": "https://about.gitlab.com",
				  "id": 9,
				  "name": "production"
				},
				"id": 41,
				"iid": 1,
				"ref": "main",
				"sha": "99d03678b90d914dbb1b109132516d71a4a03ea8",
				"user": {
				  "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
				  "id": 1,
				  "name": "Administrator",
				  "state": "active",
				  "username": "root",
				  "web_url": "http://localhost:3000/root"
				}
			  }
			]
		`)
	})

	want := []*Deployment{{
		ID:     41,
		IID:    1,
		Ref:    "main",
		SHA:    "99d03678b90d914dbb1b109132516d71a4a03ea8",
		Status: "created",
		User: &ProjectUser{
			ID:        1,
			Name:      "Administrator",
			Username:  "root",
			State:     "active",
			AvatarURL: "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
			WebURL:    "http://localhost:3000/root",
		},
		Environment: &Environment{
			ID:             9,
			Name:           "production",
			Slug:           "",
			State:          "",
			ExternalURL:    "https://about.gitlab.com",
			Project:        nil,
			LastDeployment: nil,
		},
		Deployable: struct {
			ID         int        `json:"id"`
			Status     string     `json:"status"`
			Stage      string     `json:"stage"`
			Name       string     `json:"name"`
			Ref        string     `json:"ref"`
			Tag        bool       `json:"tag"`
			Coverage   float64    `json:"coverage"`
			CreatedAt  *time.Time `json:"created_at"`
			StartedAt  *time.Time `json:"started_at"`
			FinishedAt *time.Time `json:"finished_at"`
			Duration   float64    `json:"duration"`
			User       *User      `json:"user"`
			Commit     *Commit    `json:"commit"`
			Pipeline   struct {
				ID        int        `json:"id"`
				SHA       string     `json:"sha"`
				Ref       string     `json:"ref"`
				Status    string     `json:"status"`
				CreatedAt *time.Time `json:"created_at"`
				UpdatedAt *time.Time `json:"updated_at"`
			} `json:"pipeline"`
			Runner *Runner `json:"runner"`
		}{
			ID:         657,
			Status:     "success",
			Stage:      "deploy",
			Name:       "deploy",
			Ref:        "main",
			Tag:        false,
			Coverage:   0,
			CreatedAt:  nil,
			StartedAt:  nil,
			FinishedAt: nil,
			Duration:   0,
			User: &User{
				ID:        1,
				Name:      "Administrator",
				Username:  "root",
				State:     "active",
				AvatarURL: "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
				WebURL:    "http://gitlab.dev/root",
			},
			Commit: &Commit{
				ID:             "99d03678b90d914dbb1b109132516d71a4a03ea8",
				ShortID:        "99d03678",
				Title:          "Merge branch 'new-title' into 'main'\r",
				AuthorName:     "Administrator",
				AuthorEmail:    "admin@example.com",
				AuthoredDate:   nil,
				CommitterName:  "",
				CommitterEmail: "",
				CommittedDate:  nil,
				CreatedAt:      nil,
				Message:        "Merge branch 'new-title' into 'main'\r\n\r\nUpdate README\r\n\r\n\r\n\r\nSee merge request !1",
				ParentIDs:      nil,
				Stats:          nil,
				Status:         nil,
				LastPipeline:   nil,
				ProjectID:      0,
				WebURL:         "",
			},
			Pipeline: struct {
				ID        int        `json:"id"`
				SHA       string     `json:"sha"`
				Ref       string     `json:"ref"`
				Status    string     `json:"status"`
				CreatedAt *time.Time `json:"created_at"`
				UpdatedAt *time.Time `json:"updated_at"`
			}{
				ID:        36,
				SHA:       "99d03678b90d914dbb1b109132516d71a4a03ea8",
				Ref:       "main",
				Status:    "success",
				CreatedAt: nil,
				UpdatedAt: nil,
			},
			Runner: nil,
		},
	}}

	ds, resp, err := client.Deployments.ListProjectDeployments(1, nil, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, ds)

	ds, resp, err = client.Deployments.ListProjectDeployments(1.01, nil, nil)
	require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, ds)

	ds, resp, err = client.Deployments.ListProjectDeployments(1, nil, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, ds)

	ds, resp, err = client.Deployments.ListProjectDeployments(3, nil, nil)
	require.Error(t, err)
	require.Nil(t, ds)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestDeploymentsService_GetProjectDeployment(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/deployments/1", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
		  {
			"status": "created",
			"deployable": {
			  "commit": {
				"author_email": "admin@example.com",
				"author_name": "Administrator",
				"id": "99d03678b90d914dbb1b109132516d71a4a03ea8",
				"message": "Merge branch 'new-title' into 'main'\r\n\r\nUpdate README\r\n\r\n\r\n\r\nSee merge request !1",
				"short_id": "99d03678",
				"title": "Merge branch 'new-title' into 'main'\r"
			  },
			  "coverage": null,
			  "id": 657,
			  "name": "deploy",
			  "ref": "main",
			  "runner": null,
			  "stage": "deploy",
			  "status": "success",
			  "tag": false,
			  "user": {
				"id": 1,
				"name": "Administrator",
				"username": "root",
				"state": "active",
				"avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
				"web_url": "http://gitlab.dev/root",
				"bio": null,
				"location": null,
				"public_email": "",
				"skype": "",
				"linkedin": "",
				"twitter": "",
				"website_url": "",
				"organization": ""
			  },
			  "pipeline": {
				"id": 36,
				"ref": "main",
				"sha": "99d03678b90d914dbb1b109132516d71a4a03ea8",
				"status": "success",
				"web_url": "http://gitlab.dev/root/project/pipelines/12"
			  }
			},
			"environment": {
			  "external_url": "https://about.gitlab.com",
			  "id": 9,
			  "name": "production"
			},
			"id": 41,
			"iid": 1,
			"ref": "main",
			"sha": "99d03678b90d914dbb1b109132516d71a4a03ea8",
			"user": {
			  "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
			  "id": 1,
			  "name": "Administrator",
			  "state": "active",
			  "username": "root",
			  "web_url": "http://localhost:3000/root"
			}
		  }
		`)
	})

	want := &Deployment{
		ID:     41,
		IID:    1,
		Ref:    "main",
		SHA:    "99d03678b90d914dbb1b109132516d71a4a03ea8",
		Status: "created",
		User: &ProjectUser{
			ID:        1,
			Name:      "Administrator",
			Username:  "root",
			State:     "active",
			AvatarURL: "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
			WebURL:    "http://localhost:3000/root",
		},
		Environment: &Environment{
			ID:             9,
			Name:           "production",
			Slug:           "",
			State:          "",
			ExternalURL:    "https://about.gitlab.com",
			Project:        nil,
			LastDeployment: nil,
		},
		Deployable: struct {
			ID         int        `json:"id"`
			Status     string     `json:"status"`
			Stage      string     `json:"stage"`
			Name       string     `json:"name"`
			Ref        string     `json:"ref"`
			Tag        bool       `json:"tag"`
			Coverage   float64    `json:"coverage"`
			CreatedAt  *time.Time `json:"created_at"`
			StartedAt  *time.Time `json:"started_at"`
			FinishedAt *time.Time `json:"finished_at"`
			Duration   float64    `json:"duration"`
			User       *User      `json:"user"`
			Commit     *Commit    `json:"commit"`
			Pipeline   struct {
				ID        int        `json:"id"`
				SHA       string     `json:"sha"`
				Ref       string     `json:"ref"`
				Status    string     `json:"status"`
				CreatedAt *time.Time `json:"created_at"`
				UpdatedAt *time.Time `json:"updated_at"`
			} `json:"pipeline"`
			Runner *Runner `json:"runner"`
		}{
			ID:         657,
			Status:     "success",
			Stage:      "deploy",
			Name:       "deploy",
			Ref:        "main",
			Tag:        false,
			Coverage:   0,
			CreatedAt:  nil,
			StartedAt:  nil,
			FinishedAt: nil,
			Duration:   0,
			User: &User{
				ID:        1,
				Name:      "Administrator",
				Username:  "root",
				State:     "active",
				AvatarURL: "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
				WebURL:    "http://gitlab.dev/root",
			},
			Commit: &Commit{
				ID:             "99d03678b90d914dbb1b109132516d71a4a03ea8",
				ShortID:        "99d03678",
				Title:          "Merge branch 'new-title' into 'main'\r",
				AuthorName:     "Administrator",
				AuthorEmail:    "admin@example.com",
				AuthoredDate:   nil,
				CommitterName:  "",
				CommitterEmail: "",
				CommittedDate:  nil,
				CreatedAt:      nil,
				Message:        "Merge branch 'new-title' into 'main'\r\n\r\nUpdate README\r\n\r\n\r\n\r\nSee merge request !1",
				ParentIDs:      nil,
				Stats:          nil,
				Status:         nil,
				LastPipeline:   nil,
				ProjectID:      0,
				WebURL:         "",
			},
			Pipeline: struct {
				ID        int        `json:"id"`
				SHA       string     `json:"sha"`
				Ref       string     `json:"ref"`
				Status    string     `json:"status"`
				CreatedAt *time.Time `json:"created_at"`
				UpdatedAt *time.Time `json:"updated_at"`
			}{
				ID:        36,
				SHA:       "99d03678b90d914dbb1b109132516d71a4a03ea8",
				Ref:       "main",
				Status:    "success",
				CreatedAt: nil,
				UpdatedAt: nil,
			},
			Runner: nil,
		},
	}

	d, resp, err := client.Deployments.GetProjectDeployment(1, 1, nil, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, d)

	d, resp, err = client.Deployments.GetProjectDeployment(1.01, 1, nil, nil)
	require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, d)

	d, resp, err = client.Deployments.GetProjectDeployment(1, 1, nil, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, d)

	d, resp, err = client.Deployments.GetProjectDeployment(3, 1, nil, nil)
	require.Error(t, err)
	require.Nil(t, d)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestDeploymentsService_CreateProjectDeployment(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/deployments", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		fmt.Fprintf(w, `
		  {
			"status": "created",
			"deployable": {
			  "commit": {
				"author_email": "admin@example.com",
				"author_name": "Administrator",
				"id": "99d03678b90d914dbb1b109132516d71a4a03ea8",
				"message": "Merge branch 'new-title' into 'main'\r\n\r\nUpdate README\r\n\r\n\r\n\r\nSee merge request !1",
				"short_id": "99d03678",
				"title": "Merge branch 'new-title' into 'main'\r"
			  },
			  "coverage": null,
			  "id": 657,
			  "name": "deploy",
			  "ref": "main",
			  "runner": null,
			  "stage": "deploy",
			  "status": "success",
			  "tag": false,
			  "user": {
				"id": 1,
				"name": "Administrator",
				"username": "root",
				"state": "active",
				"avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
				"web_url": "http://gitlab.dev/root",
				"bio": null,
				"location": null,
				"public_email": "",
				"skype": "",
				"linkedin": "",
				"twitter": "",
				"website_url": "",
				"organization": ""
			  },
			  "pipeline": {
				"id": 36,
				"ref": "main",
				"sha": "99d03678b90d914dbb1b109132516d71a4a03ea8",
				"status": "success",
				"web_url": "http://gitlab.dev/root/project/pipelines/12"
			  }
			},
			"environment": {
			  "external_url": "https://about.gitlab.com",
			  "id": 9,
			  "name": "production"
			},
			"id": 41,
			"iid": 1,
			"ref": "main",
			"sha": "99d03678b90d914dbb1b109132516d71a4a03ea8",
			"user": {
			  "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
			  "id": 1,
			  "name": "Administrator",
			  "state": "active",
			  "username": "root",
			  "web_url": "http://localhost:3000/root"
			}
		  }
		`)
	})

	want := &Deployment{
		ID:     41,
		IID:    1,
		Ref:    "main",
		SHA:    "99d03678b90d914dbb1b109132516d71a4a03ea8",
		Status: "created",
		User: &ProjectUser{
			ID:        1,
			Name:      "Administrator",
			Username:  "root",
			State:     "active",
			AvatarURL: "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
			WebURL:    "http://localhost:3000/root",
		},
		Environment: &Environment{
			ID:             9,
			Name:           "production",
			Slug:           "",
			State:          "",
			ExternalURL:    "https://about.gitlab.com",
			Project:        nil,
			LastDeployment: nil,
		},
		Deployable: struct {
			ID         int        `json:"id"`
			Status     string     `json:"status"`
			Stage      string     `json:"stage"`
			Name       string     `json:"name"`
			Ref        string     `json:"ref"`
			Tag        bool       `json:"tag"`
			Coverage   float64    `json:"coverage"`
			CreatedAt  *time.Time `json:"created_at"`
			StartedAt  *time.Time `json:"started_at"`
			FinishedAt *time.Time `json:"finished_at"`
			Duration   float64    `json:"duration"`
			User       *User      `json:"user"`
			Commit     *Commit    `json:"commit"`
			Pipeline   struct {
				ID        int        `json:"id"`
				SHA       string     `json:"sha"`
				Ref       string     `json:"ref"`
				Status    string     `json:"status"`
				CreatedAt *time.Time `json:"created_at"`
				UpdatedAt *time.Time `json:"updated_at"`
			} `json:"pipeline"`
			Runner *Runner `json:"runner"`
		}{
			ID:         657,
			Status:     "success",
			Stage:      "deploy",
			Name:       "deploy",
			Ref:        "main",
			Tag:        false,
			Coverage:   0,
			CreatedAt:  nil,
			StartedAt:  nil,
			FinishedAt: nil,
			Duration:   0,
			User: &User{
				ID:        1,
				Name:      "Administrator",
				Username:  "root",
				State:     "active",
				AvatarURL: "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
				WebURL:    "http://gitlab.dev/root",
			},
			Commit: &Commit{
				ID:             "99d03678b90d914dbb1b109132516d71a4a03ea8",
				ShortID:        "99d03678",
				Title:          "Merge branch 'new-title' into 'main'\r",
				AuthorName:     "Administrator",
				AuthorEmail:    "admin@example.com",
				AuthoredDate:   nil,
				CommitterName:  "",
				CommitterEmail: "",
				CommittedDate:  nil,
				CreatedAt:      nil,
				Message:        "Merge branch 'new-title' into 'main'\r\n\r\nUpdate README\r\n\r\n\r\n\r\nSee merge request !1",
				ParentIDs:      nil,
				Stats:          nil,
				Status:         nil,
				LastPipeline:   nil,
				ProjectID:      0,
				WebURL:         "",
			},
			Pipeline: struct {
				ID        int        `json:"id"`
				SHA       string     `json:"sha"`
				Ref       string     `json:"ref"`
				Status    string     `json:"status"`
				CreatedAt *time.Time `json:"created_at"`
				UpdatedAt *time.Time `json:"updated_at"`
			}{
				ID:        36,
				SHA:       "99d03678b90d914dbb1b109132516d71a4a03ea8",
				Ref:       "main",
				Status:    "success",
				CreatedAt: nil,
				UpdatedAt: nil,
			},
			Runner: nil,
		},
	}

	d, resp, err := client.Deployments.CreateProjectDeployment(1, nil, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, d)

	d, resp, err = client.Deployments.CreateProjectDeployment(1.01, nil, nil)
	require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, d)

	d, resp, err = client.Deployments.CreateProjectDeployment(1, nil, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, d)

	d, resp, err = client.Deployments.CreateProjectDeployment(3, nil, nil)
	require.Error(t, err)
	require.Nil(t, d)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestDeploymentsService_UpdateProjectDeployment(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/deployments/1", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPut)
		fmt.Fprintf(w, `
		  {
			"status": "created",
			"deployable": {
			  "commit": {
				"author_email": "admin@example.com",
				"author_name": "Administrator",
				"id": "99d03678b90d914dbb1b109132516d71a4a03ea8",
				"message": "Merge branch 'new-title' into 'main'\r\n\r\nUpdate README\r\n\r\n\r\n\r\nSee merge request !1",
				"short_id": "99d03678",
				"title": "Merge branch 'new-title' into 'main'\r"
			  },
			  "coverage": null,
			  "id": 657,
			  "name": "deploy",
			  "ref": "main",
			  "runner": null,
			  "stage": "deploy",
			  "status": "success",
			  "tag": false,
			  "user": {
				"id": 1,
				"name": "Administrator",
				"username": "root",
				"state": "active",
				"avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
				"web_url": "http://gitlab.dev/root",
				"bio": null,
				"location": null,
				"public_email": "",
				"skype": "",
				"linkedin": "",
				"twitter": "",
				"website_url": "",
				"organization": ""
			  },
			  "pipeline": {
				"id": 36,
				"ref": "main",
				"sha": "99d03678b90d914dbb1b109132516d71a4a03ea8",
				"status": "success",
				"web_url": "http://gitlab.dev/root/project/pipelines/12"
			  }
			},
			"environment": {
			  "external_url": "https://about.gitlab.com",
			  "id": 9,
			  "name": "production"
			},
			"id": 41,
			"iid": 1,
			"ref": "main",
			"sha": "99d03678b90d914dbb1b109132516d71a4a03ea8",
			"user": {
			  "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
			  "id": 1,
			  "name": "Administrator",
			  "state": "active",
			  "username": "root",
			  "web_url": "http://localhost:3000/root"
			}
		  }
		`)
	})

	want := &Deployment{
		ID:     41,
		IID:    1,
		Ref:    "main",
		SHA:    "99d03678b90d914dbb1b109132516d71a4a03ea8",
		Status: "created",
		User: &ProjectUser{
			ID:        1,
			Name:      "Administrator",
			Username:  "root",
			State:     "active",
			AvatarURL: "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
			WebURL:    "http://localhost:3000/root",
		},
		Environment: &Environment{
			ID:             9,
			Name:           "production",
			Slug:           "",
			State:          "",
			ExternalURL:    "https://about.gitlab.com",
			Project:        nil,
			LastDeployment: nil,
		},
		Deployable: struct {
			ID         int        `json:"id"`
			Status     string     `json:"status"`
			Stage      string     `json:"stage"`
			Name       string     `json:"name"`
			Ref        string     `json:"ref"`
			Tag        bool       `json:"tag"`
			Coverage   float64    `json:"coverage"`
			CreatedAt  *time.Time `json:"created_at"`
			StartedAt  *time.Time `json:"started_at"`
			FinishedAt *time.Time `json:"finished_at"`
			Duration   float64    `json:"duration"`
			User       *User      `json:"user"`
			Commit     *Commit    `json:"commit"`
			Pipeline   struct {
				ID        int        `json:"id"`
				SHA       string     `json:"sha"`
				Ref       string     `json:"ref"`
				Status    string     `json:"status"`
				CreatedAt *time.Time `json:"created_at"`
				UpdatedAt *time.Time `json:"updated_at"`
			} `json:"pipeline"`
			Runner *Runner `json:"runner"`
		}{
			ID:         657,
			Status:     "success",
			Stage:      "deploy",
			Name:       "deploy",
			Ref:        "main",
			Tag:        false,
			Coverage:   0,
			CreatedAt:  nil,
			StartedAt:  nil,
			FinishedAt: nil,
			Duration:   0,
			User: &User{
				ID:        1,
				Name:      "Administrator",
				Username:  "root",
				State:     "active",
				AvatarURL: "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
				WebURL:    "http://gitlab.dev/root",
			},
			Commit: &Commit{
				ID:             "99d03678b90d914dbb1b109132516d71a4a03ea8",
				ShortID:        "99d03678",
				Title:          "Merge branch 'new-title' into 'main'\r",
				AuthorName:     "Administrator",
				AuthorEmail:    "admin@example.com",
				AuthoredDate:   nil,
				CommitterName:  "",
				CommitterEmail: "",
				CommittedDate:  nil,
				CreatedAt:      nil,
				Message:        "Merge branch 'new-title' into 'main'\r\n\r\nUpdate README\r\n\r\n\r\n\r\nSee merge request !1",
				ParentIDs:      nil,
				Stats:          nil,
				Status:         nil,
				LastPipeline:   nil,
				ProjectID:      0,
				WebURL:         "",
			},
			Pipeline: struct {
				ID        int        `json:"id"`
				SHA       string     `json:"sha"`
				Ref       string     `json:"ref"`
				Status    string     `json:"status"`
				CreatedAt *time.Time `json:"created_at"`
				UpdatedAt *time.Time `json:"updated_at"`
			}{
				ID:        36,
				SHA:       "99d03678b90d914dbb1b109132516d71a4a03ea8",
				Ref:       "main",
				Status:    "success",
				CreatedAt: nil,
				UpdatedAt: nil,
			},
			Runner: nil,
		},
	}

	d, resp, err := client.Deployments.UpdateProjectDeployment(1, 1, nil, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, d)

	d, resp, err = client.Deployments.UpdateProjectDeployment(1.01, 1, nil, nil)
	require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, d)

	d, resp, err = client.Deployments.UpdateProjectDeployment(1, 1, nil, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, d)

	d, resp, err = client.Deployments.UpdateProjectDeployment(3, 1, nil, nil)
	require.Error(t, err)
	require.Nil(t, d)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/discussions.go000066400000000000000000001046061475761473200237630ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"fmt"
	"net/http"
	"time"
)

// DiscussionsService handles communication with the discussions related
// methods of the GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/discussions.html
type DiscussionsService struct {
	client *Client
}

// Discussion represents a GitLab discussion.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/discussions.html
type Discussion struct {
	ID             string  `json:"id"`
	IndividualNote bool    `json:"individual_note"`
	Notes          []*Note `json:"notes"`
}

func (d Discussion) String() string {
	return Stringify(d)
}

// ListIssueDiscussionsOptions represents the available ListIssueDiscussions()
// options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/discussions.html#list-project-issue-discussion-items
type ListIssueDiscussionsOptions ListOptions

// ListIssueDiscussions gets a list of all discussions for a single
// issue.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/discussions.html#list-project-issue-discussion-items
func (s *DiscussionsService) ListIssueDiscussions(pid interface{}, issue int, opt *ListIssueDiscussionsOptions, options ...RequestOptionFunc) ([]*Discussion, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/issues/%d/discussions", PathEscape(project), issue)

	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var ds []*Discussion
	resp, err := s.client.Do(req, &ds)
	if err != nil {
		return nil, resp, err
	}

	return ds, resp, nil
}

// GetIssueDiscussion returns a single discussion for a specific project issue.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/discussions.html#get-single-issue-discussion-item
func (s *DiscussionsService) GetIssueDiscussion(pid interface{}, issue int, discussion string, options ...RequestOptionFunc) (*Discussion, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/issues/%d/discussions/%s",
		PathEscape(project),
		issue,
		discussion,
	)

	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	d := new(Discussion)
	resp, err := s.client.Do(req, d)
	if err != nil {
		return nil, resp, err
	}

	return d, resp, nil
}

// CreateIssueDiscussionOptions represents the available CreateIssueDiscussion()
// options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/discussions.html#create-new-issue-thread
type CreateIssueDiscussionOptions struct {
	Body      *string    `url:"body,omitempty" json:"body,omitempty"`
	CreatedAt *time.Time `url:"created_at,omitempty" json:"created_at,omitempty"`
}

// CreateIssueDiscussion creates a new discussion to a single project issue.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/discussions.html#create-new-issue-thread
func (s *DiscussionsService) CreateIssueDiscussion(pid interface{}, issue int, opt *CreateIssueDiscussionOptions, options ...RequestOptionFunc) (*Discussion, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/issues/%d/discussions", PathEscape(project), issue)

	req, err := s.client.NewRequest(http.MethodPost, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	d := new(Discussion)
	resp, err := s.client.Do(req, d)
	if err != nil {
		return nil, resp, err
	}

	return d, resp, nil
}

// AddIssueDiscussionNoteOptions represents the available AddIssueDiscussionNote()
// options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/discussions.html#add-note-to-existing-issue-thread
type AddIssueDiscussionNoteOptions struct {
	Body      *string    `url:"body,omitempty" json:"body,omitempty"`
	CreatedAt *time.Time `url:"created_at,omitempty" json:"created_at,omitempty"`
}

// AddIssueDiscussionNote creates a new discussion to a single project issue.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/discussions.html#add-note-to-existing-issue-thread
func (s *DiscussionsService) AddIssueDiscussionNote(pid interface{}, issue int, discussion string, opt *AddIssueDiscussionNoteOptions, options ...RequestOptionFunc) (*Note, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/issues/%d/discussions/%s/notes",
		PathEscape(project),
		issue,
		discussion,
	)

	req, err := s.client.NewRequest(http.MethodPost, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	n := new(Note)
	resp, err := s.client.Do(req, n)
	if err != nil {
		return nil, resp, err
	}

	return n, resp, nil
}

// UpdateIssueDiscussionNoteOptions represents the available
// UpdateIssueDiscussion() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/discussions.html#modify-existing-issue-thread-note
type UpdateIssueDiscussionNoteOptions struct {
	Body      *string    `url:"body,omitempty" json:"body,omitempty"`
	CreatedAt *time.Time `url:"created_at,omitempty" json:"created_at,omitempty"`
}

// UpdateIssueDiscussionNote modifies existing discussion of an issue.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/discussions.html#modify-existing-issue-thread-note
func (s *DiscussionsService) UpdateIssueDiscussionNote(pid interface{}, issue int, discussion string, note int, opt *UpdateIssueDiscussionNoteOptions, options ...RequestOptionFunc) (*Note, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/issues/%d/discussions/%s/notes/%d",
		PathEscape(project),
		issue,
		discussion,
		note,
	)

	req, err := s.client.NewRequest(http.MethodPut, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	n := new(Note)
	resp, err := s.client.Do(req, n)
	if err != nil {
		return nil, resp, err
	}

	return n, resp, nil
}

// DeleteIssueDiscussionNote deletes an existing discussion of an issue.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/discussions.html#delete-an-issue-thread-note
func (s *DiscussionsService) DeleteIssueDiscussionNote(pid interface{}, issue int, discussion string, note int, options ...RequestOptionFunc) (*Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("projects/%s/issues/%d/discussions/%s/notes/%d",
		PathEscape(project),
		issue,
		discussion,
		note,
	)

	req, err := s.client.NewRequest(http.MethodDelete, u, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}

// ListSnippetDiscussionsOptions represents the available ListSnippetDiscussions()
// options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/discussions.html#list-project-snippet-discussion-items
type ListSnippetDiscussionsOptions ListOptions

// ListSnippetDiscussions gets a list of all discussions for a single
// snippet. Snippet discussions are comments users can post to a snippet.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/discussions.html#list-project-snippet-discussion-items
func (s *DiscussionsService) ListSnippetDiscussions(pid interface{}, snippet int, opt *ListSnippetDiscussionsOptions, options ...RequestOptionFunc) ([]*Discussion, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/snippets/%d/discussions", PathEscape(project), snippet)

	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var ds []*Discussion
	resp, err := s.client.Do(req, &ds)
	if err != nil {
		return nil, resp, err
	}

	return ds, resp, nil
}

// GetSnippetDiscussion returns a single discussion for a given snippet.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/discussions.html#get-single-snippet-discussion-item
func (s *DiscussionsService) GetSnippetDiscussion(pid interface{}, snippet int, discussion string, options ...RequestOptionFunc) (*Discussion, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/snippets/%d/discussions/%s",
		PathEscape(project),
		snippet,
		discussion,
	)

	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	d := new(Discussion)
	resp, err := s.client.Do(req, d)
	if err != nil {
		return nil, resp, err
	}

	return d, resp, nil
}

// CreateSnippetDiscussionOptions represents the available
// CreateSnippetDiscussion() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/discussions.html#create-new-snippet-thread
type CreateSnippetDiscussionOptions struct {
	Body      *string    `url:"body,omitempty" json:"body,omitempty"`
	CreatedAt *time.Time `url:"created_at,omitempty" json:"created_at,omitempty"`
}

// CreateSnippetDiscussion creates a new discussion for a single snippet.
// Snippet discussions are comments users can post to a snippet.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/discussions.html#create-new-snippet-thread
func (s *DiscussionsService) CreateSnippetDiscussion(pid interface{}, snippet int, opt *CreateSnippetDiscussionOptions, options ...RequestOptionFunc) (*Discussion, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/snippets/%d/discussions", PathEscape(project), snippet)

	req, err := s.client.NewRequest(http.MethodPost, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	d := new(Discussion)
	resp, err := s.client.Do(req, d)
	if err != nil {
		return nil, resp, err
	}

	return d, resp, nil
}

// AddSnippetDiscussionNoteOptions represents the available
// AddSnippetDiscussionNote() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/discussions.html#add-note-to-existing-snippet-thread
type AddSnippetDiscussionNoteOptions struct {
	Body      *string    `url:"body,omitempty" json:"body,omitempty"`
	CreatedAt *time.Time `url:"created_at,omitempty" json:"created_at,omitempty"`
}

// AddSnippetDiscussionNote creates a new discussion to a single project
// snippet.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/discussions.html#add-note-to-existing-snippet-thread
func (s *DiscussionsService) AddSnippetDiscussionNote(pid interface{}, snippet int, discussion string, opt *AddSnippetDiscussionNoteOptions, options ...RequestOptionFunc) (*Note, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/snippets/%d/discussions/%s/notes",
		PathEscape(project),
		snippet,
		discussion,
	)

	req, err := s.client.NewRequest(http.MethodPost, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	n := new(Note)
	resp, err := s.client.Do(req, n)
	if err != nil {
		return nil, resp, err
	}

	return n, resp, nil
}

// UpdateSnippetDiscussionNoteOptions represents the available
// UpdateSnippetDiscussion() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/discussions.html#modify-existing-snippet-thread-note
type UpdateSnippetDiscussionNoteOptions struct {
	Body      *string    `url:"body,omitempty" json:"body,omitempty"`
	CreatedAt *time.Time `url:"created_at,omitempty" json:"created_at,omitempty"`
}

// UpdateSnippetDiscussionNote modifies existing discussion of a snippet.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/discussions.html#modify-existing-snippet-thread-note
func (s *DiscussionsService) UpdateSnippetDiscussionNote(pid interface{}, snippet int, discussion string, note int, opt *UpdateSnippetDiscussionNoteOptions, options ...RequestOptionFunc) (*Note, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/snippets/%d/discussions/%s/notes/%d",
		PathEscape(project),
		snippet,
		discussion,
		note,
	)

	req, err := s.client.NewRequest(http.MethodPut, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	n := new(Note)
	resp, err := s.client.Do(req, n)
	if err != nil {
		return nil, resp, err
	}

	return n, resp, nil
}

// DeleteSnippetDiscussionNote deletes an existing discussion of a snippet.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/discussions.html#delete-a-snippet-thread-note
func (s *DiscussionsService) DeleteSnippetDiscussionNote(pid interface{}, snippet int, discussion string, note int, options ...RequestOptionFunc) (*Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("projects/%s/snippets/%d/discussions/%s/notes/%d",
		PathEscape(project),
		snippet,
		discussion,
		note,
	)

	req, err := s.client.NewRequest(http.MethodDelete, u, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}

// ListGroupEpicDiscussionsOptions represents the available
// ListEpicDiscussions() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/discussions.html#list-group-epic-discussion-items
type ListGroupEpicDiscussionsOptions ListOptions

// ListGroupEpicDiscussions gets a list of all discussions for a single
// epic. Epic discussions are comments users can post to a epic.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/discussions.html#list-group-epic-discussion-items
func (s *DiscussionsService) ListGroupEpicDiscussions(gid interface{}, epic int, opt *ListGroupEpicDiscussionsOptions, options ...RequestOptionFunc) ([]*Discussion, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/epics/%d/discussions",
		PathEscape(group),
		epic,
	)

	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var ds []*Discussion
	resp, err := s.client.Do(req, &ds)
	if err != nil {
		return nil, resp, err
	}

	return ds, resp, nil
}

// GetEpicDiscussion returns a single discussion for a given epic.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/discussions.html#get-single-epic-discussion-item
func (s *DiscussionsService) GetEpicDiscussion(gid interface{}, epic int, discussion string, options ...RequestOptionFunc) (*Discussion, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/epics/%d/discussions/%s",
		PathEscape(group),
		epic,
		discussion,
	)

	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	d := new(Discussion)
	resp, err := s.client.Do(req, d)
	if err != nil {
		return nil, resp, err
	}

	return d, resp, nil
}

// CreateEpicDiscussionOptions represents the available CreateEpicDiscussion()
// options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/discussions.html#create-new-epic-thread
type CreateEpicDiscussionOptions struct {
	Body      *string    `url:"body,omitempty" json:"body,omitempty"`
	CreatedAt *time.Time `url:"created_at,omitempty" json:"created_at,omitempty"`
}

// CreateEpicDiscussion creates a new discussion for a single epic. Epic
// discussions are comments users can post to a epic.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/discussions.html#create-new-epic-thread
func (s *DiscussionsService) CreateEpicDiscussion(gid interface{}, epic int, opt *CreateEpicDiscussionOptions, options ...RequestOptionFunc) (*Discussion, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/epics/%d/discussions",
		PathEscape(group),
		epic,
	)

	req, err := s.client.NewRequest(http.MethodPost, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	d := new(Discussion)
	resp, err := s.client.Do(req, d)
	if err != nil {
		return nil, resp, err
	}

	return d, resp, nil
}

// AddEpicDiscussionNoteOptions represents the available
// AddEpicDiscussionNote() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/discussions.html#add-note-to-existing-epic-thread
type AddEpicDiscussionNoteOptions struct {
	Body      *string    `url:"body,omitempty" json:"body,omitempty"`
	CreatedAt *time.Time `url:"created_at,omitempty" json:"created_at,omitempty"`
}

// AddEpicDiscussionNote creates a new discussion to a single project epic.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/discussions.html#add-note-to-existing-epic-thread
func (s *DiscussionsService) AddEpicDiscussionNote(gid interface{}, epic int, discussion string, opt *AddEpicDiscussionNoteOptions, options ...RequestOptionFunc) (*Note, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/epics/%d/discussions/%s/notes",
		PathEscape(group),
		epic,
		discussion,
	)

	req, err := s.client.NewRequest(http.MethodPost, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	n := new(Note)
	resp, err := s.client.Do(req, n)
	if err != nil {
		return nil, resp, err
	}

	return n, resp, nil
}

// UpdateEpicDiscussionNoteOptions represents the available UpdateEpicDiscussion()
// options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/discussions.html#modify-existing-epic-thread-note
type UpdateEpicDiscussionNoteOptions struct {
	Body      *string    `url:"body,omitempty" json:"body,omitempty"`
	CreatedAt *time.Time `url:"created_at,omitempty" json:"created_at,omitempty"`
}

// UpdateEpicDiscussionNote modifies existing discussion of a epic.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/discussions.html#modify-existing-epic-thread-note
func (s *DiscussionsService) UpdateEpicDiscussionNote(gid interface{}, epic int, discussion string, note int, opt *UpdateEpicDiscussionNoteOptions, options ...RequestOptionFunc) (*Note, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/epics/%d/discussions/%s/notes/%d",
		PathEscape(group),
		epic,
		discussion,
		note,
	)

	req, err := s.client.NewRequest(http.MethodPut, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	n := new(Note)
	resp, err := s.client.Do(req, n)
	if err != nil {
		return nil, resp, err
	}

	return n, resp, nil
}

// DeleteEpicDiscussionNote deletes an existing discussion of a epic.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/discussions.html#delete-an-epic-thread-note
func (s *DiscussionsService) DeleteEpicDiscussionNote(gid interface{}, epic int, discussion string, note int, options ...RequestOptionFunc) (*Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("groups/%s/epics/%d/discussions/%s/notes/%d",
		PathEscape(group),
		epic,
		discussion,
		note,
	)

	req, err := s.client.NewRequest(http.MethodDelete, u, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}

// ListMergeRequestDiscussionsOptions represents the available
// ListMergeRequestDiscussions() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/discussions.html#list-project-merge-request-discussion-items
type ListMergeRequestDiscussionsOptions ListOptions

// ListMergeRequestDiscussions gets a list of all discussions for a single
// merge request.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/discussions.html#list-project-merge-request-discussion-items
func (s *DiscussionsService) ListMergeRequestDiscussions(pid interface{}, mergeRequest int, opt *ListMergeRequestDiscussionsOptions, options ...RequestOptionFunc) ([]*Discussion, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/merge_requests/%d/discussions",
		PathEscape(project),
		mergeRequest,
	)

	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var ds []*Discussion
	resp, err := s.client.Do(req, &ds)
	if err != nil {
		return nil, resp, err
	}

	return ds, resp, nil
}

// GetMergeRequestDiscussion returns a single discussion for a given merge
// request.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/discussions.html#get-single-merge-request-discussion-item
func (s *DiscussionsService) GetMergeRequestDiscussion(pid interface{}, mergeRequest int, discussion string, options ...RequestOptionFunc) (*Discussion, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/merge_requests/%d/discussions/%s",
		PathEscape(project),
		mergeRequest,
		discussion,
	)

	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	d := new(Discussion)
	resp, err := s.client.Do(req, d)
	if err != nil {
		return nil, resp, err
	}

	return d, resp, nil
}

// CreateMergeRequestDiscussionOptions represents the available
// CreateMergeRequestDiscussion() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/discussions.html#create-new-merge-request-thread
type CreateMergeRequestDiscussionOptions struct {
	Body      *string          `url:"body,omitempty" json:"body,omitempty"`
	CommitID  *string          `url:"commit_id,omitempty" json:"commit_id,omitempty"`
	CreatedAt *time.Time       `url:"created_at,omitempty" json:"created_at,omitempty"`
	Position  *PositionOptions `url:"position,omitempty" json:"position,omitempty"`
}

// PositionOptions represents the position option of a discussion.
type PositionOptions struct {
	BaseSHA      *string           `url:"base_sha,omitempty" json:"base_sha,omitempty"`
	HeadSHA      *string           `url:"head_sha,omitempty" json:"head_sha,omitempty"`
	StartSHA     *string           `url:"start_sha,omitempty" json:"start_sha,omitempty"`
	NewPath      *string           `url:"new_path,omitempty" json:"new_path,omitempty"`
	OldPath      *string           `url:"old_path,omitempty" json:"old_path,omitempty"`
	PositionType *string           `url:"position_type,omitempty" json:"position_type"`
	NewLine      *int              `url:"new_line,omitempty" json:"new_line,omitempty"`
	OldLine      *int              `url:"old_line,omitempty" json:"old_line,omitempty"`
	LineRange    *LineRangeOptions `url:"line_range,omitempty" json:"line_range,omitempty"`
	Width        *int              `url:"width,omitempty" json:"width,omitempty"`
	Height       *int              `url:"height,omitempty" json:"height,omitempty"`
	X            *float64          `url:"x,omitempty" json:"x,omitempty"`
	Y            *float64          `url:"y,omitempty" json:"y,omitempty"`
}

// LineRangeOptions represents the line range option of a discussion.
type LineRangeOptions struct {
	Start *LinePositionOptions `url:"start,omitempty" json:"start,omitempty"`
	End   *LinePositionOptions `url:"end,omitempty" json:"end,omitempty"`
}

// LinePositionOptions represents the line position option of a discussion.
type LinePositionOptions struct {
	LineCode *string `url:"line_code,omitempty" json:"line_code,omitempty"`
	Type     *string `url:"type,omitempty" json:"type,omitempty"`
}

// CreateMergeRequestDiscussion creates a new discussion for a single merge
// request.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/discussions.html#create-new-merge-request-thread
func (s *DiscussionsService) CreateMergeRequestDiscussion(pid interface{}, mergeRequest int, opt *CreateMergeRequestDiscussionOptions, options ...RequestOptionFunc) (*Discussion, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/merge_requests/%d/discussions",
		PathEscape(project),
		mergeRequest,
	)

	req, err := s.client.NewRequest(http.MethodPost, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	d := new(Discussion)
	resp, err := s.client.Do(req, d)
	if err != nil {
		return nil, resp, err
	}

	return d, resp, nil
}

// ResolveMergeRequestDiscussionOptions represents the available
// ResolveMergeRequestDiscussion() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/discussions.html#resolve-a-merge-request-thread
type ResolveMergeRequestDiscussionOptions struct {
	Resolved *bool `url:"resolved,omitempty" json:"resolved,omitempty"`
}

// ResolveMergeRequestDiscussion resolves/unresolves whole discussion of a merge
// request.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/discussions.html#resolve-a-merge-request-thread
func (s *DiscussionsService) ResolveMergeRequestDiscussion(pid interface{}, mergeRequest int, discussion string, opt *ResolveMergeRequestDiscussionOptions, options ...RequestOptionFunc) (*Discussion, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/merge_requests/%d/discussions/%s",
		PathEscape(project),
		mergeRequest,
		discussion,
	)

	req, err := s.client.NewRequest(http.MethodPut, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	d := new(Discussion)
	resp, err := s.client.Do(req, d)
	if err != nil {
		return nil, resp, err
	}

	return d, resp, nil
}

// AddMergeRequestDiscussionNoteOptions represents the available
// AddMergeRequestDiscussionNote() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/discussions.html#add-note-to-existing-merge-request-thread
type AddMergeRequestDiscussionNoteOptions struct {
	Body      *string    `url:"body,omitempty" json:"body,omitempty"`
	CreatedAt *time.Time `url:"created_at,omitempty" json:"created_at,omitempty"`
}

// AddMergeRequestDiscussionNote creates a new discussion to a single project
// merge request.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/discussions.html#add-note-to-existing-merge-request-thread
func (s *DiscussionsService) AddMergeRequestDiscussionNote(pid interface{}, mergeRequest int, discussion string, opt *AddMergeRequestDiscussionNoteOptions, options ...RequestOptionFunc) (*Note, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/merge_requests/%d/discussions/%s/notes",
		PathEscape(project),
		mergeRequest,
		discussion,
	)

	req, err := s.client.NewRequest(http.MethodPost, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	n := new(Note)
	resp, err := s.client.Do(req, n)
	if err != nil {
		return nil, resp, err
	}

	return n, resp, nil
}

// UpdateMergeRequestDiscussionNoteOptions represents the available
// UpdateMergeRequestDiscussion() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/discussions.html#modify-an-existing-merge-request-thread-note
type UpdateMergeRequestDiscussionNoteOptions struct {
	Body      *string    `url:"body,omitempty" json:"body,omitempty"`
	CreatedAt *time.Time `url:"created_at,omitempty" json:"created_at,omitempty"`
	Resolved  *bool      `url:"resolved,omitempty" json:"resolved,omitempty"`
}

// UpdateMergeRequestDiscussionNote modifies existing discussion of a merge
// request.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/discussions.html#modify-an-existing-merge-request-thread-note
func (s *DiscussionsService) UpdateMergeRequestDiscussionNote(pid interface{}, mergeRequest int, discussion string, note int, opt *UpdateMergeRequestDiscussionNoteOptions, options ...RequestOptionFunc) (*Note, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/merge_requests/%d/discussions/%s/notes/%d",
		PathEscape(project),
		mergeRequest,
		discussion,
		note,
	)

	req, err := s.client.NewRequest(http.MethodPut, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	n := new(Note)
	resp, err := s.client.Do(req, n)
	if err != nil {
		return nil, resp, err
	}

	return n, resp, nil
}

// DeleteMergeRequestDiscussionNote deletes an existing discussion of a merge
// request.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/discussions.html#delete-a-merge-request-thread-note
func (s *DiscussionsService) DeleteMergeRequestDiscussionNote(pid interface{}, mergeRequest int, discussion string, note int, options ...RequestOptionFunc) (*Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("projects/%s/merge_requests/%d/discussions/%s/notes/%d",
		PathEscape(project),
		mergeRequest,
		discussion,
		note,
	)

	req, err := s.client.NewRequest(http.MethodDelete, u, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}

// ListCommitDiscussionsOptions represents the available
// ListCommitDiscussions() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/discussions.html#list-project-commit-discussion-items
type ListCommitDiscussionsOptions ListOptions

// ListCommitDiscussions gets a list of all discussions for a single
// commit.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/discussions.html#list-project-commit-discussion-items
func (s *DiscussionsService) ListCommitDiscussions(pid interface{}, commit string, opt *ListCommitDiscussionsOptions, options ...RequestOptionFunc) ([]*Discussion, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/repository/commits/%s/discussions",
		PathEscape(project),
		commit,
	)

	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var ds []*Discussion
	resp, err := s.client.Do(req, &ds)
	if err != nil {
		return nil, resp, err
	}

	return ds, resp, nil
}

// GetCommitDiscussion returns a single discussion for a specific project
// commit.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/discussions.html#get-single-commit-discussion-item
func (s *DiscussionsService) GetCommitDiscussion(pid interface{}, commit string, discussion string, options ...RequestOptionFunc) (*Discussion, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/repository/commits/%s/discussions/%s",
		PathEscape(project),
		commit,
		discussion,
	)

	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	d := new(Discussion)
	resp, err := s.client.Do(req, d)
	if err != nil {
		return nil, resp, err
	}

	return d, resp, nil
}

// CreateCommitDiscussionOptions represents the available
// CreateCommitDiscussion() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/discussions.html#create-new-commit-thread
type CreateCommitDiscussionOptions struct {
	Body      *string       `url:"body,omitempty" json:"body,omitempty"`
	CreatedAt *time.Time    `url:"created_at,omitempty" json:"created_at,omitempty"`
	Position  *NotePosition `url:"position,omitempty" json:"position,omitempty"`
}

// CreateCommitDiscussion creates a new discussion to a single project commit.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/discussions.html#create-new-commit-thread
func (s *DiscussionsService) CreateCommitDiscussion(pid interface{}, commit string, opt *CreateCommitDiscussionOptions, options ...RequestOptionFunc) (*Discussion, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/repository/commits/%s/discussions",
		PathEscape(project),
		commit,
	)

	req, err := s.client.NewRequest(http.MethodPost, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	d := new(Discussion)
	resp, err := s.client.Do(req, d)
	if err != nil {
		return nil, resp, err
	}

	return d, resp, nil
}

// AddCommitDiscussionNoteOptions represents the available
// AddCommitDiscussionNote() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/discussions.html#add-note-to-existing-commit-thread
type AddCommitDiscussionNoteOptions struct {
	Body      *string    `url:"body,omitempty" json:"body,omitempty"`
	CreatedAt *time.Time `url:"created_at,omitempty" json:"created_at,omitempty"`
}

// AddCommitDiscussionNote creates a new discussion to a single project commit.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/discussions.html#add-note-to-existing-commit-thread
func (s *DiscussionsService) AddCommitDiscussionNote(pid interface{}, commit string, discussion string, opt *AddCommitDiscussionNoteOptions, options ...RequestOptionFunc) (*Note, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/repository/commits/%s/discussions/%s/notes",
		PathEscape(project),
		commit,
		discussion,
	)

	req, err := s.client.NewRequest(http.MethodPost, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	n := new(Note)
	resp, err := s.client.Do(req, n)
	if err != nil {
		return nil, resp, err
	}

	return n, resp, nil
}

// UpdateCommitDiscussionNoteOptions represents the available
// UpdateCommitDiscussion() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/discussions.html#modify-an-existing-commit-thread-note
type UpdateCommitDiscussionNoteOptions struct {
	Body      *string    `url:"body,omitempty" json:"body,omitempty"`
	CreatedAt *time.Time `url:"created_at,omitempty" json:"created_at,omitempty"`
}

// UpdateCommitDiscussionNote modifies existing discussion of an commit.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/discussions.html#modify-an-existing-commit-thread-note
func (s *DiscussionsService) UpdateCommitDiscussionNote(pid interface{}, commit string, discussion string, note int, opt *UpdateCommitDiscussionNoteOptions, options ...RequestOptionFunc) (*Note, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/repository/commits/%s/discussions/%s/notes/%d",
		PathEscape(project),
		commit,
		discussion,
		note,
	)

	req, err := s.client.NewRequest(http.MethodPut, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	n := new(Note)
	resp, err := s.client.Do(req, n)
	if err != nil {
		return nil, resp, err
	}

	return n, resp, nil
}

// DeleteCommitDiscussionNote deletes an existing discussion of an commit.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/discussions.html#delete-a-commit-thread-note
func (s *DiscussionsService) DeleteCommitDiscussionNote(pid interface{}, commit string, discussion string, note int, options ...RequestOptionFunc) (*Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("projects/%s/repository/commits/%s/discussions/%s/notes/%d",
		PathEscape(project),
		commit,
		discussion,
		note,
	)

	req, err := s.client.NewRequest(http.MethodDelete, u, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/discussions_test.go000066400000000000000000002415731475761473200250270ustar00rootroot00000000000000package gitlab

import (
	"fmt"
	"net/http"
	"testing"

	"github.com/stretchr/testify/require"
)

func TestDiscussionsService_ListIssueDiscussions(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/5/issues/11/discussions", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
			[
			  {
				"id": "6a9c1750b37d513a43987b574953fceb50b03ce7",
				"individual_note": false,
				"notes": [
				  {
					"id": 1126,
					"type": "DiscussionNote",
					"body": "discussion text",
					"attachment": null,
					"author": {
					  "id": 1,
					  "name": "Venkatesh Thalluri",
					  "username": "venky333",
					  "state": "active",
					  "avatar_url": "https://www.gravatar.com/avatar/00afb8fb6ab07c3ee3e9c1f38777e2f4?s=80&d=identicon",
					  "web_url": "http://localhost:3000/venky333"
					},
					"system": false,
					"noteable_id": 3,
					"noteable_type": "Issue",
					"noteable_iid": null
				  }
				]
			  }
			]
		`)
	})

	want := []*Discussion{{
		ID:             "6a9c1750b37d513a43987b574953fceb50b03ce7",
		IndividualNote: false,
		Notes: []*Note{{
			ID:         1126,
			Type:       "DiscussionNote",
			Body:       "discussion text",
			Attachment: "",
			Title:      "",
			FileName:   "",
			Author: struct {
				ID        int    `json:"id"`
				Username  string `json:"username"`
				Email     string `json:"email"`
				Name      string `json:"name"`
				State     string `json:"state"`
				AvatarURL string `json:"avatar_url"`
				WebURL    string `json:"web_url"`
			}{
				ID:        1,
				Username:  "venky333",
				Email:     "",
				Name:      "Venkatesh Thalluri",
				State:     "active",
				AvatarURL: "https://www.gravatar.com/avatar/00afb8fb6ab07c3ee3e9c1f38777e2f4?s=80&d=identicon",
				WebURL:    "http://localhost:3000/venky333",
			},
			System:       false,
			ExpiresAt:    nil,
			UpdatedAt:    nil,
			CreatedAt:    nil,
			NoteableID:   3,
			NoteableType: "Issue",
			CommitID:     "",
			Position:     nil,
			Resolvable:   false,
			Resolved:     false,
			ResolvedBy: struct {
				ID        int    `json:"id"`
				Username  string `json:"username"`
				Email     string `json:"email"`
				Name      string `json:"name"`
				State     string `json:"state"`
				AvatarURL string `json:"avatar_url"`
				WebURL    string `json:"web_url"`
			}{},
			NoteableIID: 0,
		}},
	}}

	ds, resp, err := client.Discussions.ListIssueDiscussions(5, 11, nil, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, ds)

	ds, resp, err = client.Discussions.ListIssueDiscussions(5.01, 11, nil, nil)
	require.EqualError(t, err, "invalid ID type 5.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, ds)

	ds, resp, err = client.Discussions.ListIssueDiscussions(5, 11, nil, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, ds)

	ds, resp, err = client.Discussions.ListIssueDiscussions(3, 11, nil, nil)
	require.Error(t, err)
	require.Nil(t, ds)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestDiscussionsService_GetIssueDiscussion(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/5/issues/11/discussions/6a9c1750b37d513a43987b574953fceb50b03ce7", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
		  {
			"id": "6a9c1750b37d513a43987b574953fceb50b03ce7",
			"individual_note": false,
			"notes": [
			  {
				"id": 1126,
				"type": "DiscussionNote",
				"body": "discussion text",
				"attachment": null,
				"author": {
				  "id": 1,
				  "name": "Venkatesh Thalluri",
				  "username": "venky333",
				  "state": "active",
				  "avatar_url": "https://www.gravatar.com/avatar/00afb8fb6ab07c3ee3e9c1f38777e2f4?s=80&d=identicon",
				  "web_url": "http://localhost:3000/venky333"
				},
				"system": false,
				"noteable_id": 3,
				"noteable_type": "Issue",
				"noteable_iid": null
			  }
			]
		  }
		`)
	})

	want := &Discussion{
		ID:             "6a9c1750b37d513a43987b574953fceb50b03ce7",
		IndividualNote: false,
		Notes: []*Note{{
			ID:         1126,
			Type:       "DiscussionNote",
			Body:       "discussion text",
			Attachment: "",
			Title:      "",
			FileName:   "",
			Author: struct {
				ID        int    `json:"id"`
				Username  string `json:"username"`
				Email     string `json:"email"`
				Name      string `json:"name"`
				State     string `json:"state"`
				AvatarURL string `json:"avatar_url"`
				WebURL    string `json:"web_url"`
			}{
				ID:        1,
				Username:  "venky333",
				Email:     "",
				Name:      "Venkatesh Thalluri",
				State:     "active",
				AvatarURL: "https://www.gravatar.com/avatar/00afb8fb6ab07c3ee3e9c1f38777e2f4?s=80&d=identicon",
				WebURL:    "http://localhost:3000/venky333",
			},
			System:       false,
			ExpiresAt:    nil,
			UpdatedAt:    nil,
			CreatedAt:    nil,
			NoteableID:   3,
			NoteableType: "Issue",
			CommitID:     "",
			Position:     nil,
			Resolvable:   false,
			Resolved:     false,
			ResolvedBy: struct {
				ID        int    `json:"id"`
				Username  string `json:"username"`
				Email     string `json:"email"`
				Name      string `json:"name"`
				State     string `json:"state"`
				AvatarURL string `json:"avatar_url"`
				WebURL    string `json:"web_url"`
			}{},
			NoteableIID: 0,
		}},
	}

	d, resp, err := client.Discussions.GetIssueDiscussion(5, 11, "6a9c1750b37d513a43987b574953fceb50b03ce7", nil, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, d)

	d, resp, err = client.Discussions.GetIssueDiscussion(5.01, 11, "6a9c1750b37d513a43987b574953fceb50b03ce7", nil, nil)
	require.EqualError(t, err, "invalid ID type 5.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, d)

	d, resp, err = client.Discussions.GetIssueDiscussion(5, 11, "6a9c1750b37d513a43987b574953fceb50b03ce7", nil, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, d)

	d, resp, err = client.Discussions.GetIssueDiscussion(3, 11, "6a9c1750b37d513a43987b574953fceb50b03ce7", nil, nil)
	require.Error(t, err)
	require.Nil(t, d)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestDiscussionsService_CreateIssueDiscussion(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/5/issues/11/discussions", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		fmt.Fprintf(w, `
		  {
			"id": "6a9c1750b37d513a43987b574953fceb50b03ce7",
			"individual_note": false,
			"notes": [
			  {
				"id": 1126,
				"type": "DiscussionNote",
				"body": "discussion text",
				"attachment": null,
				"author": {
				  "id": 1,
				  "name": "Venkatesh Thalluri",
				  "username": "venky333",
				  "state": "active",
				  "avatar_url": "https://www.gravatar.com/avatar/00afb8fb6ab07c3ee3e9c1f38777e2f4?s=80&d=identicon",
				  "web_url": "http://localhost:3000/venky333"
				},
				"system": false,
				"noteable_id": 3,
				"noteable_type": "Issue",
				"noteable_iid": null
			  }
			]
		  }
		`)
	})

	want := &Discussion{
		ID:             "6a9c1750b37d513a43987b574953fceb50b03ce7",
		IndividualNote: false,
		Notes: []*Note{{
			ID:         1126,
			Type:       "DiscussionNote",
			Body:       "discussion text",
			Attachment: "",
			Title:      "",
			FileName:   "",
			Author: struct {
				ID        int    `json:"id"`
				Username  string `json:"username"`
				Email     string `json:"email"`
				Name      string `json:"name"`
				State     string `json:"state"`
				AvatarURL string `json:"avatar_url"`
				WebURL    string `json:"web_url"`
			}{
				ID:        1,
				Username:  "venky333",
				Email:     "",
				Name:      "Venkatesh Thalluri",
				State:     "active",
				AvatarURL: "https://www.gravatar.com/avatar/00afb8fb6ab07c3ee3e9c1f38777e2f4?s=80&d=identicon",
				WebURL:    "http://localhost:3000/venky333",
			},
			System:       false,
			ExpiresAt:    nil,
			UpdatedAt:    nil,
			CreatedAt:    nil,
			NoteableID:   3,
			NoteableType: "Issue",
			CommitID:     "",
			Position:     nil,
			Resolvable:   false,
			Resolved:     false,
			ResolvedBy: struct {
				ID        int    `json:"id"`
				Username  string `json:"username"`
				Email     string `json:"email"`
				Name      string `json:"name"`
				State     string `json:"state"`
				AvatarURL string `json:"avatar_url"`
				WebURL    string `json:"web_url"`
			}{},
			NoteableIID: 0,
		}},
	}

	d, resp, err := client.Discussions.CreateIssueDiscussion(5, 11, nil, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, d)

	d, resp, err = client.Discussions.CreateIssueDiscussion(5.01, 11, nil, nil)
	require.EqualError(t, err, "invalid ID type 5.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, d)

	d, resp, err = client.Discussions.CreateIssueDiscussion(5, 11, nil, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, d)

	d, resp, err = client.Discussions.CreateIssueDiscussion(3, 11, nil, nil)
	require.Error(t, err)
	require.Nil(t, d)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestDiscussionsService_AddIssueDiscussionNote(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/5/issues/11/discussions/6a9c1750b37d513a43987b574953fceb50b03ce7/notes", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		fmt.Fprintf(w, `
		  {
			"id": 302,
			"body": "closed",
			"attachment": null,
			"author": {
			  "id": 1,
			  "username": "venky333",
			  "email": "venky333@example.com",
			  "name": "venky333",
			  "state": "active"
			},
			"system": true,
			"noteable_id": 377,
			"noteable_type": "Issue",
			"noteable_iid": 377,
			"resolvable": false,
			"confidential": false
		  }
		`)
	})

	want := &Note{
		ID:         302,
		Type:       "",
		Body:       "closed",
		Attachment: "",
		Title:      "",
		FileName:   "",
		Author: struct {
			ID        int    `json:"id"`
			Username  string `json:"username"`
			Email     string `json:"email"`
			Name      string `json:"name"`
			State     string `json:"state"`
			AvatarURL string `json:"avatar_url"`
			WebURL    string `json:"web_url"`
		}{
			ID:        1,
			Username:  "venky333",
			Email:     "venky333@example.com",
			Name:      "venky333",
			State:     "active",
			AvatarURL: "",
			WebURL:    "",
		},
		System:       true,
		ExpiresAt:    nil,
		UpdatedAt:    nil,
		CreatedAt:    nil,
		NoteableID:   377,
		NoteableType: "Issue",
		CommitID:     "",
		Position:     nil,
		Resolvable:   false,
		Resolved:     false,
		ResolvedBy: struct {
			ID        int    `json:"id"`
			Username  string `json:"username"`
			Email     string `json:"email"`
			Name      string `json:"name"`
			State     string `json:"state"`
			AvatarURL string `json:"avatar_url"`
			WebURL    string `json:"web_url"`
		}{},
		NoteableIID: 377,
	}

	n, resp, err := client.Discussions.AddIssueDiscussionNote(5, 11, "6a9c1750b37d513a43987b574953fceb50b03ce7", nil, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, n)

	n, resp, err = client.Discussions.AddIssueDiscussionNote(5.01, 11, "6a9c1750b37d513a43987b574953fceb50b03ce7", nil, nil)
	require.EqualError(t, err, "invalid ID type 5.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, n)

	n, resp, err = client.Discussions.AddIssueDiscussionNote(5, 11, "6a9c1750b37d513a43987b574953fceb50b03ce7", nil, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, n)

	n, resp, err = client.Discussions.AddIssueDiscussionNote(3, 11, "6a9c1750b37d513a43987b574953fceb50b03ce7", nil, nil)
	require.Error(t, err)
	require.Nil(t, n)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestDiscussionsService_UpdateIssueDiscussionNote(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/5/issues/11/discussions/6a9c1750b37d513a43987b574953fceb50b03ce7/notes/302", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPut)
		fmt.Fprintf(w, `
		  {
			"id": 302,
			"body": "closed",
			"attachment": null,
			"author": {
			  "id": 1,
			  "username": "venky333",
			  "email": "venky333@example.com",
			  "name": "venky333",
			  "state": "active"
			},
			"system": true,
			"noteable_id": 377,
			"noteable_type": "Issue",
			"noteable_iid": 377,
			"resolvable": false,
			"confidential": false
		  }
		`)
	})

	want := &Note{
		ID:         302,
		Type:       "",
		Body:       "closed",
		Attachment: "",
		Title:      "",
		FileName:   "",
		Author: struct {
			ID        int    `json:"id"`
			Username  string `json:"username"`
			Email     string `json:"email"`
			Name      string `json:"name"`
			State     string `json:"state"`
			AvatarURL string `json:"avatar_url"`
			WebURL    string `json:"web_url"`
		}{
			ID:        1,
			Username:  "venky333",
			Email:     "venky333@example.com",
			Name:      "venky333",
			State:     "active",
			AvatarURL: "",
			WebURL:    "",
		},
		System:       true,
		ExpiresAt:    nil,
		UpdatedAt:    nil,
		CreatedAt:    nil,
		NoteableID:   377,
		NoteableType: "Issue",
		CommitID:     "",
		Position:     nil,
		Resolvable:   false,
		Resolved:     false,
		ResolvedBy: struct {
			ID        int    `json:"id"`
			Username  string `json:"username"`
			Email     string `json:"email"`
			Name      string `json:"name"`
			State     string `json:"state"`
			AvatarURL string `json:"avatar_url"`
			WebURL    string `json:"web_url"`
		}{},
		NoteableIID: 377,
	}

	n, resp, err := client.Discussions.UpdateIssueDiscussionNote(5, 11, "6a9c1750b37d513a43987b574953fceb50b03ce7", 302, nil, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, n)

	n, resp, err = client.Discussions.UpdateIssueDiscussionNote(5.01, 11, "6a9c1750b37d513a43987b574953fceb50b03ce7", 302, nil, nil)
	require.EqualError(t, err, "invalid ID type 5.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, n)

	n, resp, err = client.Discussions.UpdateIssueDiscussionNote(5, 11, "6a9c1750b37d513a43987b574953fceb50b03ce7", 302, nil, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, n)

	n, resp, err = client.Discussions.UpdateIssueDiscussionNote(3, 11, "6a9c1750b37d513a43987b574953fceb50b03ce7", 302, nil, nil)
	require.Error(t, err)
	require.Nil(t, n)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestDiscussionsService_DeleteIssueDiscussionNote(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/5/issues/11/discussions/6a9c1750b37d513a43987b574953fceb50b03ce7/notes/302", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodDelete)
	})

	resp, err := client.Discussions.DeleteIssueDiscussionNote(5, 11, "6a9c1750b37d513a43987b574953fceb50b03ce7", 302, nil, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)

	resp, err = client.Discussions.DeleteIssueDiscussionNote(5.01, 11, "6a9c1750b37d513a43987b574953fceb50b03ce7", 302, nil, nil)
	require.EqualError(t, err, "invalid ID type 5.01, the ID must be an int or a string")
	require.Nil(t, resp)

	resp, err = client.Discussions.DeleteIssueDiscussionNote(5, 11, "6a9c1750b37d513a43987b574953fceb50b03ce7", 302, nil, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)

	resp, err = client.Discussions.DeleteIssueDiscussionNote(3, 11, "6a9c1750b37d513a43987b574953fceb50b03ce7", 302, nil, nil)
	require.Error(t, err)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestDiscussionsService_ListSnippetDiscussions(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/5/snippets/11/discussions", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
			[
			  {
				"id": "6a9c1750b37d513a43987b574953fceb50b03ce7",
				"individual_note": false,
				"notes": [
				  {
					"id": 1126,
					"type": "DiscussionNote",
					"body": "discussion text",
					"attachment": null,
					"author": {
					  "id": 1,
					  "name": "Venkatesh Thalluri",
					  "username": "venky333",
					  "state": "active",
					  "avatar_url": "https://www.gravatar.com/avatar/00afb8fb6ab07c3ee3e9c1f38777e2f4?s=80&d=identicon",
					  "web_url": "http://localhost:3000/venky333"
					},
					"system": false,
					"noteable_id": 3,
					"noteable_type": "Snippet",
					"noteable_iid": null
				  }
				]
			  }
			]
		`)
	})

	want := []*Discussion{{
		ID:             "6a9c1750b37d513a43987b574953fceb50b03ce7",
		IndividualNote: false,
		Notes: []*Note{{
			ID:         1126,
			Type:       "DiscussionNote",
			Body:       "discussion text",
			Attachment: "",
			Title:      "",
			FileName:   "",
			Author: struct {
				ID        int    `json:"id"`
				Username  string `json:"username"`
				Email     string `json:"email"`
				Name      string `json:"name"`
				State     string `json:"state"`
				AvatarURL string `json:"avatar_url"`
				WebURL    string `json:"web_url"`
			}{
				ID:        1,
				Username:  "venky333",
				Email:     "",
				Name:      "Venkatesh Thalluri",
				State:     "active",
				AvatarURL: "https://www.gravatar.com/avatar/00afb8fb6ab07c3ee3e9c1f38777e2f4?s=80&d=identicon",
				WebURL:    "http://localhost:3000/venky333",
			},
			System:       false,
			ExpiresAt:    nil,
			UpdatedAt:    nil,
			CreatedAt:    nil,
			NoteableID:   3,
			NoteableType: "Snippet",
			CommitID:     "",
			Position:     nil,
			Resolvable:   false,
			Resolved:     false,
			ResolvedBy: struct {
				ID        int    `json:"id"`
				Username  string `json:"username"`
				Email     string `json:"email"`
				Name      string `json:"name"`
				State     string `json:"state"`
				AvatarURL string `json:"avatar_url"`
				WebURL    string `json:"web_url"`
			}{},
			NoteableIID: 0,
		}},
	}}

	ds, resp, err := client.Discussions.ListSnippetDiscussions(5, 11, nil, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, ds)

	ds, resp, err = client.Discussions.ListSnippetDiscussions(5.01, 11, nil, nil)
	require.EqualError(t, err, "invalid ID type 5.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, ds)

	ds, resp, err = client.Discussions.ListSnippetDiscussions(5, 11, nil, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, ds)

	ds, resp, err = client.Discussions.ListSnippetDiscussions(3, 11, nil, nil)
	require.Error(t, err)
	require.Nil(t, ds)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestDiscussionsService_GetSnippetDiscussion(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/5/snippets/11/discussions/6a9c1750b37d513a43987b574953fceb50b03ce7", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
		  {
			"id": "6a9c1750b37d513a43987b574953fceb50b03ce7",
			"individual_note": false,
			"notes": [
			  {
				"id": 1126,
				"type": "DiscussionNote",
				"body": "discussion text",
				"attachment": null,
				"author": {
				  "id": 1,
				  "name": "Venkatesh Thalluri",
				  "username": "venky333",
				  "state": "active",
				  "avatar_url": "https://www.gravatar.com/avatar/00afb8fb6ab07c3ee3e9c1f38777e2f4?s=80&d=identicon",
				  "web_url": "http://localhost:3000/venky333"
				},
				"system": false,
				"noteable_id": 3,
				"noteable_type": "Snippet",
				"noteable_iid": null
			  }
			]
		  }
		`)
	})

	want := &Discussion{
		ID:             "6a9c1750b37d513a43987b574953fceb50b03ce7",
		IndividualNote: false,
		Notes: []*Note{{
			ID:         1126,
			Type:       "DiscussionNote",
			Body:       "discussion text",
			Attachment: "",
			Title:      "",
			FileName:   "",
			Author: struct {
				ID        int    `json:"id"`
				Username  string `json:"username"`
				Email     string `json:"email"`
				Name      string `json:"name"`
				State     string `json:"state"`
				AvatarURL string `json:"avatar_url"`
				WebURL    string `json:"web_url"`
			}{
				ID:        1,
				Username:  "venky333",
				Email:     "",
				Name:      "Venkatesh Thalluri",
				State:     "active",
				AvatarURL: "https://www.gravatar.com/avatar/00afb8fb6ab07c3ee3e9c1f38777e2f4?s=80&d=identicon",
				WebURL:    "http://localhost:3000/venky333",
			},
			System:       false,
			ExpiresAt:    nil,
			UpdatedAt:    nil,
			CreatedAt:    nil,
			NoteableID:   3,
			NoteableType: "Snippet",
			CommitID:     "",
			Position:     nil,
			Resolvable:   false,
			Resolved:     false,
			ResolvedBy: struct {
				ID        int    `json:"id"`
				Username  string `json:"username"`
				Email     string `json:"email"`
				Name      string `json:"name"`
				State     string `json:"state"`
				AvatarURL string `json:"avatar_url"`
				WebURL    string `json:"web_url"`
			}{},
			NoteableIID: 0,
		}},
	}

	d, resp, err := client.Discussions.GetSnippetDiscussion(5, 11, "6a9c1750b37d513a43987b574953fceb50b03ce7", nil, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, d)

	d, resp, err = client.Discussions.GetSnippetDiscussion(5.01, 11, "6a9c1750b37d513a43987b574953fceb50b03ce7", nil, nil)
	require.EqualError(t, err, "invalid ID type 5.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, d)

	d, resp, err = client.Discussions.GetSnippetDiscussion(5, 11, "6a9c1750b37d513a43987b574953fceb50b03ce7", nil, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, d)

	d, resp, err = client.Discussions.GetSnippetDiscussion(3, 11, "6a9c1750b37d513a43987b574953fceb50b03ce7", nil, nil)
	require.Error(t, err)
	require.Nil(t, d)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestDiscussionsService_CreateSnippetDiscussion(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/5/snippets/11/discussions", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		fmt.Fprintf(w, `
		  {
			"id": "6a9c1750b37d513a43987b574953fceb50b03ce7",
			"individual_note": false,
			"notes": [
			  {
				"id": 1126,
				"type": "DiscussionNote",
				"body": "discussion text",
				"attachment": null,
				"author": {
				  "id": 1,
				  "name": "Venkatesh Thalluri",
				  "username": "venky333",
				  "state": "active",
				  "avatar_url": "https://www.gravatar.com/avatar/00afb8fb6ab07c3ee3e9c1f38777e2f4?s=80&d=identicon",
				  "web_url": "http://localhost:3000/venky333"
				},
				"system": false,
				"noteable_id": 3,
				"noteable_type": "Snippet",
				"noteable_iid": null
			  }
			]
		  }
		`)
	})

	want := &Discussion{
		ID:             "6a9c1750b37d513a43987b574953fceb50b03ce7",
		IndividualNote: false,
		Notes: []*Note{{
			ID:         1126,
			Type:       "DiscussionNote",
			Body:       "discussion text",
			Attachment: "",
			Title:      "",
			FileName:   "",
			Author: struct {
				ID        int    `json:"id"`
				Username  string `json:"username"`
				Email     string `json:"email"`
				Name      string `json:"name"`
				State     string `json:"state"`
				AvatarURL string `json:"avatar_url"`
				WebURL    string `json:"web_url"`
			}{
				ID:        1,
				Username:  "venky333",
				Email:     "",
				Name:      "Venkatesh Thalluri",
				State:     "active",
				AvatarURL: "https://www.gravatar.com/avatar/00afb8fb6ab07c3ee3e9c1f38777e2f4?s=80&d=identicon",
				WebURL:    "http://localhost:3000/venky333",
			},
			System:       false,
			ExpiresAt:    nil,
			UpdatedAt:    nil,
			CreatedAt:    nil,
			NoteableID:   3,
			NoteableType: "Snippet",
			CommitID:     "",
			Position:     nil,
			Resolvable:   false,
			Resolved:     false,
			ResolvedBy: struct {
				ID        int    `json:"id"`
				Username  string `json:"username"`
				Email     string `json:"email"`
				Name      string `json:"name"`
				State     string `json:"state"`
				AvatarURL string `json:"avatar_url"`
				WebURL    string `json:"web_url"`
			}{},
			NoteableIID: 0,
		}},
	}

	d, resp, err := client.Discussions.CreateSnippetDiscussion(5, 11, nil, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, d)

	d, resp, err = client.Discussions.CreateSnippetDiscussion(5.01, 11, nil, nil)
	require.EqualError(t, err, "invalid ID type 5.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, d)

	d, resp, err = client.Discussions.CreateSnippetDiscussion(5, 11, nil, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, d)

	d, resp, err = client.Discussions.CreateSnippetDiscussion(3, 11, nil, nil)
	require.Error(t, err)
	require.Nil(t, d)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestDiscussionsService_AddSnippetDiscussionNote(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/5/snippets/11/discussions/6a9c1750b37d513a43987b574953fceb50b03ce7/notes", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		fmt.Fprintf(w, `
		  {
			"id": 302,
			"body": "closed",
			"attachment": null,
			"author": {
			  "id": 1,
			  "username": "venky333",
			  "email": "venky333@example.com",
			  "name": "venky333",
			  "state": "active"
			},
			"system": true,
			"noteable_id": 377,
			"noteable_type": "Snippet",
			"noteable_iid": 377,
			"resolvable": false,
			"confidential": false
		  }
		`)
	})

	want := &Note{
		ID:         302,
		Type:       "",
		Body:       "closed",
		Attachment: "",
		Title:      "",
		FileName:   "",
		Author: struct {
			ID        int    `json:"id"`
			Username  string `json:"username"`
			Email     string `json:"email"`
			Name      string `json:"name"`
			State     string `json:"state"`
			AvatarURL string `json:"avatar_url"`
			WebURL    string `json:"web_url"`
		}{
			ID:        1,
			Username:  "venky333",
			Email:     "venky333@example.com",
			Name:      "venky333",
			State:     "active",
			AvatarURL: "",
			WebURL:    "",
		},
		System:       true,
		ExpiresAt:    nil,
		UpdatedAt:    nil,
		CreatedAt:    nil,
		NoteableID:   377,
		NoteableType: "Snippet",
		CommitID:     "",
		Position:     nil,
		Resolvable:   false,
		Resolved:     false,
		ResolvedBy: struct {
			ID        int    `json:"id"`
			Username  string `json:"username"`
			Email     string `json:"email"`
			Name      string `json:"name"`
			State     string `json:"state"`
			AvatarURL string `json:"avatar_url"`
			WebURL    string `json:"web_url"`
		}{},
		NoteableIID: 377,
	}

	n, resp, err := client.Discussions.AddSnippetDiscussionNote(5, 11, "6a9c1750b37d513a43987b574953fceb50b03ce7", nil, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, n)

	n, resp, err = client.Discussions.AddSnippetDiscussionNote(5.01, 11, "6a9c1750b37d513a43987b574953fceb50b03ce7", nil, nil)
	require.EqualError(t, err, "invalid ID type 5.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, n)

	n, resp, err = client.Discussions.AddSnippetDiscussionNote(5, 11, "6a9c1750b37d513a43987b574953fceb50b03ce7", nil, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, n)

	n, resp, err = client.Discussions.AddSnippetDiscussionNote(3, 11, "6a9c1750b37d513a43987b574953fceb50b03ce7", nil, nil)
	require.Error(t, err)
	require.Nil(t, n)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestDiscussionsService_UpdateSnippetDiscussionNote(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/5/snippets/11/discussions/6a9c1750b37d513a43987b574953fceb50b03ce7/notes/302", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPut)
		fmt.Fprintf(w, `
		  {
			"id": 302,
			"body": "closed",
			"attachment": null,
			"author": {
			  "id": 1,
			  "username": "venky333",
			  "email": "venky333@example.com",
			  "name": "venky333",
			  "state": "active"
			},
			"system": true,
			"noteable_id": 377,
			"noteable_type": "Snippet",
			"noteable_iid": 377,
			"resolvable": false,
			"confidential": false
		  }
		`)
	})

	want := &Note{
		ID:         302,
		Type:       "",
		Body:       "closed",
		Attachment: "",
		Title:      "",
		FileName:   "",
		Author: struct {
			ID        int    `json:"id"`
			Username  string `json:"username"`
			Email     string `json:"email"`
			Name      string `json:"name"`
			State     string `json:"state"`
			AvatarURL string `json:"avatar_url"`
			WebURL    string `json:"web_url"`
		}{
			ID:        1,
			Username:  "venky333",
			Email:     "venky333@example.com",
			Name:      "venky333",
			State:     "active",
			AvatarURL: "",
			WebURL:    "",
		},
		System:       true,
		ExpiresAt:    nil,
		UpdatedAt:    nil,
		CreatedAt:    nil,
		NoteableID:   377,
		NoteableType: "Snippet",
		CommitID:     "",
		Position:     nil,
		Resolvable:   false,
		Resolved:     false,
		ResolvedBy: struct {
			ID        int    `json:"id"`
			Username  string `json:"username"`
			Email     string `json:"email"`
			Name      string `json:"name"`
			State     string `json:"state"`
			AvatarURL string `json:"avatar_url"`
			WebURL    string `json:"web_url"`
		}{},
		NoteableIID: 377,
	}

	n, resp, err := client.Discussions.UpdateSnippetDiscussionNote(5, 11, "6a9c1750b37d513a43987b574953fceb50b03ce7", 302, nil, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, n)

	n, resp, err = client.Discussions.UpdateSnippetDiscussionNote(5.01, 11, "6a9c1750b37d513a43987b574953fceb50b03ce7", 302, nil, nil)
	require.EqualError(t, err, "invalid ID type 5.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, n)

	n, resp, err = client.Discussions.UpdateSnippetDiscussionNote(5, 11, "6a9c1750b37d513a43987b574953fceb50b03ce7", 302, nil, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, n)

	n, resp, err = client.Discussions.UpdateSnippetDiscussionNote(3, 11, "6a9c1750b37d513a43987b574953fceb50b03ce7", 302, nil, nil)
	require.Error(t, err)
	require.Nil(t, n)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestDiscussionsService_DeleteSnippetDiscussionNote(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/5/snippets/11/discussions/6a9c1750b37d513a43987b574953fceb50b03ce7/notes/302", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodDelete)
	})

	resp, err := client.Discussions.DeleteSnippetDiscussionNote(5, 11, "6a9c1750b37d513a43987b574953fceb50b03ce7", 302, nil, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)

	resp, err = client.Discussions.DeleteSnippetDiscussionNote(5.01, 11, "6a9c1750b37d513a43987b574953fceb50b03ce7", 302, nil, nil)
	require.EqualError(t, err, "invalid ID type 5.01, the ID must be an int or a string")
	require.Nil(t, resp)

	resp, err = client.Discussions.DeleteSnippetDiscussionNote(5, 11, "6a9c1750b37d513a43987b574953fceb50b03ce7", 302, nil, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)

	resp, err = client.Discussions.DeleteSnippetDiscussionNote(3, 11, "6a9c1750b37d513a43987b574953fceb50b03ce7", 302, nil, nil)
	require.Error(t, err)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestDiscussionsService_ListGroupEpicDiscussions(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/5/epics/11/discussions", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
			[
			  {
				"id": "6a9c1750b37d513a43987b574953fceb50b03ce7",
				"individual_note": false,
				"notes": [
				  {
					"id": 1126,
					"type": "DiscussionNote",
					"body": "discussion text",
					"attachment": null,
					"author": {
					  "id": 1,
					  "name": "Venkatesh Thalluri",
					  "username": "venky333",
					  "state": "active",
					  "avatar_url": "https://www.gravatar.com/avatar/00afb8fb6ab07c3ee3e9c1f38777e2f4?s=80&d=identicon",
					  "web_url": "http://localhost:3000/venky333"
					},
					"system": false,
					"noteable_id": 3,
					"noteable_type": "Epic",
					"noteable_iid": null
				  }
				]
			  }
			]
		`)
	})

	want := []*Discussion{{
		ID:             "6a9c1750b37d513a43987b574953fceb50b03ce7",
		IndividualNote: false,
		Notes: []*Note{{
			ID:         1126,
			Type:       "DiscussionNote",
			Body:       "discussion text",
			Attachment: "",
			Title:      "",
			FileName:   "",
			Author: struct {
				ID        int    `json:"id"`
				Username  string `json:"username"`
				Email     string `json:"email"`
				Name      string `json:"name"`
				State     string `json:"state"`
				AvatarURL string `json:"avatar_url"`
				WebURL    string `json:"web_url"`
			}{
				ID:        1,
				Username:  "venky333",
				Email:     "",
				Name:      "Venkatesh Thalluri",
				State:     "active",
				AvatarURL: "https://www.gravatar.com/avatar/00afb8fb6ab07c3ee3e9c1f38777e2f4?s=80&d=identicon",
				WebURL:    "http://localhost:3000/venky333",
			},
			System:       false,
			ExpiresAt:    nil,
			UpdatedAt:    nil,
			CreatedAt:    nil,
			NoteableID:   3,
			NoteableType: "Epic",
			CommitID:     "",
			Position:     nil,
			Resolvable:   false,
			Resolved:     false,
			ResolvedBy: struct {
				ID        int    `json:"id"`
				Username  string `json:"username"`
				Email     string `json:"email"`
				Name      string `json:"name"`
				State     string `json:"state"`
				AvatarURL string `json:"avatar_url"`
				WebURL    string `json:"web_url"`
			}{},
			NoteableIID: 0,
		}},
	}}

	ds, resp, err := client.Discussions.ListGroupEpicDiscussions(5, 11, nil, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, ds)

	ds, resp, err = client.Discussions.ListGroupEpicDiscussions(5.01, 11, nil, nil)
	require.EqualError(t, err, "invalid ID type 5.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, ds)

	ds, resp, err = client.Discussions.ListGroupEpicDiscussions(5, 11, nil, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, ds)

	ds, resp, err = client.Discussions.ListGroupEpicDiscussions(3, 11, nil, nil)
	require.Error(t, err)
	require.Nil(t, ds)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestDiscussionsService_GetEpicDiscussion(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/5/epics/11/discussions/6a9c1750b37d513a43987b574953fceb50b03ce7", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
		  {
			"id": "6a9c1750b37d513a43987b574953fceb50b03ce7",
			"individual_note": false,
			"notes": [
			  {
				"id": 1126,
				"type": "DiscussionNote",
				"body": "discussion text",
				"attachment": null,
				"author": {
				  "id": 1,
				  "name": "Venkatesh Thalluri",
				  "username": "venky333",
				  "state": "active",
				  "avatar_url": "https://www.gravatar.com/avatar/00afb8fb6ab07c3ee3e9c1f38777e2f4?s=80&d=identicon",
				  "web_url": "http://localhost:3000/venky333"
				},
				"system": false,
				"noteable_id": 3,
				"noteable_type": "Epic",
				"noteable_iid": null
			  }
			]
		  }
		`)
	})

	want := &Discussion{
		ID:             "6a9c1750b37d513a43987b574953fceb50b03ce7",
		IndividualNote: false,
		Notes: []*Note{{
			ID:         1126,
			Type:       "DiscussionNote",
			Body:       "discussion text",
			Attachment: "",
			Title:      "",
			FileName:   "",
			Author: struct {
				ID        int    `json:"id"`
				Username  string `json:"username"`
				Email     string `json:"email"`
				Name      string `json:"name"`
				State     string `json:"state"`
				AvatarURL string `json:"avatar_url"`
				WebURL    string `json:"web_url"`
			}{
				ID:        1,
				Username:  "venky333",
				Email:     "",
				Name:      "Venkatesh Thalluri",
				State:     "active",
				AvatarURL: "https://www.gravatar.com/avatar/00afb8fb6ab07c3ee3e9c1f38777e2f4?s=80&d=identicon",
				WebURL:    "http://localhost:3000/venky333",
			},
			System:       false,
			ExpiresAt:    nil,
			UpdatedAt:    nil,
			CreatedAt:    nil,
			NoteableID:   3,
			NoteableType: "Epic",
			CommitID:     "",
			Position:     nil,
			Resolvable:   false,
			Resolved:     false,
			ResolvedBy: struct {
				ID        int    `json:"id"`
				Username  string `json:"username"`
				Email     string `json:"email"`
				Name      string `json:"name"`
				State     string `json:"state"`
				AvatarURL string `json:"avatar_url"`
				WebURL    string `json:"web_url"`
			}{},
			NoteableIID: 0,
		}},
	}

	d, resp, err := client.Discussions.GetEpicDiscussion(5, 11, "6a9c1750b37d513a43987b574953fceb50b03ce7", nil, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, d)

	d, resp, err = client.Discussions.GetEpicDiscussion(5.01, 11, "6a9c1750b37d513a43987b574953fceb50b03ce7", nil, nil)
	require.EqualError(t, err, "invalid ID type 5.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, d)

	d, resp, err = client.Discussions.GetEpicDiscussion(5, 11, "6a9c1750b37d513a43987b574953fceb50b03ce7", nil, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, d)

	d, resp, err = client.Discussions.GetEpicDiscussion(3, 11, "6a9c1750b37d513a43987b574953fceb50b03ce7", nil, nil)
	require.Error(t, err)
	require.Nil(t, d)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestDiscussionsService_CreateEpicDiscussion(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/5/epics/11/discussions", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		fmt.Fprintf(w, `
		  {
			"id": "6a9c1750b37d513a43987b574953fceb50b03ce7",
			"individual_note": false,
			"notes": [
			  {
				"id": 1126,
				"type": "DiscussionNote",
				"body": "discussion text",
				"attachment": null,
				"author": {
				  "id": 1,
				  "name": "Venkatesh Thalluri",
				  "username": "venky333",
				  "state": "active",
				  "avatar_url": "https://www.gravatar.com/avatar/00afb8fb6ab07c3ee3e9c1f38777e2f4?s=80&d=identicon",
				  "web_url": "http://localhost:3000/venky333"
				},
				"system": false,
				"noteable_id": 3,
				"noteable_type": "Epic",
				"noteable_iid": null
			  }
			]
		  }
		`)
	})

	want := &Discussion{
		ID:             "6a9c1750b37d513a43987b574953fceb50b03ce7",
		IndividualNote: false,
		Notes: []*Note{{
			ID:         1126,
			Type:       "DiscussionNote",
			Body:       "discussion text",
			Attachment: "",
			Title:      "",
			FileName:   "",
			Author: struct {
				ID        int    `json:"id"`
				Username  string `json:"username"`
				Email     string `json:"email"`
				Name      string `json:"name"`
				State     string `json:"state"`
				AvatarURL string `json:"avatar_url"`
				WebURL    string `json:"web_url"`
			}{
				ID:        1,
				Username:  "venky333",
				Email:     "",
				Name:      "Venkatesh Thalluri",
				State:     "active",
				AvatarURL: "https://www.gravatar.com/avatar/00afb8fb6ab07c3ee3e9c1f38777e2f4?s=80&d=identicon",
				WebURL:    "http://localhost:3000/venky333",
			},
			System:       false,
			ExpiresAt:    nil,
			UpdatedAt:    nil,
			CreatedAt:    nil,
			NoteableID:   3,
			NoteableType: "Epic",
			CommitID:     "",
			Position:     nil,
			Resolvable:   false,
			Resolved:     false,
			ResolvedBy: struct {
				ID        int    `json:"id"`
				Username  string `json:"username"`
				Email     string `json:"email"`
				Name      string `json:"name"`
				State     string `json:"state"`
				AvatarURL string `json:"avatar_url"`
				WebURL    string `json:"web_url"`
			}{},
			NoteableIID: 0,
		}},
	}

	d, resp, err := client.Discussions.CreateEpicDiscussion(5, 11, nil, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, d)

	d, resp, err = client.Discussions.CreateEpicDiscussion(5.01, 11, nil, nil)
	require.EqualError(t, err, "invalid ID type 5.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, d)

	d, resp, err = client.Discussions.CreateEpicDiscussion(5, 11, nil, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, d)

	d, resp, err = client.Discussions.CreateEpicDiscussion(3, 11, nil, nil)
	require.Error(t, err)
	require.Nil(t, d)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestDiscussionsService_AddEpicDiscussionNote(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/5/epics/11/discussions/6a9c1750b37d513a43987b574953fceb50b03ce7/notes", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		fmt.Fprintf(w, `
		  {
			"id": 302,
			"body": "closed",
			"attachment": null,
			"author": {
			  "id": 1,
			  "username": "venky333",
			  "email": "venky333@example.com",
			  "name": "venky333",
			  "state": "active"
			},
			"system": true,
			"noteable_id": 377,
			"noteable_type": "Epic",
			"noteable_iid": 377,
			"resolvable": false,
			"confidential": false
		  }
		`)
	})

	want := &Note{
		ID:         302,
		Type:       "",
		Body:       "closed",
		Attachment: "",
		Title:      "",
		FileName:   "",
		Author: struct {
			ID        int    `json:"id"`
			Username  string `json:"username"`
			Email     string `json:"email"`
			Name      string `json:"name"`
			State     string `json:"state"`
			AvatarURL string `json:"avatar_url"`
			WebURL    string `json:"web_url"`
		}{
			ID:        1,
			Username:  "venky333",
			Email:     "venky333@example.com",
			Name:      "venky333",
			State:     "active",
			AvatarURL: "",
			WebURL:    "",
		},
		System:       true,
		ExpiresAt:    nil,
		UpdatedAt:    nil,
		CreatedAt:    nil,
		NoteableID:   377,
		NoteableType: "Epic",
		CommitID:     "",
		Position:     nil,
		Resolvable:   false,
		Resolved:     false,
		ResolvedBy: struct {
			ID        int    `json:"id"`
			Username  string `json:"username"`
			Email     string `json:"email"`
			Name      string `json:"name"`
			State     string `json:"state"`
			AvatarURL string `json:"avatar_url"`
			WebURL    string `json:"web_url"`
		}{},
		NoteableIID: 377,
	}

	n, resp, err := client.Discussions.AddEpicDiscussionNote(5, 11, "6a9c1750b37d513a43987b574953fceb50b03ce7", nil, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, n)

	n, resp, err = client.Discussions.AddEpicDiscussionNote(5.01, 11, "6a9c1750b37d513a43987b574953fceb50b03ce7", nil, nil)
	require.EqualError(t, err, "invalid ID type 5.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, n)

	n, resp, err = client.Discussions.AddEpicDiscussionNote(5, 11, "6a9c1750b37d513a43987b574953fceb50b03ce7", nil, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, n)

	n, resp, err = client.Discussions.AddEpicDiscussionNote(3, 11, "6a9c1750b37d513a43987b574953fceb50b03ce7", nil, nil)
	require.Error(t, err)
	require.Nil(t, n)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestDiscussionsService_UpdateEpicDiscussionNote(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/5/epics/11/discussions/6a9c1750b37d513a43987b574953fceb50b03ce7/notes/302", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPut)
		fmt.Fprintf(w, `
		  {
			"id": 302,
			"body": "closed",
			"attachment": null,
			"author": {
			  "id": 1,
			  "username": "venky333",
			  "email": "venky333@example.com",
			  "name": "venky333",
			  "state": "active"
			},
			"system": true,
			"noteable_id": 377,
			"noteable_type": "Epic",
			"noteable_iid": 377,
			"resolvable": false,
			"confidential": false
		  }
		`)
	})

	want := &Note{
		ID:         302,
		Type:       "",
		Body:       "closed",
		Attachment: "",
		Title:      "",
		FileName:   "",
		Author: struct {
			ID        int    `json:"id"`
			Username  string `json:"username"`
			Email     string `json:"email"`
			Name      string `json:"name"`
			State     string `json:"state"`
			AvatarURL string `json:"avatar_url"`
			WebURL    string `json:"web_url"`
		}{
			ID:        1,
			Username:  "venky333",
			Email:     "venky333@example.com",
			Name:      "venky333",
			State:     "active",
			AvatarURL: "",
			WebURL:    "",
		},
		System:       true,
		ExpiresAt:    nil,
		UpdatedAt:    nil,
		CreatedAt:    nil,
		NoteableID:   377,
		NoteableType: "Epic",
		CommitID:     "",
		Position:     nil,
		Resolvable:   false,
		Resolved:     false,
		ResolvedBy: struct {
			ID        int    `json:"id"`
			Username  string `json:"username"`
			Email     string `json:"email"`
			Name      string `json:"name"`
			State     string `json:"state"`
			AvatarURL string `json:"avatar_url"`
			WebURL    string `json:"web_url"`
		}{},
		NoteableIID: 377,
	}

	n, resp, err := client.Discussions.UpdateEpicDiscussionNote(5, 11, "6a9c1750b37d513a43987b574953fceb50b03ce7", 302, nil, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, n)

	n, resp, err = client.Discussions.UpdateEpicDiscussionNote(5.01, 11, "6a9c1750b37d513a43987b574953fceb50b03ce7", 302, nil, nil)
	require.EqualError(t, err, "invalid ID type 5.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, n)

	n, resp, err = client.Discussions.UpdateEpicDiscussionNote(5, 11, "6a9c1750b37d513a43987b574953fceb50b03ce7", 302, nil, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, n)

	n, resp, err = client.Discussions.UpdateEpicDiscussionNote(3, 11, "6a9c1750b37d513a43987b574953fceb50b03ce7", 302, nil, nil)
	require.Error(t, err)
	require.Nil(t, n)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestDiscussionsService_DeleteEpicDiscussionNote(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/5/epics/11/discussions/6a9c1750b37d513a43987b574953fceb50b03ce7/notes/302", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodDelete)
	})

	resp, err := client.Discussions.DeleteEpicDiscussionNote(5, 11, "6a9c1750b37d513a43987b574953fceb50b03ce7", 302, nil, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)

	resp, err = client.Discussions.DeleteEpicDiscussionNote(5.01, 11, "6a9c1750b37d513a43987b574953fceb50b03ce7", 302, nil, nil)
	require.EqualError(t, err, "invalid ID type 5.01, the ID must be an int or a string")
	require.Nil(t, resp)

	resp, err = client.Discussions.DeleteEpicDiscussionNote(5, 11, "6a9c1750b37d513a43987b574953fceb50b03ce7", 302, nil, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)

	resp, err = client.Discussions.DeleteEpicDiscussionNote(3, 11, "6a9c1750b37d513a43987b574953fceb50b03ce7", 302, nil, nil)
	require.Error(t, err)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestDiscussionsService_ListMergeRequestDiscussions(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/5/merge_requests/11/discussions", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
			[
			  {
				"id": "6a9c1750b37d513a43987b574953fceb50b03ce7",
				"individual_note": false,
				"notes": [
				  {
					"id": 1126,
					"type": "DiscussionNote",
					"body": "discussion text",
					"attachment": null,
					"author": {
					  "id": 1,
					  "name": "Venkatesh Thalluri",
					  "username": "venky333",
					  "state": "active",
					  "avatar_url": "https://www.gravatar.com/avatar/00afb8fb6ab07c3ee3e9c1f38777e2f4?s=80&d=identicon",
					  "web_url": "http://localhost:3000/venky333"
					},
					"system": false,
					"noteable_id": 3,
					"noteable_type": "Merge request",
					"noteable_iid": null
				  }
				]
			  }
			]
		`)
	})

	want := []*Discussion{{
		ID:             "6a9c1750b37d513a43987b574953fceb50b03ce7",
		IndividualNote: false,
		Notes: []*Note{{
			ID:         1126,
			Type:       "DiscussionNote",
			Body:       "discussion text",
			Attachment: "",
			Title:      "",
			FileName:   "",
			Author: struct {
				ID        int    `json:"id"`
				Username  string `json:"username"`
				Email     string `json:"email"`
				Name      string `json:"name"`
				State     string `json:"state"`
				AvatarURL string `json:"avatar_url"`
				WebURL    string `json:"web_url"`
			}{
				ID:        1,
				Username:  "venky333",
				Email:     "",
				Name:      "Venkatesh Thalluri",
				State:     "active",
				AvatarURL: "https://www.gravatar.com/avatar/00afb8fb6ab07c3ee3e9c1f38777e2f4?s=80&d=identicon",
				WebURL:    "http://localhost:3000/venky333",
			},
			System:       false,
			ExpiresAt:    nil,
			UpdatedAt:    nil,
			CreatedAt:    nil,
			NoteableID:   3,
			NoteableType: "Merge request",
			CommitID:     "",
			Position:     nil,
			Resolvable:   false,
			Resolved:     false,
			ResolvedBy: struct {
				ID        int    `json:"id"`
				Username  string `json:"username"`
				Email     string `json:"email"`
				Name      string `json:"name"`
				State     string `json:"state"`
				AvatarURL string `json:"avatar_url"`
				WebURL    string `json:"web_url"`
			}{},
			NoteableIID: 0,
		}},
	}}

	ds, resp, err := client.Discussions.ListMergeRequestDiscussions(5, 11, nil, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, ds)

	ds, resp, err = client.Discussions.ListMergeRequestDiscussions(5.01, 11, nil, nil)
	require.EqualError(t, err, "invalid ID type 5.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, ds)

	ds, resp, err = client.Discussions.ListMergeRequestDiscussions(5, 11, nil, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, ds)

	ds, resp, err = client.Discussions.ListMergeRequestDiscussions(3, 11, nil, nil)
	require.Error(t, err)
	require.Nil(t, ds)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestDiscussionsService_GetMergeRequestDiscussion(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/5/merge_requests/11/discussions/6a9c1750b37d513a43987b574953fceb50b03ce7", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
		  {
			"id": "6a9c1750b37d513a43987b574953fceb50b03ce7",
			"individual_note": false,
			"notes": [
			  {
				"id": 1126,
				"type": "DiscussionNote",
				"body": "discussion text",
				"attachment": null,
				"author": {
				  "id": 1,
				  "name": "Venkatesh Thalluri",
				  "username": "venky333",
				  "state": "active",
				  "avatar_url": "https://www.gravatar.com/avatar/00afb8fb6ab07c3ee3e9c1f38777e2f4?s=80&d=identicon",
				  "web_url": "http://localhost:3000/venky333"
				},
				"system": false,
				"noteable_id": 3,
				"noteable_type": "Merge request",
				"noteable_iid": null
			  }
			]
		  }
		`)
	})

	want := &Discussion{
		ID:             "6a9c1750b37d513a43987b574953fceb50b03ce7",
		IndividualNote: false,
		Notes: []*Note{{
			ID:         1126,
			Type:       "DiscussionNote",
			Body:       "discussion text",
			Attachment: "",
			Title:      "",
			FileName:   "",
			Author: struct {
				ID        int    `json:"id"`
				Username  string `json:"username"`
				Email     string `json:"email"`
				Name      string `json:"name"`
				State     string `json:"state"`
				AvatarURL string `json:"avatar_url"`
				WebURL    string `json:"web_url"`
			}{
				ID:        1,
				Username:  "venky333",
				Email:     "",
				Name:      "Venkatesh Thalluri",
				State:     "active",
				AvatarURL: "https://www.gravatar.com/avatar/00afb8fb6ab07c3ee3e9c1f38777e2f4?s=80&d=identicon",
				WebURL:    "http://localhost:3000/venky333",
			},
			System:       false,
			ExpiresAt:    nil,
			UpdatedAt:    nil,
			CreatedAt:    nil,
			NoteableID:   3,
			NoteableType: "Merge request",
			CommitID:     "",
			Position:     nil,
			Resolvable:   false,
			Resolved:     false,
			ResolvedBy: struct {
				ID        int    `json:"id"`
				Username  string `json:"username"`
				Email     string `json:"email"`
				Name      string `json:"name"`
				State     string `json:"state"`
				AvatarURL string `json:"avatar_url"`
				WebURL    string `json:"web_url"`
			}{},
			NoteableIID: 0,
		}},
	}

	d, resp, err := client.Discussions.GetMergeRequestDiscussion(5, 11, "6a9c1750b37d513a43987b574953fceb50b03ce7", nil, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, d)

	d, resp, err = client.Discussions.GetMergeRequestDiscussion(5.01, 11, "6a9c1750b37d513a43987b574953fceb50b03ce7", nil, nil)
	require.EqualError(t, err, "invalid ID type 5.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, d)

	d, resp, err = client.Discussions.GetMergeRequestDiscussion(5, 11, "6a9c1750b37d513a43987b574953fceb50b03ce7", nil, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, d)

	d, resp, err = client.Discussions.GetMergeRequestDiscussion(3, 11, "6a9c1750b37d513a43987b574953fceb50b03ce7", nil, nil)
	require.Error(t, err)
	require.Nil(t, d)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestDiscussionsService_CreateMergeRequestDiscussion(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/5/merge_requests/11/discussions", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		fmt.Fprintf(w, `
		  {
			"id": "6a9c1750b37d513a43987b574953fceb50b03ce7",
			"individual_note": false,
			"notes": [
			  {
				"id": 1126,
				"type": "DiscussionNote",
				"body": "discussion text",
				"attachment": null,
				"author": {
				  "id": 1,
				  "name": "Venkatesh Thalluri",
				  "username": "venky333",
				  "state": "active",
				  "avatar_url": "https://www.gravatar.com/avatar/00afb8fb6ab07c3ee3e9c1f38777e2f4?s=80&d=identicon",
				  "web_url": "http://localhost:3000/venky333"
				},
				"system": false,
				"noteable_id": 3,
				"noteable_type": "Merge request",
				"noteable_iid": null
			  }
			]
		  }
		`)
	})

	want := &Discussion{
		ID:             "6a9c1750b37d513a43987b574953fceb50b03ce7",
		IndividualNote: false,
		Notes: []*Note{{
			ID:         1126,
			Type:       "DiscussionNote",
			Body:       "discussion text",
			Attachment: "",
			Title:      "",
			FileName:   "",
			Author: struct {
				ID        int    `json:"id"`
				Username  string `json:"username"`
				Email     string `json:"email"`
				Name      string `json:"name"`
				State     string `json:"state"`
				AvatarURL string `json:"avatar_url"`
				WebURL    string `json:"web_url"`
			}{
				ID:        1,
				Username:  "venky333",
				Email:     "",
				Name:      "Venkatesh Thalluri",
				State:     "active",
				AvatarURL: "https://www.gravatar.com/avatar/00afb8fb6ab07c3ee3e9c1f38777e2f4?s=80&d=identicon",
				WebURL:    "http://localhost:3000/venky333",
			},
			System:       false,
			ExpiresAt:    nil,
			UpdatedAt:    nil,
			CreatedAt:    nil,
			NoteableID:   3,
			NoteableType: "Merge request",
			CommitID:     "",
			Position:     nil,
			Resolvable:   false,
			Resolved:     false,
			ResolvedBy: struct {
				ID        int    `json:"id"`
				Username  string `json:"username"`
				Email     string `json:"email"`
				Name      string `json:"name"`
				State     string `json:"state"`
				AvatarURL string `json:"avatar_url"`
				WebURL    string `json:"web_url"`
			}{},
			NoteableIID: 0,
		}},
	}

	d, resp, err := client.Discussions.CreateMergeRequestDiscussion(5, 11, nil, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, d)

	d, resp, err = client.Discussions.CreateMergeRequestDiscussion(5.01, 11, nil, nil)
	require.EqualError(t, err, "invalid ID type 5.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, d)

	d, resp, err = client.Discussions.CreateMergeRequestDiscussion(5, 11, nil, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, d)

	d, resp, err = client.Discussions.CreateMergeRequestDiscussion(3, 11, nil, nil)
	require.Error(t, err)
	require.Nil(t, d)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestDiscussionsService_ResolveMergeRequestDiscussion(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/5/merge_requests/11/discussions/6a9c1750b37d513a43987b574953fceb50b03ce7", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPut)
		fmt.Fprintf(w, `
		  {
			"id": "6a9c1750b37d513a43987b574953fceb50b03ce7",
			"individual_note": false,
			"notes": [
			  {
				"id": 1126,
				"type": "DiscussionNote",
				"body": "discussion text",
				"attachment": null,
				"author": {
				  "id": 1,
				  "name": "Venkatesh Thalluri",
				  "username": "venky333",
				  "state": "active",
				  "avatar_url": "https://www.gravatar.com/avatar/00afb8fb6ab07c3ee3e9c1f38777e2f4?s=80&d=identicon",
				  "web_url": "http://localhost:3000/venky333"
				},
				"system": false,
				"noteable_id": 3,
				"noteable_type": "Merge request",
				"noteable_iid": null
			  }
			]
		  }
		`)
	})

	want := &Discussion{
		ID:             "6a9c1750b37d513a43987b574953fceb50b03ce7",
		IndividualNote: false,
		Notes: []*Note{{
			ID:         1126,
			Type:       "DiscussionNote",
			Body:       "discussion text",
			Attachment: "",
			Title:      "",
			FileName:   "",
			Author: struct {
				ID        int    `json:"id"`
				Username  string `json:"username"`
				Email     string `json:"email"`
				Name      string `json:"name"`
				State     string `json:"state"`
				AvatarURL string `json:"avatar_url"`
				WebURL    string `json:"web_url"`
			}{
				ID:        1,
				Username:  "venky333",
				Email:     "",
				Name:      "Venkatesh Thalluri",
				State:     "active",
				AvatarURL: "https://www.gravatar.com/avatar/00afb8fb6ab07c3ee3e9c1f38777e2f4?s=80&d=identicon",
				WebURL:    "http://localhost:3000/venky333",
			},
			System:       false,
			ExpiresAt:    nil,
			UpdatedAt:    nil,
			CreatedAt:    nil,
			NoteableID:   3,
			NoteableType: "Merge request",
			CommitID:     "",
			Position:     nil,
			Resolvable:   false,
			Resolved:     false,
			ResolvedBy: struct {
				ID        int    `json:"id"`
				Username  string `json:"username"`
				Email     string `json:"email"`
				Name      string `json:"name"`
				State     string `json:"state"`
				AvatarURL string `json:"avatar_url"`
				WebURL    string `json:"web_url"`
			}{},
			NoteableIID: 0,
		}},
	}

	d, resp, err := client.Discussions.ResolveMergeRequestDiscussion(5, 11, "6a9c1750b37d513a43987b574953fceb50b03ce7", nil, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, d)

	d, resp, err = client.Discussions.ResolveMergeRequestDiscussion(5.01, 11, "6a9c1750b37d513a43987b574953fceb50b03ce7", nil, nil)
	require.EqualError(t, err, "invalid ID type 5.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, d)

	d, resp, err = client.Discussions.ResolveMergeRequestDiscussion(5, 11, "6a9c1750b37d513a43987b574953fceb50b03ce7", nil, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, d)

	d, resp, err = client.Discussions.ResolveMergeRequestDiscussion(3, 11, "6a9c1750b37d513a43987b574953fceb50b03ce7", nil, nil)
	require.Error(t, err)
	require.Nil(t, d)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestDiscussionsService_AddMergeRequestDiscussionNote(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/5/merge_requests/11/discussions/6a9c1750b37d513a43987b574953fceb50b03ce7/notes", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		fmt.Fprintf(w, `
		  {
			"id": 302,
			"body": "closed",
			"attachment": null,
			"author": {
			  "id": 1,
			  "username": "venky333",
			  "email": "venky333@example.com",
			  "name": "venky333",
			  "state": "active"
			},
			"system": true,
			"noteable_id": 377,
			"noteable_type": "Merge request",
			"noteable_iid": 377,
			"resolvable": false,
			"confidential": false
		  }
		`)
	})

	want := &Note{
		ID:         302,
		Type:       "",
		Body:       "closed",
		Attachment: "",
		Title:      "",
		FileName:   "",
		Author: struct {
			ID        int    `json:"id"`
			Username  string `json:"username"`
			Email     string `json:"email"`
			Name      string `json:"name"`
			State     string `json:"state"`
			AvatarURL string `json:"avatar_url"`
			WebURL    string `json:"web_url"`
		}{
			ID:        1,
			Username:  "venky333",
			Email:     "venky333@example.com",
			Name:      "venky333",
			State:     "active",
			AvatarURL: "",
			WebURL:    "",
		},
		System:       true,
		ExpiresAt:    nil,
		UpdatedAt:    nil,
		CreatedAt:    nil,
		NoteableID:   377,
		NoteableType: "Merge request",
		CommitID:     "",
		Position:     nil,
		Resolvable:   false,
		Resolved:     false,
		ResolvedBy: struct {
			ID        int    `json:"id"`
			Username  string `json:"username"`
			Email     string `json:"email"`
			Name      string `json:"name"`
			State     string `json:"state"`
			AvatarURL string `json:"avatar_url"`
			WebURL    string `json:"web_url"`
		}{},
		NoteableIID: 377,
	}

	n, resp, err := client.Discussions.AddMergeRequestDiscussionNote(5, 11, "6a9c1750b37d513a43987b574953fceb50b03ce7", nil, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, n)

	n, resp, err = client.Discussions.AddMergeRequestDiscussionNote(5.01, 11, "6a9c1750b37d513a43987b574953fceb50b03ce7", nil, nil)
	require.EqualError(t, err, "invalid ID type 5.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, n)

	n, resp, err = client.Discussions.AddMergeRequestDiscussionNote(5, 11, "6a9c1750b37d513a43987b574953fceb50b03ce7", nil, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, n)

	n, resp, err = client.Discussions.AddMergeRequestDiscussionNote(3, 11, "6a9c1750b37d513a43987b574953fceb50b03ce7", nil, nil)
	require.Error(t, err)
	require.Nil(t, n)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestDiscussionsService_UpdateMergeRequestDiscussionNote(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/5/merge_requests/11/discussions/6a9c1750b37d513a43987b574953fceb50b03ce7/notes/302", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPut)
		fmt.Fprintf(w, `
		  {
			"id": 302,
			"body": "closed",
			"attachment": null,
			"author": {
			  "id": 1,
			  "username": "venky333",
			  "email": "venky333@example.com",
			  "name": "venky333",
			  "state": "active"
			},
			"system": true,
			"noteable_id": 377,
			"noteable_type": "Merge request",
			"noteable_iid": 377,
			"resolvable": false,
			"confidential": false
		  }
		`)
	})

	want := &Note{
		ID:         302,
		Type:       "",
		Body:       "closed",
		Attachment: "",
		Title:      "",
		FileName:   "",
		Author: struct {
			ID        int    `json:"id"`
			Username  string `json:"username"`
			Email     string `json:"email"`
			Name      string `json:"name"`
			State     string `json:"state"`
			AvatarURL string `json:"avatar_url"`
			WebURL    string `json:"web_url"`
		}{
			ID:        1,
			Username:  "venky333",
			Email:     "venky333@example.com",
			Name:      "venky333",
			State:     "active",
			AvatarURL: "",
			WebURL:    "",
		},
		System:       true,
		ExpiresAt:    nil,
		UpdatedAt:    nil,
		CreatedAt:    nil,
		NoteableID:   377,
		NoteableType: "Merge request",
		CommitID:     "",
		Position:     nil,
		Resolvable:   false,
		Resolved:     false,
		ResolvedBy: struct {
			ID        int    `json:"id"`
			Username  string `json:"username"`
			Email     string `json:"email"`
			Name      string `json:"name"`
			State     string `json:"state"`
			AvatarURL string `json:"avatar_url"`
			WebURL    string `json:"web_url"`
		}{},
		NoteableIID: 377,
	}

	n, resp, err := client.Discussions.UpdateMergeRequestDiscussionNote(5, 11, "6a9c1750b37d513a43987b574953fceb50b03ce7", 302, nil, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, n)

	n, resp, err = client.Discussions.UpdateMergeRequestDiscussionNote(5.01, 11, "6a9c1750b37d513a43987b574953fceb50b03ce7", 302, nil, nil)
	require.EqualError(t, err, "invalid ID type 5.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, n)

	n, resp, err = client.Discussions.UpdateMergeRequestDiscussionNote(5, 11, "6a9c1750b37d513a43987b574953fceb50b03ce7", 302, nil, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, n)

	n, resp, err = client.Discussions.UpdateMergeRequestDiscussionNote(3, 11, "6a9c1750b37d513a43987b574953fceb50b03ce7", 302, nil, nil)
	require.Error(t, err)
	require.Nil(t, n)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestDiscussionsService_DeleteMergeRequestDiscussionNote(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/5/merge_requests/11/discussions/6a9c1750b37d513a43987b574953fceb50b03ce7/notes/302", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodDelete)
	})

	resp, err := client.Discussions.DeleteMergeRequestDiscussionNote(5, 11, "6a9c1750b37d513a43987b574953fceb50b03ce7", 302, nil, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)

	resp, err = client.Discussions.DeleteMergeRequestDiscussionNote(5.01, 11, "6a9c1750b37d513a43987b574953fceb50b03ce7", 302, nil, nil)
	require.EqualError(t, err, "invalid ID type 5.01, the ID must be an int or a string")
	require.Nil(t, resp)

	resp, err = client.Discussions.DeleteMergeRequestDiscussionNote(5, 11, "6a9c1750b37d513a43987b574953fceb50b03ce7", 302, nil, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)

	resp, err = client.Discussions.DeleteMergeRequestDiscussionNote(3, 11, "6a9c1750b37d513a43987b574953fceb50b03ce7", 302, nil, nil)
	require.Error(t, err)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestDiscussionsService_ListCommitDiscussions(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/5/repository/commits/abc123/discussions", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
			[
			  {
				"id": "6a9c1750b37d513a43987b574953fceb50b03ce7",
				"individual_note": false,
				"notes": [
				  {
					"id": 1126,
					"type": "DiscussionNote",
					"body": "discussion text",
					"attachment": null,
					"author": {
					  "id": 1,
					  "name": "Venkatesh Thalluri",
					  "username": "venky333",
					  "state": "active",
					  "avatar_url": "https://www.gravatar.com/avatar/00afb8fb6ab07c3ee3e9c1f38777e2f4?s=80&d=identicon",
					  "web_url": "http://localhost:3000/venky333"
					},
					"system": false,
					"noteable_id": 3,
					"noteable_type": "Commit",
					"noteable_iid": null
				  }
				]
			  }
			]
		`)
	})

	want := []*Discussion{{
		ID:             "6a9c1750b37d513a43987b574953fceb50b03ce7",
		IndividualNote: false,
		Notes: []*Note{{
			ID:         1126,
			Type:       "DiscussionNote",
			Body:       "discussion text",
			Attachment: "",
			Title:      "",
			FileName:   "",
			Author: struct {
				ID        int    `json:"id"`
				Username  string `json:"username"`
				Email     string `json:"email"`
				Name      string `json:"name"`
				State     string `json:"state"`
				AvatarURL string `json:"avatar_url"`
				WebURL    string `json:"web_url"`
			}{
				ID:        1,
				Username:  "venky333",
				Email:     "",
				Name:      "Venkatesh Thalluri",
				State:     "active",
				AvatarURL: "https://www.gravatar.com/avatar/00afb8fb6ab07c3ee3e9c1f38777e2f4?s=80&d=identicon",
				WebURL:    "http://localhost:3000/venky333",
			},
			System:       false,
			ExpiresAt:    nil,
			UpdatedAt:    nil,
			CreatedAt:    nil,
			NoteableID:   3,
			NoteableType: "Commit",
			CommitID:     "",
			Position:     nil,
			Resolvable:   false,
			Resolved:     false,
			ResolvedBy: struct {
				ID        int    `json:"id"`
				Username  string `json:"username"`
				Email     string `json:"email"`
				Name      string `json:"name"`
				State     string `json:"state"`
				AvatarURL string `json:"avatar_url"`
				WebURL    string `json:"web_url"`
			}{},
			NoteableIID: 0,
		}},
	}}

	ds, resp, err := client.Discussions.ListCommitDiscussions(5, "abc123", nil, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, ds)

	ds, resp, err = client.Discussions.ListCommitDiscussions(5.01, "abc123", nil, nil)
	require.EqualError(t, err, "invalid ID type 5.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, ds)

	ds, resp, err = client.Discussions.ListCommitDiscussions(5, "abc123", nil, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, ds)

	ds, resp, err = client.Discussions.ListCommitDiscussions(3, "abc123", nil, nil)
	require.Error(t, err)
	require.Nil(t, ds)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestDiscussionsService_GetCommitDiscussion(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/5/repository/commits/abc123/discussions/6a9c1750b37d513a43987b574953fceb50b03ce7", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
		  {
			"id": "6a9c1750b37d513a43987b574953fceb50b03ce7",
			"individual_note": false,
			"notes": [
			  {
				"id": 1126,
				"type": "DiscussionNote",
				"body": "discussion text",
				"attachment": null,
				"author": {
				  "id": 1,
				  "name": "Venkatesh Thalluri",
				  "username": "venky333",
				  "state": "active",
				  "avatar_url": "https://www.gravatar.com/avatar/00afb8fb6ab07c3ee3e9c1f38777e2f4?s=80&d=identicon",
				  "web_url": "http://localhost:3000/venky333"
				},
				"system": false,
				"noteable_id": 3,
				"noteable_type": "Commit",
				"noteable_iid": null
			  }
			]
		  }
		`)
	})

	want := &Discussion{
		ID:             "6a9c1750b37d513a43987b574953fceb50b03ce7",
		IndividualNote: false,
		Notes: []*Note{{
			ID:         1126,
			Type:       "DiscussionNote",
			Body:       "discussion text",
			Attachment: "",
			Title:      "",
			FileName:   "",
			Author: struct {
				ID        int    `json:"id"`
				Username  string `json:"username"`
				Email     string `json:"email"`
				Name      string `json:"name"`
				State     string `json:"state"`
				AvatarURL string `json:"avatar_url"`
				WebURL    string `json:"web_url"`
			}{
				ID:        1,
				Username:  "venky333",
				Email:     "",
				Name:      "Venkatesh Thalluri",
				State:     "active",
				AvatarURL: "https://www.gravatar.com/avatar/00afb8fb6ab07c3ee3e9c1f38777e2f4?s=80&d=identicon",
				WebURL:    "http://localhost:3000/venky333",
			},
			System:       false,
			ExpiresAt:    nil,
			UpdatedAt:    nil,
			CreatedAt:    nil,
			NoteableID:   3,
			NoteableType: "Commit",
			CommitID:     "",
			Position:     nil,
			Resolvable:   false,
			Resolved:     false,
			ResolvedBy: struct {
				ID        int    `json:"id"`
				Username  string `json:"username"`
				Email     string `json:"email"`
				Name      string `json:"name"`
				State     string `json:"state"`
				AvatarURL string `json:"avatar_url"`
				WebURL    string `json:"web_url"`
			}{},
			NoteableIID: 0,
		}},
	}

	d, resp, err := client.Discussions.GetCommitDiscussion(5, "abc123", "6a9c1750b37d513a43987b574953fceb50b03ce7", nil, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, d)

	d, resp, err = client.Discussions.GetCommitDiscussion(5.01, "abc123", "6a9c1750b37d513a43987b574953fceb50b03ce7", nil, nil)
	require.EqualError(t, err, "invalid ID type 5.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, d)

	d, resp, err = client.Discussions.GetCommitDiscussion(5, "abc123", "6a9c1750b37d513a43987b574953fceb50b03ce7", nil, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, d)

	d, resp, err = client.Discussions.GetCommitDiscussion(3, "abc123", "6a9c1750b37d513a43987b574953fceb50b03ce7", nil, nil)
	require.Error(t, err)
	require.Nil(t, d)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestDiscussionsService_CreateCommitDiscussion(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/5/repository/commits/abc123/discussions", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		fmt.Fprintf(w, `
		  {
			"id": "6a9c1750b37d513a43987b574953fceb50b03ce7",
			"individual_note": false,
			"notes": [
			  {
				"id": 1126,
				"type": "DiscussionNote",
				"body": "discussion text",
				"attachment": null,
				"author": {
				  "id": 1,
				  "name": "Venkatesh Thalluri",
				  "username": "venky333",
				  "state": "active",
				  "avatar_url": "https://www.gravatar.com/avatar/00afb8fb6ab07c3ee3e9c1f38777e2f4?s=80&d=identicon",
				  "web_url": "http://localhost:3000/venky333"
				},
				"system": false,
				"noteable_id": 3,
				"noteable_type": "Commit",
				"noteable_iid": null
			  }
			]
		  }
		`)
	})

	want := &Discussion{
		ID:             "6a9c1750b37d513a43987b574953fceb50b03ce7",
		IndividualNote: false,
		Notes: []*Note{{
			ID:         1126,
			Type:       "DiscussionNote",
			Body:       "discussion text",
			Attachment: "",
			Title:      "",
			FileName:   "",
			Author: struct {
				ID        int    `json:"id"`
				Username  string `json:"username"`
				Email     string `json:"email"`
				Name      string `json:"name"`
				State     string `json:"state"`
				AvatarURL string `json:"avatar_url"`
				WebURL    string `json:"web_url"`
			}{
				ID:        1,
				Username:  "venky333",
				Email:     "",
				Name:      "Venkatesh Thalluri",
				State:     "active",
				AvatarURL: "https://www.gravatar.com/avatar/00afb8fb6ab07c3ee3e9c1f38777e2f4?s=80&d=identicon",
				WebURL:    "http://localhost:3000/venky333",
			},
			System:       false,
			ExpiresAt:    nil,
			UpdatedAt:    nil,
			CreatedAt:    nil,
			NoteableID:   3,
			NoteableType: "Commit",
			CommitID:     "",
			Position:     nil,
			Resolvable:   false,
			Resolved:     false,
			ResolvedBy: struct {
				ID        int    `json:"id"`
				Username  string `json:"username"`
				Email     string `json:"email"`
				Name      string `json:"name"`
				State     string `json:"state"`
				AvatarURL string `json:"avatar_url"`
				WebURL    string `json:"web_url"`
			}{},
			NoteableIID: 0,
		}},
	}

	d, resp, err := client.Discussions.CreateCommitDiscussion(5, "abc123", nil, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, d)

	d, resp, err = client.Discussions.CreateCommitDiscussion(5.01, "abc123", nil, nil)
	require.EqualError(t, err, "invalid ID type 5.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, d)

	d, resp, err = client.Discussions.CreateCommitDiscussion(5, "abc123", nil, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, d)

	d, resp, err = client.Discussions.CreateCommitDiscussion(3, "abc123", nil, nil)
	require.Error(t, err)
	require.Nil(t, d)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestDiscussionsService_AddCommitDiscussionNote(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/5/repository/commits/abc123/discussions/6a9c1750b37d513a43987b574953fceb50b03ce7/notes", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		fmt.Fprintf(w, `
		  {
			"id": 302,
			"body": "closed",
			"attachment": null,
			"author": {
			  "id": 1,
			  "username": "venky333",
			  "email": "venky333@example.com",
			  "name": "venky333",
			  "state": "active"
			},
			"system": true,
			"noteable_id": 377,
			"noteable_type": "Commit",
			"noteable_iid": 377,
			"resolvable": false,
			"confidential": false
		  }
		`)
	})

	want := &Note{
		ID:         302,
		Type:       "",
		Body:       "closed",
		Attachment: "",
		Title:      "",
		FileName:   "",
		Author: struct {
			ID        int    `json:"id"`
			Username  string `json:"username"`
			Email     string `json:"email"`
			Name      string `json:"name"`
			State     string `json:"state"`
			AvatarURL string `json:"avatar_url"`
			WebURL    string `json:"web_url"`
		}{
			ID:        1,
			Username:  "venky333",
			Email:     "venky333@example.com",
			Name:      "venky333",
			State:     "active",
			AvatarURL: "",
			WebURL:    "",
		},
		System:       true,
		ExpiresAt:    nil,
		UpdatedAt:    nil,
		CreatedAt:    nil,
		NoteableID:   377,
		NoteableType: "Commit",
		CommitID:     "",
		Position:     nil,
		Resolvable:   false,
		Resolved:     false,
		ResolvedBy: struct {
			ID        int    `json:"id"`
			Username  string `json:"username"`
			Email     string `json:"email"`
			Name      string `json:"name"`
			State     string `json:"state"`
			AvatarURL string `json:"avatar_url"`
			WebURL    string `json:"web_url"`
		}{},
		NoteableIID: 377,
	}

	n, resp, err := client.Discussions.AddCommitDiscussionNote(5, "abc123", "6a9c1750b37d513a43987b574953fceb50b03ce7", nil, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, n)

	n, resp, err = client.Discussions.AddCommitDiscussionNote(5.01, "abc123", "6a9c1750b37d513a43987b574953fceb50b03ce7", nil, nil)
	require.EqualError(t, err, "invalid ID type 5.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, n)

	n, resp, err = client.Discussions.AddCommitDiscussionNote(5, "abc123", "6a9c1750b37d513a43987b574953fceb50b03ce7", nil, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, n)

	n, resp, err = client.Discussions.AddCommitDiscussionNote(3, "abc123", "6a9c1750b37d513a43987b574953fceb50b03ce7", nil, nil)
	require.Error(t, err)
	require.Nil(t, n)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestDiscussionsService_UpdateCommitDiscussionNote(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/5/repository/commits/abc123/discussions/6a9c1750b37d513a43987b574953fceb50b03ce7/notes/302", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPut)
		fmt.Fprintf(w, `
		  {
			"id": 302,
			"body": "closed",
			"attachment": null,
			"author": {
			  "id": 1,
			  "username": "venky333",
			  "email": "venky333@example.com",
			  "name": "venky333",
			  "state": "active"
			},
			"system": true,
			"noteable_id": 377,
			"noteable_type": "Commit",
			"noteable_iid": 377,
			"resolvable": false,
			"confidential": false
		  }
		`)
	})

	want := &Note{
		ID:         302,
		Type:       "",
		Body:       "closed",
		Attachment: "",
		Title:      "",
		FileName:   "",
		Author: struct {
			ID        int    `json:"id"`
			Username  string `json:"username"`
			Email     string `json:"email"`
			Name      string `json:"name"`
			State     string `json:"state"`
			AvatarURL string `json:"avatar_url"`
			WebURL    string `json:"web_url"`
		}{
			ID:        1,
			Username:  "venky333",
			Email:     "venky333@example.com",
			Name:      "venky333",
			State:     "active",
			AvatarURL: "",
			WebURL:    "",
		},
		System:       true,
		ExpiresAt:    nil,
		UpdatedAt:    nil,
		CreatedAt:    nil,
		NoteableID:   377,
		NoteableType: "Commit",
		CommitID:     "",
		Position:     nil,
		Resolvable:   false,
		Resolved:     false,
		ResolvedBy: struct {
			ID        int    `json:"id"`
			Username  string `json:"username"`
			Email     string `json:"email"`
			Name      string `json:"name"`
			State     string `json:"state"`
			AvatarURL string `json:"avatar_url"`
			WebURL    string `json:"web_url"`
		}{},
		NoteableIID: 377,
	}

	n, resp, err := client.Discussions.UpdateCommitDiscussionNote(5, "abc123", "6a9c1750b37d513a43987b574953fceb50b03ce7", 302, nil, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, n)

	n, resp, err = client.Discussions.UpdateCommitDiscussionNote(5.01, "abc123", "6a9c1750b37d513a43987b574953fceb50b03ce7", 302, nil, nil)
	require.EqualError(t, err, "invalid ID type 5.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, n)

	n, resp, err = client.Discussions.UpdateCommitDiscussionNote(5, "abc123", "6a9c1750b37d513a43987b574953fceb50b03ce7", 302, nil, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, n)

	n, resp, err = client.Discussions.UpdateCommitDiscussionNote(3, "abc123", "6a9c1750b37d513a43987b574953fceb50b03ce7", 302, nil, nil)
	require.Error(t, err)
	require.Nil(t, n)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestDiscussionsService_DeleteCommitDiscussionNote(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/5/repository/commits/abc123/discussions/6a9c1750b37d513a43987b574953fceb50b03ce7/notes/302", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodDelete)
	})

	resp, err := client.Discussions.DeleteCommitDiscussionNote(5, "abc123", "6a9c1750b37d513a43987b574953fceb50b03ce7", 302, nil, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)

	resp, err = client.Discussions.DeleteCommitDiscussionNote(5.01, "abc123", "6a9c1750b37d513a43987b574953fceb50b03ce7", 302, nil, nil)
	require.EqualError(t, err, "invalid ID type 5.01, the ID must be an int or a string")
	require.Nil(t, resp)

	resp, err = client.Discussions.DeleteCommitDiscussionNote(5, "abc123", "6a9c1750b37d513a43987b574953fceb50b03ce7", 302, nil, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)

	resp, err = client.Discussions.DeleteCommitDiscussionNote(3, "abc123", "6a9c1750b37d513a43987b574953fceb50b03ce7", 302, nil, nil)
	require.Error(t, err)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/dockerfile_templates.go000066400000000000000000000055121475761473200255760ustar00rootroot00000000000000//
// Copyright 2022, FantasyTeddy
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"fmt"
	"net/http"
	"net/url"
)

// DockerfileTemplatesService handles communication with the Dockerfile
// templates related methods of the GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/templates/dockerfiles.html
type DockerfileTemplatesService struct {
	client *Client
}

// DockerfileTemplate represents a GitLab Dockerfile template.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/templates/dockerfiles.html
type DockerfileTemplate struct {
	Name    string `json:"name"`
	Content string `json:"content"`
}

// DockerfileTemplateListItem represents a GitLab Dockerfile template from the list.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/templates/dockerfiles.html
type DockerfileTemplateListItem struct {
	Key  string `json:"key"`
	Name string `json:"name"`
}

// ListDockerfileTemplatesOptions represents the available ListAllTemplates() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/templates/dockerfiles.html#list-dockerfile-templates
type ListDockerfileTemplatesOptions ListOptions

// ListTemplates get a list of available Dockerfile templates.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/templates/dockerfiles.html#list-dockerfile-templates
func (s *DockerfileTemplatesService) ListTemplates(opt *ListDockerfileTemplatesOptions, options ...RequestOptionFunc) ([]*DockerfileTemplateListItem, *Response, error) {
	req, err := s.client.NewRequest(http.MethodGet, "templates/dockerfiles", opt, options)
	if err != nil {
		return nil, nil, err
	}

	var gs []*DockerfileTemplateListItem
	resp, err := s.client.Do(req, &gs)
	if err != nil {
		return nil, resp, err
	}

	return gs, resp, nil
}

// GetTemplate get a single Dockerfile template.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/templates/dockerfiles.html#single-dockerfile-template
func (s *DockerfileTemplatesService) GetTemplate(key string, options ...RequestOptionFunc) (*DockerfileTemplate, *Response, error) {
	u := fmt.Sprintf("templates/dockerfiles/%s", url.PathEscape(key))

	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	g := new(DockerfileTemplate)
	resp, err := s.client.Do(req, g)
	if err != nil {
		return nil, resp, err
	}

	return g, resp, nil
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/dockerfile_templates_test.go000066400000000000000000000054151475761473200266370ustar00rootroot00000000000000//
// Copyright 2022, FantasyTeddy
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"fmt"
	"net/http"
	"reflect"
	"testing"
)

func TestDockerfileTemplatesService_ListTemplates(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/templates/dockerfiles", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `[
			{
			  "key":"Binary",
			  "name":"Binary"
			},
			{
			  "key":"Binary-alpine",
			  "name":"Binary-alpine"
			},
			{
			  "key":"Binary-scratch",
			  "name":"Binary-scratch"
			},
			{
			  "key":"Golang",
			  "name":"Golang"
			},
			{
			  "key":"Golang-alpine",
			  "name":"Golang-alpine"
			},
			{
			  "key":"Golang-scratch",
			  "name":"Golang-scratch"
			}
		  ]`)
	})

	templates, _, err := client.DockerfileTemplate.ListTemplates(&ListDockerfileTemplatesOptions{})
	if err != nil {
		t.Errorf("DockerfileTemplate.ListTemplates returned error: %v", err)
	}

	want := []*DockerfileTemplateListItem{
		{
			Key:  "Binary",
			Name: "Binary",
		},
		{
			Key:  "Binary-alpine",
			Name: "Binary-alpine",
		},
		{
			Key:  "Binary-scratch",
			Name: "Binary-scratch",
		},
		{
			Key:  "Golang",
			Name: "Golang",
		},
		{
			Key:  "Golang-alpine",
			Name: "Golang-alpine",
		},
		{
			Key:  "Golang-scratch",
			Name: "Golang-scratch",
		},
	}
	if !reflect.DeepEqual(want, templates) {
		t.Errorf("DockerfileTemplate.ListTemplates returned %+v, want %+v", templates, want)
	}
}

func TestDockerfileTemplatesService_GetTemplate(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/templates/dockerfiles/Binary", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `{
			"name": "Binary",
			"content": "# This file is a template, and might need editing before it works on your project."
		  }`)
	})

	template, _, err := client.DockerfileTemplate.GetTemplate("Binary")
	if err != nil {
		t.Errorf("DockerfileTemplate.GetTemplate returned error: %v", err)
	}

	want := &DockerfileTemplate{
		Name:    "Binary",
		Content: "# This file is a template, and might need editing before it works on your project.",
	}
	if !reflect.DeepEqual(want, template) {
		t.Errorf("DockerfileTemplate.GetTemplate returned %+v, want %+v", template, want)
	}
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/dora_metrics.go000066400000000000000000000067171475761473200240740ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"fmt"
	"net/http"
)

// DORAMetricsService handles communication with the DORA metrics related methods
// of the GitLab API.
//
// Gitlab API docs: https://docs.gitlab.com/ee/api/dora/metrics.html
type DORAMetricsService struct {
	client *Client
}

// DORAMetric represents a single DORA metric data point.
//
// Gitlab API docs: https://docs.gitlab.com/ee/api/dora/metrics.html
type DORAMetric struct {
	Date  string  `json:"date"`
	Value float64 `json:"value"`
}

// Gets a string representation of a DORAMetric data point
//
// GitLab API docs: https://docs.gitlab.com/ee/api/dora/metrics.html
func (m DORAMetric) String() string {
	return Stringify(m)
}

// GetDORAMetricsOptions represent the request body options for getting
// DORA metrics
//
// GitLab API docs: https://docs.gitlab.com/ee/api/dora/metrics.html
type GetDORAMetricsOptions struct {
	Metric           *DORAMetricType     `url:"metric,omitempty" json:"metric,omitempty"`
	EndDate          *ISOTime            `url:"end_date,omitempty" json:"end_date,omitempty"`
	EnvironmentTiers *[]string           `url:"environment_tiers,comma,omitempty" json:"environment_tiers,omitempty"`
	Interval         *DORAMetricInterval `url:"interval,omitempty" json:"interval,omitempty"`
	StartDate        *ISOTime            `url:"start_date,omitempty" json:"start_date,omitempty"`

	// Deprecated, use environment tiers instead
	EnvironmentTier *string `url:"environment_tier,omitempty" json:"environment_tier,omitempty"`
}

// GetProjectDORAMetrics gets the DORA metrics for a project.
//
// GitLab API Docs:
// https://docs.gitlab.com/ee/api/dora/metrics.html#get-project-level-dora-metrics
func (s *DORAMetricsService) GetProjectDORAMetrics(pid interface{}, opt GetDORAMetricsOptions, options ...RequestOptionFunc) ([]DORAMetric, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/dora/metrics", PathEscape(project))

	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var metrics []DORAMetric
	resp, err := s.client.Do(req, &metrics)
	if err != nil {
		return nil, resp, err
	}

	return metrics, resp, err
}

// GetGroupDORAMetrics gets the DORA metrics for a group.
//
// GitLab API Docs:
// https://docs.gitlab.com/ee/api/dora/metrics.html#get-group-level-dora-metrics
func (s *DORAMetricsService) GetGroupDORAMetrics(gid interface{}, opt GetDORAMetricsOptions, options ...RequestOptionFunc) ([]DORAMetric, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/dora/metrics", PathEscape(group))

	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var metrics []DORAMetric
	resp, err := s.client.Do(req, &metrics)
	if err != nil {
		return nil, resp, err
	}

	return metrics, resp, err
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/dora_metrics_test.go000066400000000000000000000063011475761473200251200ustar00rootroot00000000000000package gitlab

import (
	"fmt"
	"net/http"
	"testing"
	"time"

	"github.com/stretchr/testify/require"
)

func TestDORAMetrics_GetProjectDORAMetrics(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/dora/metrics", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		query := r.URL.Query()
		for k, v := range map[string]string{
			"metric":     "deployment_frequency",
			"start_date": "2021-03-01",
			"end_date":   "2021-03-08",
		} {
			if query.Get(k) != v {
				t.Errorf("Query parameter %s: %s, want %s", k, query.Get(k), v)
			}
		}

		fmt.Fprint(w, `
			[
				{ "date": "2021-03-01", "value": 3 },
				{ "date": "2021-03-02", "value": 6 },
				{ "date": "2021-03-03", "value": 0 },
				{ "date": "2021-03-04", "value": 0 },
				{ "date": "2021-03-05", "value": 0 },
				{ "date": "2021-03-06", "value": 0 },
				{ "date": "2021-03-07", "value": 0 },
				{ "date": "2021-03-08", "value": 4 }
			]
		`)
	})

	want := []DORAMetric{
		{Date: "2021-03-01", Value: 3},
		{Date: "2021-03-02", Value: 6},
		{Date: "2021-03-03", Value: 0},
		{Date: "2021-03-04", Value: 0},
		{Date: "2021-03-05", Value: 0},
		{Date: "2021-03-06", Value: 0},
		{Date: "2021-03-07", Value: 0},
		{Date: "2021-03-08", Value: 4},
	}

	startDate := ISOTime(time.Date(2021, time.March, 1, 0, 0, 0, 0, time.UTC))
	endDate := ISOTime(time.Date(2021, time.March, 8, 0, 0, 0, 0, time.UTC))

	d, resp, err := client.DORAMetrics.GetProjectDORAMetrics(1, GetDORAMetricsOptions{
		Metric:    Ptr(DORAMetricDeploymentFrequency),
		StartDate: &startDate,
		EndDate:   &endDate,
	})
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, d)
}

func TestDORAMetrics_GetGroupDORAMetrics(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1/dora/metrics", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		query := r.URL.Query()
		for k, v := range map[string]string{
			"metric":     "deployment_frequency",
			"start_date": "2021-03-01",
			"end_date":   "2021-03-08",
		} {
			if query.Get(k) != v {
				t.Errorf("Query parameter %s: %s, want %s", k, query.Get(k), v)
			}
		}

		fmt.Fprint(w, `
			[
				{ "date": "2021-03-01", "value": 3 },
				{ "date": "2021-03-02", "value": 6 },
				{ "date": "2021-03-03", "value": 0 },
				{ "date": "2021-03-04", "value": 0 },
				{ "date": "2021-03-05", "value": 0 },
				{ "date": "2021-03-06", "value": 0 },
				{ "date": "2021-03-07", "value": 0 },
				{ "date": "2021-03-08", "value": 4 }
			]
		`)
	})

	want := []DORAMetric{
		{Date: "2021-03-01", Value: 3},
		{Date: "2021-03-02", Value: 6},
		{Date: "2021-03-03", Value: 0},
		{Date: "2021-03-04", Value: 0},
		{Date: "2021-03-05", Value: 0},
		{Date: "2021-03-06", Value: 0},
		{Date: "2021-03-07", Value: 0},
		{Date: "2021-03-08", Value: 4},
	}

	startDate := ISOTime(time.Date(2021, time.March, 1, 0, 0, 0, 0, time.UTC))
	endDate := ISOTime(time.Date(2021, time.March, 8, 0, 0, 0, 0, time.UTC))

	d, resp, err := client.DORAMetrics.GetGroupDORAMetrics(1, GetDORAMetricsOptions{
		Metric:    Ptr(DORAMetricDeploymentFrequency),
		StartDate: &startDate,
		EndDate:   &endDate,
	})
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, d)
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/draft_notes.go000066400000000000000000000170451475761473200237250ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"fmt"
	"net/http"
)

type DraftNote struct {
	ID                int           `json:"id"`
	AuthorID          int           `json:"author_id"`
	MergeRequestID    int           `json:"merge_request_id"`
	ResolveDiscussion bool          `json:"resolve_discussion"`
	DiscussionID      string        `json:"discussion_id"`
	Note              string        `json:"note"`
	CommitID          string        `json:"commit_id"`
	LineCode          string        `json:"line_code"`
	Position          *NotePosition `json:"position"`
}

// DraftNotesService handles communication with the draft notes related methods
// of the GitLab API.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/draft_notes.html#list-all-merge-request-draft-notes
type DraftNotesService struct {
	client *Client
}

// ListDraftNotesOptions represents the available ListDraftNotes()
// options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/draft_notes.html#list-all-merge-request-draft-notes
type ListDraftNotesOptions struct {
	ListOptions
	OrderBy *string `url:"order_by,omitempty" json:"order_by,omitempty"`
	Sort    *string `url:"sort,omitempty" json:"sort,omitempty"`
}

// ListDraftNotes gets a list of all draft notes for a merge request.
//
// Gitlab API docs:
// https://docs.gitlab.com/ee/api/draft_notes.html#list-all-merge-request-draft-notes
func (s *DraftNotesService) ListDraftNotes(pid interface{}, mergeRequest int, opt *ListDraftNotesOptions, options ...RequestOptionFunc) ([]*DraftNote, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/merge_requests/%d/draft_notes", PathEscape(project), mergeRequest)

	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var n []*DraftNote
	resp, err := s.client.Do(req, &n)
	if err != nil {
		return nil, resp, err
	}

	return n, resp, nil
}

// GetDraftNote gets a single draft note for a merge request.
//
// Gitlab API docs:
// https://docs.gitlab.com/ee/api/draft_notes.html#get-a-single-draft-note
func (s *DraftNotesService) GetDraftNote(pid interface{}, mergeRequest int, note int, options ...RequestOptionFunc) (*DraftNote, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/merge_requests/%d/draft_notes/%d", PathEscape(project), mergeRequest, note)

	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	n := new(DraftNote)
	resp, err := s.client.Do(req, &n)
	if err != nil {
		return nil, resp, err
	}

	return n, resp, nil
}

// CreateDraftNoteOptions represents the available CreateDraftNote()
// options.
//
// Gitlab API docs:
// https://docs.gitlab.com/ee/api/draft_notes.html#create-a-draft-note
type CreateDraftNoteOptions struct {
	Note                  *string          `url:"note" json:"note"`
	CommitID              *string          `url:"commit_id,omitempty" json:"commit_id,omitempty"`
	InReplyToDiscussionID *string          `url:"in_reply_to_discussion_id,omitempty" json:"in_reply_to_discussion_id,omitempty"`
	ResolveDiscussion     *bool            `url:"resolve_discussion,omitempty" json:"resolve_discussion,omitempty"`
	Position              *PositionOptions `url:"position,omitempty" json:"position,omitempty"`
}

// CreateDraftNote creates a draft note for a merge request.
//
// Gitlab API docs:
// https://docs.gitlab.com/ee/api/draft_notes.html#create-a-draft-note
func (s *DraftNotesService) CreateDraftNote(pid interface{}, mergeRequest int, opt *CreateDraftNoteOptions, options ...RequestOptionFunc) (*DraftNote, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/merge_requests/%d/draft_notes", PathEscape(project), mergeRequest)

	req, err := s.client.NewRequest(http.MethodPost, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	n := new(DraftNote)
	resp, err := s.client.Do(req, &n)
	if err != nil {
		return nil, resp, err
	}

	return n, resp, nil
}

// UpdateDraftNoteOptions represents the available UpdateDraftNote()
// options.
//
// Gitlab API docs:
// https://docs.gitlab.com/ee/api/draft_notes.html#create-a-draft-note
type UpdateDraftNoteOptions struct {
	Note     *string          `url:"note,omitempty" json:"note,omitempty"`
	Position *PositionOptions `url:"position,omitempty" json:"position,omitempty"`
}

// UpdateDraftNote updates a draft note for a merge request.
//
// Gitlab API docs: https://docs.gitlab.com/ee/api/draft_notes.html#create-a-draft-note
func (s *DraftNotesService) UpdateDraftNote(pid interface{}, mergeRequest int, note int, opt *UpdateDraftNoteOptions, options ...RequestOptionFunc) (*DraftNote, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/merge_requests/%d/draft_notes/%d", PathEscape(project), mergeRequest, note)

	req, err := s.client.NewRequest(http.MethodPut, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	n := new(DraftNote)
	resp, err := s.client.Do(req, &n)
	if err != nil {
		return nil, resp, err
	}

	return n, resp, nil
}

// DeleteDraftNote deletes a single draft note for a merge request.
//
// Gitlab API docs:
// https://docs.gitlab.com/ee/api/draft_notes.html#delete-a-draft-note
func (s *DraftNotesService) DeleteDraftNote(pid interface{}, mergeRequest int, note int, options ...RequestOptionFunc) (*Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("projects/%s/merge_requests/%d/draft_notes/%d", PathEscape(project), mergeRequest, note)

	req, err := s.client.NewRequest(http.MethodDelete, u, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}

// PublishDraftNote publishes a single draft note for a merge request.
//
// Gitlab API docs:
// https://docs.gitlab.com/ee/api/draft_notes.html#publish-a-draft-note
func (s *DraftNotesService) PublishDraftNote(pid interface{}, mergeRequest int, note int, options ...RequestOptionFunc) (*Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("projects/%s/merge_requests/%d/draft_notes/%d/publish", PathEscape(project), mergeRequest, note)

	req, err := s.client.NewRequest(http.MethodPut, u, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}

// PublishAllDraftNotes publishes all draft notes for a merge request that belong to the user.
//
// Gitlab API docs:
// https://docs.gitlab.com/ee/api/draft_notes.html#publish-a-draft-note
func (s *DraftNotesService) PublishAllDraftNotes(pid interface{}, mergeRequest int, options ...RequestOptionFunc) (*Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("projects/%s/merge_requests/%d/draft_notes/bulk_publish", PathEscape(project), mergeRequest)

	req, err := s.client.NewRequest(http.MethodPost, u, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/draft_notes_test.go000066400000000000000000000137531475761473200247660ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"net/http"
	"reflect"
	"testing"
)

func TestGetDraftNote(t *testing.T) {
	mux, client := setup(t)
	mux.HandleFunc("/api/v4/projects/1/merge_requests/4329/draft_notes/3", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		mustWriteHTTPResponse(t, w, "testdata/get_draft_note.json")
	})

	note, _, err := client.DraftNotes.GetDraftNote("1", 4329, 3)
	if err != nil {
		t.Fatal(err)
	}

	want := &DraftNote{
		ID:                37349978,
		AuthorID:          10271899,
		MergeRequestID:    291473309,
		ResolveDiscussion: false,
		DiscussionID:      "",
		Note:              "Some draft note",
		CommitID:          "",
		LineCode:          "",
		Position:          nil,
	}

	if !reflect.DeepEqual(note, want) {
		t.Errorf("DraftNotes.GetDraftNote want %#v, got %#v", note, want)
	}
}

func TestListDraftNotes(t *testing.T) {
	mux, client := setup(t)
	mux.HandleFunc("/api/v4/projects/1/merge_requests/4329/draft_notes", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		mustWriteHTTPResponse(t, w, "testdata/list_draft_notes.json")
	})

	notes, _, err := client.DraftNotes.ListDraftNotes("1", 4329, nil)
	if err != nil {
		t.Fatal(err)
	}

	want := []*DraftNote{
		{
			ID:                37349978,
			AuthorID:          10271899,
			MergeRequestID:    291473309,
			ResolveDiscussion: false,
			DiscussionID:      "",
			Note:              "Some draft note",
			CommitID:          "",
			LineCode:          "",
			Position:          nil,
		},
		{
			ID:                37349979,
			AuthorID:          10271899,
			MergeRequestID:    291473309,
			ResolveDiscussion: false,
			DiscussionID:      "",
			Note:              "Some draft note 2",
			CommitID:          "",
			LineCode:          "3dacf79e0d779e2baa1c700cf56510e42f55cf85_10_9",
			Position: &NotePosition{
				BaseSHA:      "64581c4ee41beb44d943d7801f82d9038e25e453",
				StartSHA:     "87bffbff93bf334889780f54ae1922355661f867",
				HeadSHA:      "2c972dbf9094c380f5f00dcd8112d2c69b24c859",
				OldPath:      "src/some-dir/some-file.js",
				NewPath:      "src/some-dir/some-file.js",
				PositionType: "text",
				NewLine:      9,
				LineRange: &LineRange{
					StartRange: &LinePosition{
						LineCode: "3dacf79e0d779e2baa1c700cf56510e42f55cf85_10_9",
						Type:     "new",
						NewLine:  9,
					},
					EndRange: &LinePosition{
						LineCode: "3dacf79e0d779e2baa1c700cf56510e42f55cf85_10_9",
						Type:     "new",
						NewLine:  9,
					},
				},
			},
		},
	}

	if !reflect.DeepEqual(notes, want) {
		t.Errorf("DraftNotes.GetDraftNote want %#v, got %#v", notes, want)
	}
}

func TestCreateDraftNote(t *testing.T) {
	mux, client := setup(t)
	mux.HandleFunc("/api/v4/projects/1/merge_requests/4329/draft_notes", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		mustWriteHTTPResponse(t, w, "testdata/create_draft_note.json")
	})

	note, _, err := client.DraftNotes.CreateDraftNote("1", 4329, &CreateDraftNoteOptions{
		Note: Ptr("Some new draft note"),
	})
	if err != nil {
		t.Fatal(err)
	}

	want := &DraftNote{
		ID:                37349980,
		AuthorID:          10271899,
		MergeRequestID:    291473309,
		ResolveDiscussion: false,
		DiscussionID:      "",
		Note:              "Some new draft note",
		CommitID:          "",
		LineCode:          "",
		Position:          nil,
	}

	if !reflect.DeepEqual(note, want) {
		t.Errorf("DraftNotes.GetDraftNote want %#v, got %#v", note, want)
	}
}

func TestUpdateDraftNote(t *testing.T) {
	mux, client := setup(t)
	mux.HandleFunc("/api/v4/projects/1/merge_requests/4329/draft_notes/3", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPut)
		mustWriteHTTPResponse(t, w, "testdata/update_draft_note.json")
	})

	note, _, err := client.DraftNotes.UpdateDraftNote("1", 4329, 3, &UpdateDraftNoteOptions{
		Note: Ptr("Some changed draft note"),
	})
	if err != nil {
		t.Fatal(err)
	}

	want := &DraftNote{
		ID:                37349980,
		AuthorID:          10271899,
		MergeRequestID:    291473309,
		ResolveDiscussion: false,
		DiscussionID:      "",
		Note:              "Some changed draft note",
		CommitID:          "",
		LineCode:          "",
		Position:          nil,
	}

	if !reflect.DeepEqual(note, want) {
		t.Errorf("DraftNotes.UpdateDraftNote want %#v, got %#v", note, want)
	}
}

func TestDeleteDraftNote(t *testing.T) {
	mux, client := setup(t)
	mux.HandleFunc("/api/v4/projects/1/merge_requests/4329/draft_notes/3", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodDelete)
	})

	_, err := client.DraftNotes.DeleteDraftNote("1", 4329, 3)
	if err != nil {
		t.Errorf("DraftNotes.DeleteDraftNote returned error: %v", err)
	}
}

func TestPublishDraftNote(t *testing.T) {
	mux, client := setup(t)
	mux.HandleFunc("/api/v4/projects/1/merge_requests/4329/draft_notes/3/publish", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPut)
	})

	_, err := client.DraftNotes.PublishDraftNote("1", 4329, 3)
	if err != nil {
		t.Errorf("DraftNotes.PublishDraftNote returned error: %v", err)
	}
}

func TestPublishAllDraftNotes(t *testing.T) {
	mux, client := setup(t)
	mux.HandleFunc("/api/v4/projects/1/merge_requests/4329/draft_notes/bulk_publish", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
	})

	_, err := client.DraftNotes.PublishAllDraftNotes("1", 4329)
	if err != nil {
		t.Errorf("DraftNotes.PublishAllDraftNotes returned error: %v", err)
	}
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/environments.go000066400000000000000000000214471475761473200241450ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"fmt"
	"net/http"
	"time"
)

// EnvironmentsService handles communication with the environment related methods
// of the GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/environments.html
type EnvironmentsService struct {
	client *Client
}

// Environment represents a GitLab environment.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/environments.html
type Environment struct {
	ID                  int         `json:"id"`
	Name                string      `json:"name"`
	Slug                string      `json:"slug"`
	Description         string      `json:"description"`
	State               string      `json:"state"`
	Tier                string      `json:"tier"`
	ExternalURL         string      `json:"external_url"`
	Project             *Project    `json:"project"`
	CreatedAt           *time.Time  `json:"created_at"`
	UpdatedAt           *time.Time  `json:"updated_at"`
	LastDeployment      *Deployment `json:"last_deployment"`
	ClusterAgent        *Agent      `json:"cluster_agent"`
	KubernetesNamespace string      `json:"kubernetes_namespace"`
	FluxResourcePath    string      `json:"flux_resource_path"`
	AutoStopAt          *time.Time  `json:"auto_stop_at"`
	AutoStopSetting     string      `json:"auto_stop_setting"`
}

func (env Environment) String() string {
	return Stringify(env)
}

// ListEnvironmentsOptions represents the available ListEnvironments() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/environments.html#list-environments
type ListEnvironmentsOptions struct {
	ListOptions
	Name   *string `url:"name,omitempty" json:"name,omitempty"`
	Search *string `url:"search,omitempty" json:"search,omitempty"`
	States *string `url:"states,omitempty" json:"states,omitempty"`
}

// ListEnvironments gets a list of environments from a project, sorted by name
// alphabetically.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/environments.html#list-environments
func (s *EnvironmentsService) ListEnvironments(pid interface{}, opts *ListEnvironmentsOptions, options ...RequestOptionFunc) ([]*Environment, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/environments", PathEscape(project))

	req, err := s.client.NewRequest(http.MethodGet, u, opts, options)
	if err != nil {
		return nil, nil, err
	}

	var envs []*Environment
	resp, err := s.client.Do(req, &envs)
	if err != nil {
		return nil, resp, err
	}

	return envs, resp, nil
}

// GetEnvironment gets a specific environment from a project.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/environments.html#get-a-specific-environment
func (s *EnvironmentsService) GetEnvironment(pid interface{}, environment int, options ...RequestOptionFunc) (*Environment, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/environments/%d", PathEscape(project), environment)

	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	env := new(Environment)
	resp, err := s.client.Do(req, env)
	if err != nil {
		return nil, resp, err
	}

	return env, resp, nil
}

// CreateEnvironmentOptions represents the available CreateEnvironment() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/environments.html#create-a-new-environment
type CreateEnvironmentOptions struct {
	Name                *string `url:"name,omitempty" json:"name,omitempty"`
	Description         *string `url:"description,omitempty" json:"description,omitempty"`
	ExternalURL         *string `url:"external_url,omitempty" json:"external_url,omitempty"`
	Tier                *string `url:"tier,omitempty" json:"tier,omitempty"`
	ClusterAgentID      *int    `url:"cluster_agent_id,omitempty" json:"cluster_agent_id,omitempty"`
	KubernetesNamespace *string `url:"kubernetes_namespace,omitempty" json:"kubernetes_namespace,omitempty"`
	FluxResourcePath    *string `url:"flux_resource_path,omitempty" json:"flux_resource_path,omitempty"`
	AutoStopSetting     *string `url:"auto_stop_setting,omitempty" json:"auto_stop_setting,omitempty"`
}

// CreateEnvironment adds an environment to a project. This is an idempotent
// method and can be called multiple times with the same parameters. Createing
// an environment that is already a environment does not affect the
// existing environmentship.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/environments.html#create-a-new-environment
func (s *EnvironmentsService) CreateEnvironment(pid interface{}, opt *CreateEnvironmentOptions, options ...RequestOptionFunc) (*Environment, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/environments", PathEscape(project))

	req, err := s.client.NewRequest(http.MethodPost, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	env := new(Environment)
	resp, err := s.client.Do(req, env)
	if err != nil {
		return nil, resp, err
	}

	return env, resp, nil
}

// EditEnvironmentOptions represents the available EditEnvironment() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/environments.html#update-an-existing-environment
type EditEnvironmentOptions struct {
	Name                *string `url:"name,omitempty" json:"name,omitempty"`
	Description         *string `url:"description,omitempty" json:"description,omitempty"`
	ExternalURL         *string `url:"external_url,omitempty" json:"external_url,omitempty"`
	Tier                *string `url:"tier,omitempty" json:"tier,omitempty"`
	ClusterAgentID      *int    `url:"cluster_agent_id,omitempty" json:"cluster_agent_id,omitempty"`
	KubernetesNamespace *string `url:"kubernetes_namespace,omitempty" json:"kubernetes_namespace,omitempty"`
	FluxResourcePath    *string `url:"flux_resource_path,omitempty" json:"flux_resource_path,omitempty"`
	AutoStopSetting     *string `url:"auto_stop_setting,omitempty" json:"auto_stop_setting,omitempty"`
}

// EditEnvironment updates a project team environment to a specified access level..
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/environments.html#update-an-existing-environment
func (s *EnvironmentsService) EditEnvironment(pid interface{}, environment int, opt *EditEnvironmentOptions, options ...RequestOptionFunc) (*Environment, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/environments/%d", PathEscape(project), environment)

	req, err := s.client.NewRequest(http.MethodPut, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	env := new(Environment)
	resp, err := s.client.Do(req, env)
	if err != nil {
		return nil, resp, err
	}

	return env, resp, nil
}

// DeleteEnvironment removes an environment from a project team.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/environments.html#delete-an-environment
func (s *EnvironmentsService) DeleteEnvironment(pid interface{}, environment int, options ...RequestOptionFunc) (*Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("projects/%s/environments/%d", PathEscape(project), environment)

	req, err := s.client.NewRequest(http.MethodDelete, u, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}

// StopEnvironmentOptions represents the available StopEnvironment() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/environments.html#stop-an-environment
type StopEnvironmentOptions struct {
	Force *bool `url:"force,omitempty" json:"force,omitempty"`
}

// StopEnvironment stops an environment within a specific project.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/environments.html#stop-an-environment
func (s *EnvironmentsService) StopEnvironment(pid interface{}, environmentID int, opt *StopEnvironmentOptions, options ...RequestOptionFunc) (*Environment, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/environments/%d/stop", PathEscape(project), environmentID)

	req, err := s.client.NewRequest(http.MethodPost, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	env := new(Environment)
	resp, err := s.client.Do(req, env)
	if err != nil {
		return nil, resp, err
	}

	return env, resp, nil
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/environments_test.go000066400000000000000000000321001475761473200251700ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"encoding/json"
	"fmt"
	"net/http"
	"reflect"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
)

func TestListEnvironments(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/environments", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		testURL(t, r, "/api/v4/projects/1/environments?name=review%2Ffix-foo&page=1&per_page=10")
		fmt.Fprint(w, `[
			{
				"id": 1,
				"name": "review/fix-foo",
				"description": "test",
				"slug": "review-fix-foo-dfjre3",
				"external_url": "https://review-fix-foo-dfjre3.example.gitlab.com",
				"state": "stopped",
				"created_at": "2013-10-02T10:12:29Z",
				"updated_at": "2013-12-02T10:12:29Z",
				"cluster_agent": {
					"id": 1,
					"name": "agent-1",
					"config_project": {
						"id": 20,
						"description": "",
						"name": "test",
						"name_with_namespace": "Administrator / test",
						"path": "test",
						"path_with_namespace": "root/test",
						"created_at": "2013-10-02T10:12:29Z"
					},
					"created_at": "2013-10-02T10:12:29Z",
					"created_by_user_id": 42
				},
				"kubernetes_namespace": "flux-system",
				"flux_resource_path": "HelmRelease/flux-system"
			}
		]`)
	})

	envs, _, err := client.Environments.ListEnvironments(1, &ListEnvironmentsOptions{Name: Ptr("review/fix-foo"), ListOptions: ListOptions{Page: 1, PerPage: 10}})
	if err != nil {
		t.Fatal(err)
	}

	createdAtWant, _ := time.Parse(timeLayout, "2013-10-02T10:12:29Z")
	updatedAtWant, _ := time.Parse(timeLayout, "2013-12-02T10:12:29Z")
	want := []*Environment{{
		ID:          1,
		Name:        "review/fix-foo",
		Slug:        "review-fix-foo-dfjre3",
		Description: "test",
		ExternalURL: "https://review-fix-foo-dfjre3.example.gitlab.com",
		State:       "stopped",
		CreatedAt:   &createdAtWant,
		UpdatedAt:   &updatedAtWant,
		ClusterAgent: &Agent{
			ID:   1,
			Name: "agent-1",
			ConfigProject: ConfigProject{
				ID:                20,
				Name:              "test",
				NameWithNamespace: "Administrator / test",
				Path:              "test",
				PathWithNamespace: "root/test",
				CreatedAt:         &createdAtWant,
			},
			CreatedAt:       &createdAtWant,
			CreatedByUserID: 42,
		},
		KubernetesNamespace: "flux-system",
		FluxResourcePath:    "HelmRelease/flux-system",
	}}
	if !reflect.DeepEqual(want, envs) {
		t.Errorf("Environments.ListEnvironments returned %+v, want %+v", envs, want)
	}
}

func TestGetEnvironment(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/environments/5949167", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprint(w, `{
			"id": 1,
			"name": "review/fix-foo",
			"description": "test",
			"slug": "review-fix-foo-dfjre3",
			"external_url": "https://review-fix-foo-dfjre3.example.gitlab.com",
			"state": "stopped",
			"created_at": "2013-10-02T10:12:29Z",
			"updated_at": "2013-12-02T10:12:29Z",
			"auto_stop_at": "2025-01-25T15:08:29Z",
			"cluster_agent": {
				"id": 1,
				"name": "agent-1",
				"config_project": {
					"id": 20,
					"description": "",
					"name": "test",
					"name_with_namespace": "Administrator / test",
					"path": "test",
					"path_with_namespace": "root/test",
					"created_at": "2013-10-02T10:12:29Z"
				},
				"created_at": "2013-10-02T10:12:29Z",
				"created_by_user_id": 42
			},
			"kubernetes_namespace": "flux-system",
			"flux_resource_path": "HelmRelease/flux-system",
			"auto_stop_setting": "always"
		}`)
	})

	env, _, err := client.Environments.GetEnvironment(1, 5949167)
	if err != nil {
		t.Errorf("Environemtns.GetEnvironment returned error: %v", err)
	}

	createdAtWant, _ := time.Parse(timeLayout, "2013-10-02T10:12:29Z")
	updatedAtWant, _ := time.Parse(timeLayout, "2013-12-02T10:12:29Z")
	autoStopAtWant, _ := time.Parse(timeLayout, "2025-01-25T15:08:29Z")
	want := &Environment{
		ID:          1,
		Name:        "review/fix-foo",
		Slug:        "review-fix-foo-dfjre3",
		Description: "test",
		ExternalURL: "https://review-fix-foo-dfjre3.example.gitlab.com",
		State:       "stopped",
		CreatedAt:   &createdAtWant,
		UpdatedAt:   &updatedAtWant,
		ClusterAgent: &Agent{
			ID:   1,
			Name: "agent-1",
			ConfigProject: ConfigProject{
				ID:                20,
				Name:              "test",
				NameWithNamespace: "Administrator / test",
				Path:              "test",
				PathWithNamespace: "root/test",
				CreatedAt:         &createdAtWant,
			},
			CreatedAt:       &createdAtWant,
			CreatedByUserID: 42,
		},
		KubernetesNamespace: "flux-system",
		FluxResourcePath:    "HelmRelease/flux-system",
		AutoStopAt:          &autoStopAtWant,
		AutoStopSetting:     "always",
	}
	if !reflect.DeepEqual(want, env) {
		t.Errorf("Environments.GetEnvironment returned %+v, want %+v", env, want)
	}
}

func TestCreateEnvironment(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/environments", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		testURL(t, r, "/api/v4/projects/1/environments")
		fmt.Fprint(w, `{
      "id": 1,
      "name": "deploy",
	  "description": "test",
      "slug": "deploy",
      "external_url": "https://deploy.example.gitlab.com",
      "tier": "production",
      "cluster_agent": {
        "id": 1,
        "name": "agent-1",
        "config_project": {
          "id": 20,
          "description": "",
          "name": "test",
          "name_with_namespace": "Administrator / test",
          "path": "test",
          "path_with_namespace": "root/test",
          "created_at": "2013-10-02T10:12:29Z"
        },
        "created_at": "2013-10-02T10:12:29Z",
        "created_by_user_id": 42
      },
      "kubernetes_namespace": "flux-system",
      "flux_resource_path": "HelmRelease/flux-system",
      "auto_stop_setting": "always"
    }`)
	})

	envs, _, err := client.Environments.CreateEnvironment(1, &CreateEnvironmentOptions{
		Name:                Ptr("deploy"),
		Description:         Ptr("test"),
		ExternalURL:         Ptr("https://deploy.example.gitlab.com"),
		Tier:                Ptr("production"),
		ClusterAgentID:      Ptr(1),
		KubernetesNamespace: Ptr("flux-system"),
		FluxResourcePath:    Ptr("HelmRelease/flux-system"),
		AutoStopSetting:     Ptr("always"),
	})
	if err != nil {
		t.Fatal(err)
	}

	createdAtWant, _ := time.Parse(timeLayout, "2013-10-02T10:12:29Z")
	want := &Environment{
		ID:          1,
		Name:        "deploy",
		Slug:        "deploy",
		Description: "test",
		ExternalURL: "https://deploy.example.gitlab.com",
		Tier:        "production",
		ClusterAgent: &Agent{
			ID:   1,
			Name: "agent-1",
			ConfigProject: ConfigProject{
				ID:                20,
				Name:              "test",
				NameWithNamespace: "Administrator / test",
				Path:              "test",
				PathWithNamespace: "root/test",
				CreatedAt:         &createdAtWant,
			},
			CreatedAt:       &createdAtWant,
			CreatedByUserID: 42,
		},
		KubernetesNamespace: "flux-system",
		FluxResourcePath:    "HelmRelease/flux-system",
		AutoStopSetting:     "always",
	}
	if !reflect.DeepEqual(want, envs) {
		t.Errorf("Environments.CreateEnvironment returned %+v, want %+v", envs, want)
	}
}

func TestEditEnvironment(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/environments/1", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPut)
		testURL(t, r, "/api/v4/projects/1/environments/1")
		fmt.Fprint(w, `{
      "id": 1,
      "name": "staging",
	  "description": "test",
      "slug": "staging",
      "external_url": "https://staging.example.gitlab.com",
      "tier": "staging",
      "cluster_agent": {
        "id": 1,
        "name": "agent-1",
        "config_project": {
          "id": 20,
          "description": "",
          "name": "test",
          "name_with_namespace": "Administrator / test",
          "path": "test",
          "path_with_namespace": "root/test",
          "created_at": "2013-10-02T10:12:29Z"
        },
        "created_at": "2013-10-02T10:12:29Z",
        "created_by_user_id": 42
    },
	  "kubernetes_namespace": "flux-system",
	  "flux_resource_path": "HelmRelease/flux-system",
	  "auto_stop_setting": "with_action"
    }`)
	})

	envs, _, err := client.Environments.EditEnvironment(1, 1, &EditEnvironmentOptions{
		Name:                Ptr("staging"),
		Description:         Ptr("test"),
		ExternalURL:         Ptr("https://staging.example.gitlab.com"),
		Tier:                Ptr("staging"),
		ClusterAgentID:      Ptr(1),
		KubernetesNamespace: Ptr("flux-system"),
		FluxResourcePath:    Ptr("HelmRelease/flux-system"),
		AutoStopSetting:     Ptr("with_action"),
	})
	if err != nil {
		t.Fatal(err)
	}

	createdAtWant, _ := time.Parse(timeLayout, "2013-10-02T10:12:29Z")
	want := &Environment{
		ID:          1,
		Name:        "staging",
		Slug:        "staging",
		Description: "test",
		ExternalURL: "https://staging.example.gitlab.com",
		Tier:        "staging",
		ClusterAgent: &Agent{
			ID:   1,
			Name: "agent-1",
			ConfigProject: ConfigProject{
				ID:                20,
				Name:              "test",
				NameWithNamespace: "Administrator / test",
				Path:              "test",
				PathWithNamespace: "root/test",
				CreatedAt:         &createdAtWant,
			},
			CreatedAt:       &createdAtWant,
			CreatedByUserID: 42,
		},
		KubernetesNamespace: "flux-system",
		FluxResourcePath:    "HelmRelease/flux-system",
		AutoStopSetting:     "with_action",
	}
	if !reflect.DeepEqual(want, envs) {
		t.Errorf("Environments.EditEnvironment returned %+v, want %+v", envs, want)
	}
}

func TestDeleteEnvironment(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/environments/1", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodDelete)
		testURL(t, r, "/api/v4/projects/1/environments/1")
	})
	_, err := client.Environments.DeleteEnvironment(1, 1)
	if err != nil {
		t.Fatal(err)
	}
}

func TestStopEnvironment(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/environments/1/stop", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		testURL(t, r, "/api/v4/projects/1/environments/1/stop")
		fmt.Fprint(w, `{
      "id": 1,
      "name": "staging",
      "state": "stopping",
      "slug": "staging",
      "external_url": "https://staging.example.gitlab.com",
      "tier": "staging"
    }`)
	})
	_, _, err := client.Environments.StopEnvironment(1, 1, &StopEnvironmentOptions{})
	if err != nil {
		t.Fatal(err)
	}
}

func TestUnmarshal(t *testing.T) {
	jsonObject := `
    {
        "id": 10,
        "name": "production",
		"description": "test",
        "slug": "production",
        "external_url": "https://example.com",
        "project": {
            "id": 1,
            "description": "",
            "name": "Awesome Project",
            "name_with_namespace": "FooBar Group / Awesome Project",
            "path": "awesome-project",
            "path_with_namespace": "foobar-group/awesome-project",
            "created_at": "2017-09-30T11:10:08.476-04:00",
            "default_branch": "develop",
            "tag_list": [],
            "ssh_url_to_repo": "git@example.gitlab.com:foobar-group/api.git",
            "http_url_to_repo": "https://example.gitlab.com/foobar-group/api.git",
            "web_url": "https://example.gitlab.com/foobar-group/api",
            "readme_url": null,
            "avatar_url": null,
            "star_count": 0,
            "forks_count": 1,
            "last_activity_at": "2019-11-03T22:22:46.564-05:00",
            "namespace": {
                "id": 15,
                "name": "FooBar Group",
                "path": "foobar-group",
                "kind": "group",
                "full_path": "foobar-group",
                "parent_id": null,
                "avatar_url": null,
                "web_url": "https://example.gitlab.com/groups/foobar-group"
            }
        },
        "state": "available",
        "auto_stop_setting": "always",
        "kubernetes_namespace": "flux-system",
        "flux_resource_path": "HelmRelease/flux-system"	
    }`

	var env Environment
	err := json.Unmarshal([]byte(jsonObject), &env)

	if assert.NoError(t, err) {
		assert.Equal(t, 10, env.ID)
		assert.Equal(t, "production", env.Name)
		assert.Equal(t, "test", env.Description)
		assert.Equal(t, "https://example.com", env.ExternalURL)
		assert.Equal(t, "available", env.State)
		if assert.NotNil(t, env.Project) {
			assert.Equal(t, "Awesome Project", env.Project.Name)
		}
		assert.Equal(t, "always", env.AutoStopSetting)
		assert.Equal(t, "flux-system", env.KubernetesNamespace)
		assert.Equal(t, "HelmRelease/flux-system", env.FluxResourcePath)
	}
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/epic_issues.go000066400000000000000000000106061475761473200237240ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"fmt"
	"net/http"
)

// EpicIssuesService handles communication with the epic issue related methods
// of the GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epic_issues.html
type EpicIssuesService struct {
	client *Client
}

// EpicIssueAssignment contains both the epic and issue objects returned from
// Gitlab with the assignment ID.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epic_issues.html
type EpicIssueAssignment struct {
	ID    int    `json:"id"`
	Epic  *Epic  `json:"epic"`
	Issue *Issue `json:"issue"`
}

// ListEpicIssues get a list of epic issues.
//
// Gitlab API docs:
// https://docs.gitlab.com/ee/api/epic_issues.html#list-issues-for-an-epic
func (s *EpicIssuesService) ListEpicIssues(gid interface{}, epic int, opt *ListOptions, options ...RequestOptionFunc) ([]*Issue, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/epics/%d/issues", PathEscape(group), epic)

	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var is []*Issue
	resp, err := s.client.Do(req, &is)
	if err != nil {
		return nil, resp, err
	}

	return is, resp, nil
}

// AssignEpicIssue assigns an existing issue to an epic.
//
// Gitlab API Docs:
// https://docs.gitlab.com/ee/api/epic_issues.html#assign-an-issue-to-the-epic
func (s *EpicIssuesService) AssignEpicIssue(gid interface{}, epic, issue int, options ...RequestOptionFunc) (*EpicIssueAssignment, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/epics/%d/issues/%d", PathEscape(group), epic, issue)

	req, err := s.client.NewRequest(http.MethodPost, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	a := new(EpicIssueAssignment)
	resp, err := s.client.Do(req, a)
	if err != nil {
		return nil, resp, err
	}

	return a, resp, nil
}

// RemoveEpicIssue removes an issue from an epic.
//
// Gitlab API Docs:
// https://docs.gitlab.com/ee/api/epic_issues.html#remove-an-issue-from-the-epic
func (s *EpicIssuesService) RemoveEpicIssue(gid interface{}, epic, epicIssue int, options ...RequestOptionFunc) (*EpicIssueAssignment, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/epics/%d/issues/%d", PathEscape(group), epic, epicIssue)

	req, err := s.client.NewRequest(http.MethodDelete, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	a := new(EpicIssueAssignment)
	resp, err := s.client.Do(req, a)
	if err != nil {
		return nil, resp, err
	}

	return a, resp, nil
}

// UpdateEpicIsssueAssignmentOptions describes the UpdateEpicIssueAssignment()
// options.
//
// Gitlab API Docs:
// https://docs.gitlab.com/ee/api/epic_issues.html#update-epic---issue-association
type UpdateEpicIsssueAssignmentOptions struct {
	*ListOptions
	MoveBeforeID *int `url:"move_before_id,omitempty" json:"move_before_id,omitempty"`
	MoveAfterID  *int `url:"move_after_id,omitempty" json:"move_after_id,omitempty"`
}

// UpdateEpicIssueAssignment moves an issue before or after another issue in an
// epic issue list.
//
// Gitlab API Docs:
// https://docs.gitlab.com/ee/api/epic_issues.html#update-epic---issue-association
func (s *EpicIssuesService) UpdateEpicIssueAssignment(gid interface{}, epic, epicIssue int, opt *UpdateEpicIsssueAssignmentOptions, options ...RequestOptionFunc) ([]*Issue, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/epics/%d/issues/%d", PathEscape(group), epic, epicIssue)

	req, err := s.client.NewRequest(http.MethodPut, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var is []*Issue
	resp, err := s.client.Do(req, &is)
	if err != nil {
		return nil, resp, err
	}

	return is, resp, nil
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/epic_issues_test.go000066400000000000000000000540361475761473200247700ustar00rootroot00000000000000package gitlab

import (
	"fmt"
	"net/http"
	"testing"

	"github.com/stretchr/testify/require"
)

func TestEpicIssuesService_ListEpicIssues(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1/epics/5/issues", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
			[
			  {
				"id": 76,
				"iid": 6,
				"project_id": 8,
				"title" : "Consequatur vero maxime deserunt laboriosam est voluptas dolorem.",
				"description" : "Ratione dolores corrupti mollitia soluta quia.",
				"state": "opened",
				"closed_at": null,
				"labels": [],
				"milestone": {
				  "id": 38,
				  "iid": 3,
				  "project_id": 8,
				  "title": "v2.0",
				  "description": "In tempore culpa inventore quo accusantium.",
				  "state": "closed"
				},
				"assignees": [{
				  "id": 7,
				  "name": "Pamella Huel",
				  "username": "arnita",
				  "state": "active",
				  "avatar_url": "http://www.gravatar.com/avatar/a2f5c6fcef64c9c69cb8779cb292be1b?s=80&d=identicon",
				  "web_url": "http://localhost:3001/arnita"
				}],
				"assignee": {
				  "id": 7,
				  "name": "Pamella Huel",
				  "username": "arnita",
				  "state": "active",
				  "avatar_url": "http://www.gravatar.com/avatar/a2f5c6fcef64c9c69cb8779cb292be1b?s=80&d=identicon",
				  "web_url": "http://localhost:3001/arnita"
				},
				"author": {
				  "id": 13,
				  "name": "Venkatesh Thalluri",
				  "username": "venky333",
				  "state": "active",
				  "avatar_url": "http://www.gravatar.com/avatar/30e3b2122ccd6b8e45e8e14a3ffb58fc?s=80&d=identicon",
				  "web_url": "http://localhost:3001/venky333"
				},
				"user_notes_count": 8,
				"upvotes": 0,
				"downvotes": 0,
				"due_date": null,
				"confidential": false,
				"weight": null,
				"discussion_locked": null,
				"web_url": "http://localhost:3001/h5bp/html5-boilerplate/issues/6",
				"time_stats": {
				  "time_estimate": 0,
				  "total_time_spent": 0,
				  "human_time_estimate": null,
				  "human_total_time_spent": null
				},
				"_links":{
				  "self": "http://localhost:3001/api/v4/projects/8/issues/6",
				  "notes": "http://localhost:3001/api/v4/projects/8/issues/6/notes",
				  "award_emoji": "http://localhost:3001/api/v4/projects/8/issues/6/award_emoji",
				  "project": "http://localhost:3001/api/v4/projects/8"
				},
				"epic_issue_id": 2
			  }
			]
		`)
	})

	want := []*Issue{{
		ID:          76,
		IID:         6,
		ExternalID:  "",
		State:       "opened",
		Description: "Ratione dolores corrupti mollitia soluta quia.",
		Author: &IssueAuthor{
			ID:        13,
			State:     "active",
			WebURL:    "http://localhost:3001/venky333",
			Name:      "Venkatesh Thalluri",
			AvatarURL: "http://www.gravatar.com/avatar/30e3b2122ccd6b8e45e8e14a3ffb58fc?s=80&d=identicon",
			Username:  "venky333",
		},
		Milestone: &Milestone{
			ID:          38,
			IID:         3,
			ProjectID:   8,
			Title:       "v2.0",
			Description: "In tempore culpa inventore quo accusantium.",
			State:       "closed",
			WebURL:      "",
		},
		ProjectID: 8,
		Assignees: []*IssueAssignee{{
			ID:        7,
			State:     "active",
			WebURL:    "http://localhost:3001/arnita",
			Name:      "Pamella Huel",
			AvatarURL: "http://www.gravatar.com/avatar/a2f5c6fcef64c9c69cb8779cb292be1b?s=80&d=identicon",
			Username:  "arnita",
		}},
		Assignee: &IssueAssignee{
			ID:        7,
			State:     "active",
			WebURL:    "http://localhost:3001/arnita",
			Name:      "Pamella Huel",
			AvatarURL: "http://www.gravatar.com/avatar/a2f5c6fcef64c9c69cb8779cb292be1b?s=80&d=identicon",
			Username:  "arnita",
		},
		ClosedBy:     nil,
		Title:        "Consequatur vero maxime deserunt laboriosam est voluptas dolorem.",
		CreatedAt:    nil,
		MovedToID:    0,
		Labels:       Labels{},
		LabelDetails: nil,
		Upvotes:      0,
		Downvotes:    0,
		DueDate:      nil,
		WebURL:       "http://localhost:3001/h5bp/html5-boilerplate/issues/6",
		References:   nil,
		TimeStats: &TimeStats{
			HumanTimeEstimate:   "",
			HumanTotalTimeSpent: "",
			TimeEstimate:        0,
			TotalTimeSpent:      0,
		},
		Confidential:     false,
		Weight:           0,
		DiscussionLocked: false,
		IssueType:        nil,
		Subscribed:       false,
		UserNotesCount:   8,
		Links: &IssueLinks{
			Self:       "http://localhost:3001/api/v4/projects/8/issues/6",
			Notes:      "http://localhost:3001/api/v4/projects/8/issues/6/notes",
			AwardEmoji: "http://localhost:3001/api/v4/projects/8/issues/6/award_emoji",
			Project:    "http://localhost:3001/api/v4/projects/8",
		},
		IssueLinkID:          0,
		MergeRequestCount:    0,
		EpicIssueID:          2,
		Epic:                 nil,
		TaskCompletionStatus: nil,
	}}

	is, resp, err := client.EpicIssues.ListEpicIssues(1, 5, nil, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, is)

	is, resp, err = client.EpicIssues.ListEpicIssues(1.01, 5, nil, nil)
	require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, is)

	is, resp, err = client.EpicIssues.ListEpicIssues(1, 5, nil, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, is)

	is, resp, err = client.EpicIssues.ListEpicIssues(3, 5, nil, nil)
	require.Error(t, err)
	require.Nil(t, is)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestEpicIssuesService_AssignEpicIssue(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1/epics/5/issues/55", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		fmt.Fprintf(w, `
			{
			  "id": 11,
			  "epic": {
				"id": 30,
				"iid": 5,
				"title": "Ea cupiditate dolores ut vero consequatur quasi veniam voluptatem et non.",
				"description": "Molestias dolorem eos vitae expedita impedit necessitatibus quo voluptatum.",
				"author": {
				  "id": 7,
				  "name": "Pamella Huel",
				  "username": "arnita",
				  "state": "active",
				  "avatar_url": "http://www.gravatar.com/avatar/a2f5c6fcef64c9c69cb8779cb292be1b?s=80&d=identicon",
				  "web_url": "http://localhost:3001/arnita"
				}
			  },
			  "issue": {
				"id": 55,
				"iid": 13,
				"project_id": 8,
				"title": "Beatae laborum voluptatem voluptate eligendi ex accusamus.",
				"description": "Quam veritatis debitis omnis aliquam sit.",
				"state": "opened",
				"labels": [],
				"milestone": {
				  "id": 48,
				  "iid": 6,
				  "project_id": 8,
				  "title": "Sprint - Sed sed maxime temporibus ipsa ullam qui sit.",
				  "description": "Quos veritatis qui expedita sunt deleniti accusamus.",
				  "state": "active"
				},
				"assignees": [{
				  "id": 10,
				  "name": "Lu Mayer",
				  "username": "kam",
				  "state": "active",
				  "avatar_url": "http://www.gravatar.com/avatar/018729e129a6f31c80a6327a30196823?s=80&d=identicon",
				  "web_url": "http://localhost:3001/kam"
				}],
				"assignee": {
				  "id": 10,
				  "name": "Lu Mayer",
				  "username": "kam",
				  "state": "active",
				  "avatar_url": "http://www.gravatar.com/avatar/018729e129a6f31c80a6327a30196823?s=80&d=identicon",
				  "web_url": "http://localhost:3001/kam"
				},
				"author": {
				  "id": 25,
				  "name": "User 3",
				  "username": "user3",
				  "state": "active",
				  "avatar_url": "http://www.gravatar.com/avatar/97d6d9441ff85fdc730e02a6068d267b?s=80&d=identicon",
				  "web_url": "http://localhost:3001/user3"
				},
				"user_notes_count": 0,
				"upvotes": 0,
				"downvotes": 0,
				"due_date": null,
				"confidential": false,
				"weight": null,
				"discussion_locked": null,
				"web_url": "http://localhost:3001/h5bp/html5-boilerplate/issues/13",
				"time_stats": {
				  "time_estimate": 0,
				  "total_time_spent": 0,
				  "human_time_estimate": null,
				  "human_total_time_spent": null
				}
			  }
			}
		`)
	})

	want := &EpicIssueAssignment{
		ID: 11,
		Epic: &Epic{
			ID:          30,
			IID:         5,
			GroupID:     0,
			ParentID:    0,
			Title:       "Ea cupiditate dolores ut vero consequatur quasi veniam voluptatem et non.",
			Description: "Molestias dolorem eos vitae expedita impedit necessitatibus quo voluptatum.",
			State:       "",
			WebURL:      "",
			Author: &EpicAuthor{
				ID:        7,
				State:     "active",
				WebURL:    "http://localhost:3001/arnita",
				Name:      "Pamella Huel",
				AvatarURL: "http://www.gravatar.com/avatar/a2f5c6fcef64c9c69cb8779cb292be1b?s=80&d=identicon",
				Username:  "arnita",
			},
			StartDateIsFixed: false,
			DueDateIsFixed:   false,
			Upvotes:          0, Downvotes: 0,
			UserNotesCount: 0,
			URL:            "",
		},
		Issue: &Issue{
			ID:          55,
			IID:         13,
			ExternalID:  "",
			State:       "opened",
			Description: "Quam veritatis debitis omnis aliquam sit.",
			Author: &IssueAuthor{
				ID:        25,
				State:     "active",
				WebURL:    "http://localhost:3001/user3",
				Name:      "User 3",
				AvatarURL: "http://www.gravatar.com/avatar/97d6d9441ff85fdc730e02a6068d267b?s=80&d=identicon",
				Username:  "user3",
			},
			Milestone: &Milestone{
				ID:          48,
				IID:         6,
				ProjectID:   8,
				Title:       "Sprint - Sed sed maxime temporibus ipsa ullam qui sit.",
				Description: "Quos veritatis qui expedita sunt deleniti accusamus.",
				State:       "active",
				WebURL:      "",
			},
			ProjectID: 8,
			Assignees: []*IssueAssignee{
				{
					ID:        10,
					State:     "active",
					WebURL:    "http://localhost:3001/kam",
					Name:      "Lu Mayer",
					AvatarURL: "http://www.gravatar.com/avatar/018729e129a6f31c80a6327a30196823?s=80&d=identicon",
					Username:  "kam",
				},
			},
			Assignee: &IssueAssignee{
				ID:        10,
				State:     "active",
				WebURL:    "http://localhost:3001/kam",
				Name:      "Lu Mayer",
				AvatarURL: "http://www.gravatar.com/avatar/018729e129a6f31c80a6327a30196823?s=80&d=identicon",
				Username:  "kam",
			},
			Title:     "Beatae laborum voluptatem voluptate eligendi ex accusamus.",
			MovedToID: 0,
			Labels:    Labels{},
			Upvotes:   0,
			Downvotes: 0,
			WebURL:    "http://localhost:3001/h5bp/html5-boilerplate/issues/13",
			TimeStats: &TimeStats{
				HumanTimeEstimate:   "",
				HumanTotalTimeSpent: "",
				TimeEstimate:        0,
				TotalTimeSpent:      0,
			},
			Confidential:      false,
			Weight:            0,
			DiscussionLocked:  false,
			Subscribed:        false,
			UserNotesCount:    0,
			IssueLinkID:       0,
			MergeRequestCount: 0,
			EpicIssueID:       0,
		},
	}

	eia, resp, err := client.EpicIssues.AssignEpicIssue(1, 5, 55, nil, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, eia)

	eia, resp, err = client.EpicIssues.AssignEpicIssue(1.01, 5, 55, nil, nil)
	require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, eia)

	eia, resp, err = client.EpicIssues.AssignEpicIssue(1, 5, 55, nil, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, eia)

	eia, resp, err = client.EpicIssues.AssignEpicIssue(3, 5, 55, nil, nil)
	require.Error(t, err)
	require.Nil(t, eia)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestEpicIssuesService_RemoveEpicIssue(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1/epics/5/issues/55", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodDelete)
		fmt.Fprintf(w, `
			{
			  "id": 11,
			  "epic": {
				"id": 30,
				"iid": 5,
				"title": "Ea cupiditate dolores ut vero consequatur quasi veniam voluptatem et non.",
				"description": "Molestias dolorem eos vitae expedita impedit necessitatibus quo voluptatum.",
				"author": {
				  "id": 7,
				  "name": "Pamella Huel",
				  "username": "arnita",
				  "state": "active",
				  "avatar_url": "http://www.gravatar.com/avatar/a2f5c6fcef64c9c69cb8779cb292be1b?s=80&d=identicon",
				  "web_url": "http://localhost:3001/arnita"
				}
			  },
			  "issue": {
				"id": 55,
				"iid": 13,
				"project_id": 8,
				"title": "Beatae laborum voluptatem voluptate eligendi ex accusamus.",
				"description": "Quam veritatis debitis omnis aliquam sit.",
				"state": "opened",
				"labels": [],
				"milestone": {
				  "id": 48,
				  "iid": 6,
				  "project_id": 8,
				  "title": "Sprint - Sed sed maxime temporibus ipsa ullam qui sit.",
				  "description": "Quos veritatis qui expedita sunt deleniti accusamus.",
				  "state": "active"
				},
				"assignees": [{
				  "id": 10,
				  "name": "Lu Mayer",
				  "username": "kam",
				  "state": "active",
				  "avatar_url": "http://www.gravatar.com/avatar/018729e129a6f31c80a6327a30196823?s=80&d=identicon",
				  "web_url": "http://localhost:3001/kam"
				}],
				"assignee": {
				  "id": 10,
				  "name": "Lu Mayer",
				  "username": "kam",
				  "state": "active",
				  "avatar_url": "http://www.gravatar.com/avatar/018729e129a6f31c80a6327a30196823?s=80&d=identicon",
				  "web_url": "http://localhost:3001/kam"
				},
				"author": {
				  "id": 25,
				  "name": "User 3",
				  "username": "user3",
				  "state": "active",
				  "avatar_url": "http://www.gravatar.com/avatar/97d6d9441ff85fdc730e02a6068d267b?s=80&d=identicon",
				  "web_url": "http://localhost:3001/user3"
				},
				"user_notes_count": 0,
				"upvotes": 0,
				"downvotes": 0,
				"due_date": null,
				"confidential": false,
				"weight": null,
				"discussion_locked": null,
				"web_url": "http://localhost:3001/h5bp/html5-boilerplate/issues/13",
				"time_stats": {
				  "time_estimate": 0,
				  "total_time_spent": 0,
				  "human_time_estimate": null,
				  "human_total_time_spent": null
				}
			  }
			}
		`)
	})

	want := &EpicIssueAssignment{
		ID: 11,
		Epic: &Epic{
			ID:          30,
			IID:         5,
			GroupID:     0,
			ParentID:    0,
			Title:       "Ea cupiditate dolores ut vero consequatur quasi veniam voluptatem et non.",
			Description: "Molestias dolorem eos vitae expedita impedit necessitatibus quo voluptatum.",
			State:       "",
			WebURL:      "",
			Author: &EpicAuthor{
				ID:        7,
				State:     "active",
				WebURL:    "http://localhost:3001/arnita",
				Name:      "Pamella Huel",
				AvatarURL: "http://www.gravatar.com/avatar/a2f5c6fcef64c9c69cb8779cb292be1b?s=80&d=identicon",
				Username:  "arnita",
			},
			StartDateIsFixed: false,
			DueDateIsFixed:   false,
			Upvotes:          0, Downvotes: 0,
			UserNotesCount: 0,
			URL:            "",
		},
		Issue: &Issue{
			ID:          55,
			IID:         13,
			ExternalID:  "",
			State:       "opened",
			Description: "Quam veritatis debitis omnis aliquam sit.",
			Author: &IssueAuthor{
				ID:        25,
				State:     "active",
				WebURL:    "http://localhost:3001/user3",
				Name:      "User 3",
				AvatarURL: "http://www.gravatar.com/avatar/97d6d9441ff85fdc730e02a6068d267b?s=80&d=identicon",
				Username:  "user3",
			},
			Milestone: &Milestone{
				ID:          48,
				IID:         6,
				ProjectID:   8,
				Title:       "Sprint - Sed sed maxime temporibus ipsa ullam qui sit.",
				Description: "Quos veritatis qui expedita sunt deleniti accusamus.",
				State:       "active",
				WebURL:      "",
			},
			ProjectID: 8,
			Assignees: []*IssueAssignee{
				{
					ID:        10,
					State:     "active",
					WebURL:    "http://localhost:3001/kam",
					Name:      "Lu Mayer",
					AvatarURL: "http://www.gravatar.com/avatar/018729e129a6f31c80a6327a30196823?s=80&d=identicon",
					Username:  "kam",
				},
			},
			Assignee: &IssueAssignee{
				ID:        10,
				State:     "active",
				WebURL:    "http://localhost:3001/kam",
				Name:      "Lu Mayer",
				AvatarURL: "http://www.gravatar.com/avatar/018729e129a6f31c80a6327a30196823?s=80&d=identicon",
				Username:  "kam",
			},
			Title:     "Beatae laborum voluptatem voluptate eligendi ex accusamus.",
			MovedToID: 0,
			Labels:    Labels{},
			Upvotes:   0,
			Downvotes: 0,
			WebURL:    "http://localhost:3001/h5bp/html5-boilerplate/issues/13",
			TimeStats: &TimeStats{
				HumanTimeEstimate:   "",
				HumanTotalTimeSpent: "",
				TimeEstimate:        0,
				TotalTimeSpent:      0,
			},
			Confidential:      false,
			Weight:            0,
			DiscussionLocked:  false,
			Subscribed:        false,
			UserNotesCount:    0,
			IssueLinkID:       0,
			MergeRequestCount: 0,
			EpicIssueID:       0,
		},
	}

	eia, resp, err := client.EpicIssues.RemoveEpicIssue(1, 5, 55, nil, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, eia)

	eia, resp, err = client.EpicIssues.RemoveEpicIssue(1.01, 5, 55, nil, nil)
	require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, eia)

	eia, resp, err = client.EpicIssues.RemoveEpicIssue(1, 5, 55, nil, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, eia)

	eia, resp, err = client.EpicIssues.RemoveEpicIssue(3, 5, 55, nil, nil)
	require.Error(t, err)
	require.Nil(t, eia)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestEpicIssuesService_UpdateEpicIssueAssignment(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1/epics/5/issues/2", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPut)
		fmt.Fprintf(w, `
			[
			  {
				"id": 76,
				"iid": 6,
				"project_id": 8,
				"title" : "Consequatur vero maxime deserunt laboriosam est voluptas dolorem.",
				"description" : "Ratione dolores corrupti mollitia soluta quia.",
				"state": "opened",
				"closed_at": null,
				"labels": [],
				"milestone": {
				  "id": 38,
				  "iid": 3,
				  "project_id": 8,
				  "title": "v2.0",
				  "description": "In tempore culpa inventore quo accusantium.",
				  "state": "closed"
				},
				"assignees": [{
				  "id": 7,
				  "name": "Pamella Huel",
				  "username": "arnita",
				  "state": "active",
				  "avatar_url": "http://www.gravatar.com/avatar/a2f5c6fcef64c9c69cb8779cb292be1b?s=80&d=identicon",
				  "web_url": "http://localhost:3001/arnita"
				}],
				"assignee": {
				  "id": 7,
				  "name": "Pamella Huel",
				  "username": "arnita",
				  "state": "active",
				  "avatar_url": "http://www.gravatar.com/avatar/a2f5c6fcef64c9c69cb8779cb292be1b?s=80&d=identicon",
				  "web_url": "http://localhost:3001/arnita"
				},
				"author": {
				  "id": 13,
				  "name": "Venkatesh Thalluri",
				  "username": "venky333",
				  "state": "active",
				  "avatar_url": "http://www.gravatar.com/avatar/30e3b2122ccd6b8e45e8e14a3ffb58fc?s=80&d=identicon",
				  "web_url": "http://localhost:3001/venky333"
				},
				"user_notes_count": 8,
				"upvotes": 0,
				"downvotes": 0,
				"due_date": null,
				"confidential": false,
				"weight": null,
				"discussion_locked": null,
				"web_url": "http://localhost:3001/h5bp/html5-boilerplate/issues/6",
				"time_stats": {
				  "time_estimate": 0,
				  "total_time_spent": 0,
				  "human_time_estimate": null,
				  "human_total_time_spent": null
				},
				"_links":{
				  "self": "http://localhost:3001/api/v4/projects/8/issues/6",
				  "notes": "http://localhost:3001/api/v4/projects/8/issues/6/notes",
				  "award_emoji": "http://localhost:3001/api/v4/projects/8/issues/6/award_emoji",
				  "project": "http://localhost:3001/api/v4/projects/8"
				},
				"epic_issue_id": 2
			  }
			]
		`)
	})

	want := []*Issue{{
		ID:          76,
		IID:         6,
		ExternalID:  "",
		State:       "opened",
		Description: "Ratione dolores corrupti mollitia soluta quia.",
		Author: &IssueAuthor{
			ID:        13,
			State:     "active",
			WebURL:    "http://localhost:3001/venky333",
			Name:      "Venkatesh Thalluri",
			AvatarURL: "http://www.gravatar.com/avatar/30e3b2122ccd6b8e45e8e14a3ffb58fc?s=80&d=identicon",
			Username:  "venky333",
		},
		Milestone: &Milestone{
			ID:          38,
			IID:         3,
			ProjectID:   8,
			Title:       "v2.0",
			Description: "In tempore culpa inventore quo accusantium.",
			State:       "closed",
			WebURL:      "",
		},
		ProjectID: 8,
		Assignees: []*IssueAssignee{{
			ID:        7,
			State:     "active",
			WebURL:    "http://localhost:3001/arnita",
			Name:      "Pamella Huel",
			AvatarURL: "http://www.gravatar.com/avatar/a2f5c6fcef64c9c69cb8779cb292be1b?s=80&d=identicon",
			Username:  "arnita",
		}},
		Assignee: &IssueAssignee{
			ID:        7,
			State:     "active",
			WebURL:    "http://localhost:3001/arnita",
			Name:      "Pamella Huel",
			AvatarURL: "http://www.gravatar.com/avatar/a2f5c6fcef64c9c69cb8779cb292be1b?s=80&d=identicon",
			Username:  "arnita",
		},
		ClosedBy:     nil,
		Title:        "Consequatur vero maxime deserunt laboriosam est voluptas dolorem.",
		CreatedAt:    nil,
		MovedToID:    0,
		Labels:       Labels{},
		LabelDetails: nil,
		Upvotes:      0,
		Downvotes:    0,
		DueDate:      nil,
		WebURL:       "http://localhost:3001/h5bp/html5-boilerplate/issues/6",
		References:   nil,
		TimeStats: &TimeStats{
			HumanTimeEstimate:   "",
			HumanTotalTimeSpent: "",
			TimeEstimate:        0,
			TotalTimeSpent:      0,
		},
		Confidential:     false,
		Weight:           0,
		DiscussionLocked: false,
		IssueType:        nil,
		Subscribed:       false,
		UserNotesCount:   8,
		Links: &IssueLinks{
			Self:       "http://localhost:3001/api/v4/projects/8/issues/6",
			Notes:      "http://localhost:3001/api/v4/projects/8/issues/6/notes",
			AwardEmoji: "http://localhost:3001/api/v4/projects/8/issues/6/award_emoji",
			Project:    "http://localhost:3001/api/v4/projects/8",
		},
		IssueLinkID:          0,
		MergeRequestCount:    0,
		EpicIssueID:          2,
		Epic:                 nil,
		TaskCompletionStatus: nil,
	}}

	is, resp, err := client.EpicIssues.UpdateEpicIssueAssignment(1, 5, 2, nil, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, is)

	is, resp, err = client.EpicIssues.UpdateEpicIssueAssignment(1.01, 5, 2, nil, nil)
	require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, is)

	is, resp, err = client.EpicIssues.UpdateEpicIssueAssignment(1, 5, 2, nil, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, is)

	is, resp, err = client.EpicIssues.UpdateEpicIssueAssignment(3, 5, 2, nil, nil)
	require.Error(t, err)
	require.Nil(t, is)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/epics.go000066400000000000000000000251431475761473200225160ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"fmt"
	"net/http"
	"time"
)

// EpicsService handles communication with the epic related methods
// of the GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html
type EpicsService struct {
	client *Client
}

// EpicAuthor represents a author of the epic.
type EpicAuthor struct {
	ID        int    `json:"id"`
	State     string `json:"state"`
	WebURL    string `json:"web_url"`
	Name      string `json:"name"`
	AvatarURL string `json:"avatar_url"`
	Username  string `json:"username"`
}

// Epic represents a GitLab epic.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html
type Epic struct {
	ID                      int         `json:"id"`
	IID                     int         `json:"iid"`
	GroupID                 int         `json:"group_id"`
	ParentID                int         `json:"parent_id"`
	Title                   string      `json:"title"`
	Description             string      `json:"description"`
	State                   string      `json:"state"`
	Confidential            bool        `json:"confidential"`
	WebURL                  string      `json:"web_url"`
	Author                  *EpicAuthor `json:"author"`
	StartDate               *ISOTime    `json:"start_date"`
	StartDateIsFixed        bool        `json:"start_date_is_fixed"`
	StartDateFixed          *ISOTime    `json:"start_date_fixed"`
	StartDateFromMilestones *ISOTime    `json:"start_date_from_milestones"`
	DueDate                 *ISOTime    `json:"due_date"`
	DueDateIsFixed          bool        `json:"due_date_is_fixed"`
	DueDateFixed            *ISOTime    `json:"due_date_fixed"`
	DueDateFromMilestones   *ISOTime    `json:"due_date_from_milestones"`
	CreatedAt               *time.Time  `json:"created_at"`
	UpdatedAt               *time.Time  `json:"updated_at"`
	ClosedAt                *time.Time  `json:"closed_at"`
	Labels                  []string    `json:"labels"`
	Upvotes                 int         `json:"upvotes"`
	Downvotes               int         `json:"downvotes"`
	UserNotesCount          int         `json:"user_notes_count"`
	URL                     string      `json:"url"`
}

func (e Epic) String() string {
	return Stringify(e)
}

// ListGroupEpicsOptions represents the available ListGroupEpics() options.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html#list-epics-for-a-group
type ListGroupEpicsOptions struct {
	ListOptions
	AuthorID                *int          `url:"author_id,omitempty" json:"author_id,omitempty"`
	Labels                  *LabelOptions `url:"labels,comma,omitempty" json:"labels,omitempty"`
	WithLabelDetails        *bool         `url:"with_labels_details,omitempty" json:"with_labels_details,omitempty"`
	OrderBy                 *string       `url:"order_by,omitempty" json:"order_by,omitempty"`
	Sort                    *string       `url:"sort,omitempty" json:"sort,omitempty"`
	Search                  *string       `url:"search,omitempty" json:"search,omitempty"`
	State                   *string       `url:"state,omitempty" json:"state,omitempty"`
	CreatedAfter            *time.Time    `url:"created_after,omitempty" json:"created_after,omitempty"`
	CreatedBefore           *time.Time    `url:"created_before,omitempty" json:"created_before,omitempty"`
	UpdatedAfter            *time.Time    `url:"updated_after,omitempty" json:"updated_after,omitempty"`
	UpdatedBefore           *time.Time    `url:"updated_before,omitempty" json:"updated_before,omitempty"`
	IncludeAncestorGroups   *bool         `url:"include_ancestor_groups,omitempty" json:"include_ancestor_groups,omitempty"`
	IncludeDescendantGroups *bool         `url:"include_descendant_groups,omitempty" json:"include_descendant_groups,omitempty"`
	MyReactionEmoji         *string       `url:"my_reaction_emoji,omitempty" json:"my_reaction_emoji,omitempty"`
}

// ListGroupEpics gets a list of group epics. This function accepts pagination
// parameters page and per_page to return the list of group epics.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html#list-epics-for-a-group
func (s *EpicsService) ListGroupEpics(gid interface{}, opt *ListGroupEpicsOptions, options ...RequestOptionFunc) ([]*Epic, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/epics", PathEscape(group))

	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var es []*Epic
	resp, err := s.client.Do(req, &es)
	if err != nil {
		return nil, resp, err
	}

	return es, resp, nil
}

// GetEpic gets a single group epic.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html#single-epic
func (s *EpicsService) GetEpic(gid interface{}, epic int, options ...RequestOptionFunc) (*Epic, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/epics/%d", PathEscape(group), epic)

	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	e := new(Epic)
	resp, err := s.client.Do(req, e)
	if err != nil {
		return nil, resp, err
	}

	return e, resp, nil
}

// GetEpicLinks gets all child epics of an epic.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epic_links.html
func (s *EpicsService) GetEpicLinks(gid interface{}, epic int, options ...RequestOptionFunc) ([]*Epic, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/epics/%d/epics", PathEscape(group), epic)

	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	var e []*Epic
	resp, err := s.client.Do(req, &e)
	if err != nil {
		return nil, resp, err
	}

	return e, resp, nil
}

// CreateEpicOptions represents the available CreateEpic() options.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html#new-epic
type CreateEpicOptions struct {
	Title            *string       `url:"title,omitempty" json:"title,omitempty"`
	Labels           *LabelOptions `url:"labels,comma,omitempty" json:"labels,omitempty"`
	Description      *string       `url:"description,omitempty" json:"description,omitempty"`
	Color            *string       `url:"color,omitempty" json:"color,omitempty"`
	Confidential     *bool         `url:"confidential,omitempty" json:"confidential,omitempty"`
	CreatedAt        *time.Time    `url:"created_at,omitempty" json:"created_at,omitempty"`
	StartDateIsFixed *bool         `url:"start_date_is_fixed,omitempty" json:"start_date_is_fixed,omitempty"`
	StartDateFixed   *ISOTime      `url:"start_date_fixed,omitempty" json:"start_date_fixed,omitempty"`
	DueDateIsFixed   *bool         `url:"due_date_is_fixed,omitempty" json:"due_date_is_fixed,omitempty"`
	DueDateFixed     *ISOTime      `url:"due_date_fixed,omitempty" json:"due_date_fixed,omitempty"`
	ParentID         *int          `url:"parent_id,omitempty" json:"parent_id,omitempty"`
}

// CreateEpic creates a new group epic.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html#new-epic
func (s *EpicsService) CreateEpic(gid interface{}, opt *CreateEpicOptions, options ...RequestOptionFunc) (*Epic, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/epics", PathEscape(group))

	req, err := s.client.NewRequest(http.MethodPost, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	e := new(Epic)
	resp, err := s.client.Do(req, e)
	if err != nil {
		return nil, resp, err
	}

	return e, resp, nil
}

// UpdateEpicOptions represents the available UpdateEpic() options.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html#update-epic
type UpdateEpicOptions struct {
	AddLabels        *LabelOptions `url:"add_labels,omitempty" json:"add_labels,omitempty"`
	Confidential     *bool         `url:"confidential,omitempty" json:"confidential,omitempty"`
	Description      *string       `url:"description,omitempty" json:"description,omitempty"`
	DueDateFixed     *ISOTime      `url:"due_date_fixed,omitempty" json:"due_date_fixed,omitempty"`
	DueDateIsFixed   *bool         `url:"due_date_is_fixed,omitempty" json:"due_date_is_fixed,omitempty"`
	Labels           *LabelOptions `url:"labels,comma,omitempty" json:"labels,omitempty"`
	ParentID         *int          `url:"parent_id,omitempty" json:"parent_id,omitempty"`
	RemoveLabels     *LabelOptions `url:"remove_labels,omitempty" json:"remove_labels,omitempty"`
	StartDateFixed   *ISOTime      `url:"start_date_fixed,omitempty" json:"start_date_fixed,omitempty"`
	StartDateIsFixed *bool         `url:"start_date_is_fixed,omitempty" json:"start_date_is_fixed,omitempty"`
	StateEvent       *string       `url:"state_event,omitempty" json:"state_event,omitempty"`
	Title            *string       `url:"title,omitempty" json:"title,omitempty"`
	UpdatedAt        *time.Time    `url:"updated_at,omitempty" json:"updated_at,omitempty"`
	Color            *string       `url:"color,omitempty" json:"color,omitempty"`
}

// UpdateEpic updates an existing group epic. This function is also used
// to mark an epic as closed.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html#update-epic
func (s *EpicsService) UpdateEpic(gid interface{}, epic int, opt *UpdateEpicOptions, options ...RequestOptionFunc) (*Epic, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/epics/%d", PathEscape(group), epic)

	req, err := s.client.NewRequest(http.MethodPut, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	e := new(Epic)
	resp, err := s.client.Do(req, e)
	if err != nil {
		return nil, resp, err
	}

	return e, resp, nil
}

// DeleteEpic deletes a single group epic.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html#delete-epic
func (s *EpicsService) DeleteEpic(gid interface{}, epic int, options ...RequestOptionFunc) (*Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("groups/%s/epics/%d", PathEscape(group), epic)

	req, err := s.client.NewRequest(http.MethodDelete, u, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/epics_test.go000066400000000000000000000101151475761473200235460ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"fmt"
	"net/http"
	"reflect"
	"testing"
)

func TestGetEpic(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/7/epics/8", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprint(w, `{"id":8, "title": "Incredible idea", "description": "This is a test epic", "author" : {"id" : 26, "name": "jramsay"}}`)
	})

	epic, _, err := client.Epics.GetEpic("7", 8)
	if err != nil {
		t.Fatal(err)
	}

	want := &Epic{
		ID:          8,
		Title:       "Incredible idea",
		Description: "This is a test epic",
		Author:      &EpicAuthor{ID: 26, Name: "jramsay"},
	}

	if !reflect.DeepEqual(want, epic) {
		t.Errorf("Epics.GetEpic returned %+v, want %+v", epic, want)
	}
}

func TestDeleteEpic(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/7/epics/8", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodDelete)
	})

	_, err := client.Epics.DeleteEpic("7", 8)
	if err != nil {
		t.Fatal(err)
	}
}

func TestListGroupEpics(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/7/epics", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		testURL(t, r, "/api/v4/groups/7/epics?author_id=26&state=opened")
		fmt.Fprint(w, `[{"id":8, "title": "Incredible idea", "description": "This is a test epic", "author" : {"id" : 26, "name": "jramsay"}}]`)
	})

	listGroupEpics := &ListGroupEpicsOptions{
		AuthorID: Ptr(26),
		State:    Ptr("opened"),
	}

	epics, _, err := client.Epics.ListGroupEpics("7", listGroupEpics)
	if err != nil {
		t.Fatal(err)
	}

	want := []*Epic{{
		ID:          8,
		Title:       "Incredible idea",
		Description: "This is a test epic",
		Author:      &EpicAuthor{ID: 26, Name: "jramsay"},
	}}

	if !reflect.DeepEqual(want, epics) {
		t.Errorf("Epics.ListGroupEpics returned %+v, want %+v", epics, want)
	}
}

func TestCreateEpic(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/7/epics", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		fmt.Fprint(w, `{"id":8, "title": "Incredible idea", "description": "This is a test epic", "author" : {"id" : 26, "name": "jramsay"}}`)
	})

	createEpicOptions := &CreateEpicOptions{
		Title:       Ptr("Incredible idea"),
		Description: Ptr("This is a test epic"),
	}

	epic, _, err := client.Epics.CreateEpic("7", createEpicOptions)
	if err != nil {
		t.Fatal(err)
	}

	want := &Epic{
		ID:          8,
		Title:       "Incredible idea",
		Description: "This is a test epic",
		Author:      &EpicAuthor{ID: 26, Name: "jramsay"},
	}

	if !reflect.DeepEqual(want, epic) {
		t.Errorf("Epics.CreateEpic returned %+v, want %+v", epic, want)
	}
}

func TestUpdateEpic(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/7/epics/8", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPut)
		fmt.Fprint(w, `{"id":8, "title": "Incredible idea", "description": "This is a test epic", "author" : {"id" : 26, "name": "jramsay"}}`)
	})

	updateEpicOptions := &UpdateEpicOptions{
		Title:       Ptr("Incredible idea"),
		Description: Ptr("This is a test epic"),
	}

	epic, _, err := client.Epics.UpdateEpic("7", 8, updateEpicOptions)
	if err != nil {
		t.Fatal(err)
	}

	want := &Epic{
		ID:          8,
		Title:       "Incredible idea",
		Description: "This is a test epic",
		Author:      &EpicAuthor{ID: 26, Name: "jramsay"},
	}

	if !reflect.DeepEqual(want, epic) {
		t.Errorf("Epics.UpdateEpic returned %+v, want %+v", epic, want)
	}
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/error_tracking.go000066400000000000000000000136621475761473200244310ustar00rootroot00000000000000//
// Copyright 2022, Ryan Glab 
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"fmt"
	"net/http"
)

// ErrorTrackingService handles communication with the error tracking
// methods of the GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/error_tracking.html
type ErrorTrackingService struct {
	client *Client
}

// ErrorTrackingClientKey represents an error tracking client key.
//
// GitLab docs:
// https://docs.gitlab.com/ee/api/error_tracking.html#error-tracking-client-keys
type ErrorTrackingClientKey struct {
	ID        int    `json:"id"`
	Active    bool   `json:"active"`
	PublicKey string `json:"public_key"`
	SentryDsn string `json:"sentry_dsn"`
}

func (p ErrorTrackingClientKey) String() string {
	return Stringify(p)
}

// ErrorTrackingSettings represents error tracking settings for a GitLab project.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/error_tracking.html#error-tracking-project-settings
type ErrorTrackingSettings struct {
	Active            bool   `json:"active"`
	ProjectName       string `json:"project_name"`
	SentryExternalURL string `json:"sentry_external_url"`
	APIURL            string `json:"api_url"`
	Integrated        bool   `json:"integrated"`
}

func (p ErrorTrackingSettings) String() string {
	return Stringify(p)
}

// GetErrorTrackingSettings gets error tracking settings.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/error_tracking.html#get-error-tracking-settings
func (s *ErrorTrackingService) GetErrorTrackingSettings(pid interface{}, options ...RequestOptionFunc) (*ErrorTrackingSettings, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/error_tracking/settings", PathEscape(project))

	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	ets := new(ErrorTrackingSettings)
	resp, err := s.client.Do(req, ets)
	if err != nil {
		return nil, resp, err
	}

	return ets, resp, nil
}

// EnableDisableErrorTrackingOptions represents the available
// EnableDisableErrorTracking() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/error_tracking.html#enable-or-disable-the-error-tracking-project-settings
type EnableDisableErrorTrackingOptions struct {
	Active     *bool `url:"active,omitempty" json:"active,omitempty"`
	Integrated *bool `url:"integrated,omitempty" json:"integrated,omitempty"`
}

// EnableDisableErrorTracking allows you to enable or disable the error tracking
// settings for a project.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/error_tracking.html#enable-or-disable-the-error-tracking-project-settings
func (s *ErrorTrackingService) EnableDisableErrorTracking(pid interface{}, opt *EnableDisableErrorTrackingOptions, options ...RequestOptionFunc) (*ErrorTrackingSettings, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/error_tracking/settings", PathEscape(project))

	req, err := s.client.NewRequest(http.MethodPatch, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	ets := new(ErrorTrackingSettings)
	resp, err := s.client.Do(req, &ets)
	if err != nil {
		return nil, resp, err
	}

	return ets, resp, nil
}

// ListClientKeysOptions represents the available ListClientKeys() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/error_tracking.html#list-project-client-keys
type ListClientKeysOptions ListOptions

// ListClientKeys lists error tracking project client keys.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/error_tracking.html#list-project-client-keys
func (s *ErrorTrackingService) ListClientKeys(pid interface{}, opt *ListClientKeysOptions, options ...RequestOptionFunc) ([]*ErrorTrackingClientKey, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/error_tracking/client_keys", PathEscape(project))

	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var cks []*ErrorTrackingClientKey
	resp, err := s.client.Do(req, &cks)
	if err != nil {
		return nil, resp, err
	}

	return cks, resp, nil
}

// CreateClientKey creates a new client key for a project.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/error_tracking.html#create-a-client-key
func (s *ErrorTrackingService) CreateClientKey(pid interface{}, options ...RequestOptionFunc) (*ErrorTrackingClientKey, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/error_tracking/client_keys", PathEscape(project))

	req, err := s.client.NewRequest(http.MethodPost, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	ck := new(ErrorTrackingClientKey)
	resp, err := s.client.Do(req, ck)
	if err != nil {
		return nil, resp, err
	}

	return ck, resp, nil
}

// DeleteClientKey removes a client key from the project.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/error_tracking.html#delete-a-client-key
func (s *ErrorTrackingService) DeleteClientKey(pid interface{}, keyID int, options ...RequestOptionFunc) (*Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("projects/%s/error_tracking/client_keys/%d", PathEscape(project), keyID)

	req, err := s.client.NewRequest(http.MethodDelete, u, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/error_tracking_test.go000066400000000000000000000123321475761473200254610ustar00rootroot00000000000000//
// Copyright 2022, Ryan Glab 
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"fmt"
	"net/http"
	"reflect"
	"testing"
)

func TestGetErrorTracking(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/error_tracking/settings", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprint(w, `{
			"active": true,
			"project_name": "sample sentry project",
			"sentry_external_url": "https://sentry.io/myawesomeproject/project",
			"api_url": "https://sentry.io/api/1/projects/myawesomeproject/project",
			"integrated": false
		}`)
	})

	et, _, err := client.ErrorTracking.GetErrorTrackingSettings(1)
	if err != nil {
		t.Errorf("ErrorTracking.GetErrorTracking returned error: %v", err)
	}

	want := &ErrorTrackingSettings{
		Active:            true,
		ProjectName:       "sample sentry project",
		SentryExternalURL: "https://sentry.io/myawesomeproject/project",
		APIURL:            "https://sentry.io/api/1/projects/myawesomeproject/project",
		Integrated:        false,
	}

	if !reflect.DeepEqual(want, et) {
		t.Errorf("ErrorTracking.GetErrorTracking returned %+v, want %+v", et, want)
	}
}

func TestDisableErrorTracking(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/error_tracking/settings", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPatch)
		fmt.Fprint(w, `{
			"active": false,
			"project_name": "sample sentry project",
			"sentry_external_url": "https://sentry.io/myawesomeproject/project",
			"api_url": "https://sentry.io/api/1/projects/myawesomeproject/project",
			"integrated": false
		}`)
	})

	et, _, err := client.ErrorTracking.EnableDisableErrorTracking(
		1,
		&EnableDisableErrorTrackingOptions{
			Active:     Ptr(false),
			Integrated: Ptr(false),
		},
	)
	if err != nil {
		t.Errorf("ErrorTracking.EnableDisableErrorTracking returned error: %v", err)
	}

	want := &ErrorTrackingSettings{
		Active:            false,
		ProjectName:       "sample sentry project",
		SentryExternalURL: "https://sentry.io/myawesomeproject/project",
		APIURL:            "https://sentry.io/api/1/projects/myawesomeproject/project",
		Integrated:        false,
	}

	if !reflect.DeepEqual(want, et) {
		t.Errorf("ErrorTracking.EnableDisableErrorTracking returned %+v, want %+v", et, want)
	}
}

func TestListErrorTrackingClientKeys(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/error_tracking/client_keys", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprint(w, `[
			{
				"id": 1,
				"active": true,
				"public_key": "glet_aa77551d849c083f76d0bc545ed053a3",
				"sentry_dsn": "https://glet_aa77551d849c083f76d0bc545ed053a3@gitlab.example.com/api/v4/error_tracking/collector/5"
			}
		]`)
	})

	cks, _, err := client.ErrorTracking.ListClientKeys(1, &ListClientKeysOptions{Page: 1, PerPage: 10})
	if err != nil {
		t.Errorf("ErrorTracking.ListErrorTrackingClientKeys returned error: %v", err)
	}

	want := []*ErrorTrackingClientKey{{
		ID:        1,
		Active:    true,
		PublicKey: "glet_aa77551d849c083f76d0bc545ed053a3",
		SentryDsn: "https://glet_aa77551d849c083f76d0bc545ed053a3@gitlab.example.com/api/v4/error_tracking/collector/5",
	}}

	if !reflect.DeepEqual(want, cks) {
		t.Errorf("ErrorTracking.ListErrorTrackingClientKeys returned %+v, want %+v", cks, want)
	}
}

func TestCreateClientKey(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/error_tracking/client_keys", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		fmt.Fprint(w, `{
			"id": 1,
			"active": true,
			"public_key": "glet_aa77551d849c083f76d0bc545ed053a3",
			"sentry_dsn": "https://glet_aa77551d849c083f76d0bc545ed053a3@gitlab.example.com/api/v4/error_tracking/collector/5"
		}`)
	})

	ck, _, err := client.ErrorTracking.CreateClientKey(1)
	if err != nil {
		t.Errorf("ErrorTracking.CreateClientKey returned error: %v", err)
	}

	want := &ErrorTrackingClientKey{
		ID:        1,
		Active:    true,
		PublicKey: "glet_aa77551d849c083f76d0bc545ed053a3",
		SentryDsn: "https://glet_aa77551d849c083f76d0bc545ed053a3@gitlab.example.com/api/v4/error_tracking/collector/5",
	}

	if !reflect.DeepEqual(want, ck) {
		t.Errorf("ErrorTracking.CreateClientKey returned %+v, want %+v", ck, want)
	}
}

func TestDeleteClientKey(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/error_tracking/client_keys/3", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodDelete)
		testURL(t, r, "/api/v4/projects/1/error_tracking/client_keys/3")
	})

	_, err := client.ErrorTracking.DeleteClientKey(1, 3)
	if err != nil {
		t.Errorf("ErrorTracking.DeleteClientKey returned error: %v", err)
	}
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/event_parsing.go000066400000000000000000000213051475761473200242530ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"encoding/json"
	"fmt"
	"net/http"
)

// EventType represents a Gitlab event type.
type EventType string

// List of available event types.
const (
	EventConfidentialIssue       EventType = "Confidential Issue Hook"
	EventConfidentialNote        EventType = "Confidential Note Hook"
	EventTypeBuild               EventType = "Build Hook"
	EventTypeDeployment          EventType = "Deployment Hook"
	EventTypeFeatureFlag         EventType = "Feature Flag Hook"
	EventTypeIssue               EventType = "Issue Hook"
	EventTypeJob                 EventType = "Job Hook"
	EventTypeMember              EventType = "Member Hook"
	EventTypeMergeRequest        EventType = "Merge Request Hook"
	EventTypeNote                EventType = "Note Hook"
	EventTypePipeline            EventType = "Pipeline Hook"
	EventTypePush                EventType = "Push Hook"
	EventTypeRelease             EventType = "Release Hook"
	EventTypeResourceAccessToken EventType = "Resource Access Token Hook"
	EventTypeServiceHook         EventType = "Service Hook"
	EventTypeSubGroup            EventType = "Subgroup Hook"
	EventTypeSystemHook          EventType = "System Hook"
	EventTypeTagPush             EventType = "Tag Push Hook"
	EventTypeWikiPage            EventType = "Wiki Page Hook"
)

const (
	eventObjectKindPush         = "push"
	eventObjectKindTagPush      = "tag_push"
	eventObjectKindMergeRequest = "merge_request"
)

const (
	noteableTypeCommit       = "Commit"
	noteableTypeIssue        = "Issue"
	noteableTypeMergeRequest = "MergeRequest"
	noteableTypeSnippet      = "Snippet"
)

type noteEvent struct {
	ObjectKind       string `json:"object_kind"`
	ObjectAttributes struct {
		NoteableType string `json:"noteable_type"`
	} `json:"object_attributes"`
}

type serviceEvent struct {
	ObjectKind string `json:"object_kind"`
}

const eventTokenHeader = "X-Gitlab-Token"

// HookEventToken returns the token for the given request.
func HookEventToken(r *http.Request) string {
	return r.Header.Get(eventTokenHeader)
}

const eventTypeHeader = "X-Gitlab-Event"

// HookEventType returns the event type for the given request.
func HookEventType(r *http.Request) EventType {
	return EventType(r.Header.Get(eventTypeHeader))
}

// ParseHook tries to parse both web- and system hooks.
//
// Example usage:
//
//	func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
//	    payload, err := io.ReadAll(r.Body)
//	    if err != nil { ... }
//	    event, err := gitlab.ParseHook(gitlab.HookEventType(r), payload)
//	    if err != nil { ... }
//	    switch event := event.(type) {
//	    case *gitlab.PushEvent:
//	        processPushEvent(event)
//	    case *gitlab.MergeEvent:
//	        processMergeEvent(event)
//	    ...
//	    }
//	}
func ParseHook(eventType EventType, payload []byte) (event interface{}, err error) {
	switch eventType {
	case EventTypeSystemHook:
		return ParseSystemhook(payload)
	default:
		return ParseWebhook(eventType, payload)
	}
}

// ParseSystemhook parses the event payload. For recognized event types, a
// value of the corresponding struct type will be returned. An error will be
// returned for unrecognized event types.
//
// Example usage:
//
//	func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
//	    payload, err := io.ReadAll(r.Body)
//	    if err != nil { ... }
//	    event, err := gitlab.ParseSystemhook(payload)
//	    if err != nil { ... }
//	    switch event := event.(type) {
//	    case *gitlab.PushSystemEvent:
//	        processPushSystemEvent(event)
//	    case *gitlab.MergeSystemEvent:
//	        processMergeSystemEvent(event)
//	    ...
//	    }
//	}
func ParseSystemhook(payload []byte) (event interface{}, err error) {
	e := &systemHookEvent{}
	err = json.Unmarshal(payload, e)
	if err != nil {
		return nil, err
	}

	switch e.EventName {
	case eventObjectKindPush:
		event = &PushSystemEvent{}
	case eventObjectKindTagPush:
		event = &TagPushSystemEvent{}
	case "repository_update":
		event = &RepositoryUpdateSystemEvent{}
	case
		"project_create",
		"project_update",
		"project_destroy",
		"project_transfer",
		"project_rename":
		event = &ProjectSystemEvent{}
	case
		"group_create",
		"group_destroy",
		"group_rename":
		event = &GroupSystemEvent{}
	case "key_create", "key_destroy":
		event = &KeySystemEvent{}
	case
		"user_create",
		"user_destroy",
		"user_rename",
		"user_failed_login":
		event = &UserSystemEvent{}
	case
		"user_add_to_group",
		"user_remove_from_group",
		"user_update_for_group":
		event = &UserGroupSystemEvent{}
	case
		"user_add_to_team",
		"user_remove_from_team",
		"user_update_for_team":
		event = &UserTeamSystemEvent{}
	default:
		switch e.ObjectKind {
		case string(MergeRequestEventTargetType):
			event = &MergeEvent{}
		default:
			return nil, fmt.Errorf("unexpected system hook type %s", e.EventName)
		}
	}

	if err := json.Unmarshal(payload, event); err != nil {
		return nil, err
	}

	return event, nil
}

// WebhookEventType returns the event type for the given request.
func WebhookEventType(r *http.Request) EventType {
	return EventType(r.Header.Get(eventTypeHeader))
}

// ParseWebhook parses the event payload. For recognized event types, a
// value of the corresponding struct type will be returned. An error will
// be returned for unrecognized event types.
//
// Example usage:
//
//	func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
//	    payload, err := io.ReadAll(r.Body)
//	    if err != nil { ... }
//	    event, err := gitlab.ParseWebhook(gitlab.HookEventType(r), payload)
//	    if err != nil { ... }
//	    switch event := event.(type) {
//	    case *gitlab.PushEvent:
//	        processPushEvent(event)
//	    case *gitlab.MergeEvent:
//	        processMergeEvent(event)
//	    ...
//	    }
//	}
func ParseWebhook(eventType EventType, payload []byte) (event interface{}, err error) {
	switch eventType {
	case EventTypeBuild:
		event = &BuildEvent{}
	case EventTypeDeployment:
		event = &DeploymentEvent{}
	case EventTypeFeatureFlag:
		event = &FeatureFlagEvent{}
	case EventTypeIssue, EventConfidentialIssue:
		event = &IssueEvent{}
	case EventTypeJob:
		event = &JobEvent{}
	case EventTypeMember:
		event = &MemberEvent{}
	case EventTypeMergeRequest:
		event = &MergeEvent{}
	case EventTypeNote, EventConfidentialNote:
		note := ¬eEvent{}
		err := json.Unmarshal(payload, note)
		if err != nil {
			return nil, err
		}

		if note.ObjectKind != string(NoteEventTargetType) {
			return nil, fmt.Errorf("unexpected object kind %s", note.ObjectKind)
		}

		switch note.ObjectAttributes.NoteableType {
		case noteableTypeCommit:
			event = &CommitCommentEvent{}
		case noteableTypeMergeRequest:
			event = &MergeCommentEvent{}
		case noteableTypeIssue:
			event = &IssueCommentEvent{}
		case noteableTypeSnippet:
			event = &SnippetCommentEvent{}
		default:
			return nil, fmt.Errorf("unexpected noteable type %s", note.ObjectAttributes.NoteableType)
		}
	case EventTypePipeline:
		event = &PipelineEvent{}
	case EventTypePush:
		event = &PushEvent{}
	case EventTypeRelease:
		event = &ReleaseEvent{}
	case EventTypeResourceAccessToken:
		data := map[string]interface{}{}
		err := json.Unmarshal(payload, &data)
		if err != nil {
			return nil, err
		}

		_, groupEvent := data["group"]
		_, projectEvent := data["project"]

		switch {
		case groupEvent:
			event = &GroupResourceAccessTokenEvent{}
		case projectEvent:
			event = &ProjectResourceAccessTokenEvent{}
		default:
			return nil, fmt.Errorf("unexpected resource access token payload")
		}
	case EventTypeServiceHook:
		service := &serviceEvent{}
		err := json.Unmarshal(payload, service)
		if err != nil {
			return nil, err
		}
		switch service.ObjectKind {
		case eventObjectKindPush:
			event = &PushEvent{}
		case eventObjectKindTagPush:
			event = &TagEvent{}
		case eventObjectKindMergeRequest:
			event = &MergeEvent{}
		default:
			return nil, fmt.Errorf("unexpected service type %s", service.ObjectKind)
		}
	case EventTypeSubGroup:
		event = &SubGroupEvent{}
	case EventTypeTagPush:
		event = &TagEvent{}
	case EventTypeWikiPage:
		event = &WikiPageEvent{}
	default:
		return nil, fmt.Errorf("unexpected event type: %s", eventType)
	}

	if err := json.Unmarshal(payload, event); err != nil {
		return nil, err
	}

	return event, nil
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/event_parsing_systemhook_test.go000066400000000000000000000154651475761473200276110ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestParseSystemhookPush(t *testing.T) {
	payload := loadFixture(t, "testdata/systemhooks/push.json")

	parsedEvent, err := ParseSystemhook(payload)
	if err != nil {
		t.Errorf("Error parsing build hook: %s", err)
	}

	event, ok := parsedEvent.(*PushSystemEvent)
	if !ok {
		t.Errorf("Expected PushSystemHookEvent, but parsing produced %T", parsedEvent)
	}
	assert.Equal(t, eventObjectKindPush, event.EventName)
}

func TestParseSystemhookTagPush(t *testing.T) {
	payload := loadFixture(t, "testdata/systemhooks/tag_push.json")

	parsedEvent, err := ParseSystemhook(payload)
	if err != nil {
		t.Errorf("Error parsing build hook: %s", err)
	}

	event, ok := parsedEvent.(*TagPushSystemEvent)
	if !ok {
		t.Errorf("Expected TagPushSystemHookEvent, but parsing produced %T", parsedEvent)
	}
	assert.Equal(t, eventObjectKindTagPush, event.EventName)
}

func TestParseSystemhookMergeRequest(t *testing.T) {
	payload := loadFixture(t, "testdata/systemhooks/merge_request.json")

	parsedEvent, err := ParseSystemhook(payload)
	if err != nil {
		t.Errorf("Error parsing build hook: %s", err)
	}

	event, ok := parsedEvent.(*MergeEvent)
	if !ok {
		t.Errorf("Expected MergeRequestSystemHookEvent, but parsing produced %T", parsedEvent)
	}
	assert.Equal(t, eventObjectKindMergeRequest, event.ObjectKind)
}

func TestParseSystemhookRepositoryUpdate(t *testing.T) {
	payload := loadFixture(t, "testdata/systemhooks/repository_update.json")

	parsedEvent, err := ParseSystemhook(payload)
	if err != nil {
		t.Errorf("Error parsing build hook: %s", err)
	}

	event, ok := parsedEvent.(*RepositoryUpdateSystemEvent)
	if !ok {
		t.Errorf("Expected RepositoryUpdateSystemHookEvent, but parsing produced %T", parsedEvent)
	}
	assert.Equal(t, "repository_update", event.EventName)
}

func TestParseSystemhookProject(t *testing.T) {
	tests := []struct {
		event   string
		payload []byte
	}{
		{"project_create", loadFixture(t, "testdata/systemhooks/project_create.json")},
		{"project_update", loadFixture(t, "testdata/systemhooks/project_update.json")},
		{"project_destroy", loadFixture(t, "testdata/systemhooks/project_destroy.json")},
		{"project_transfer", loadFixture(t, "testdata/systemhooks/project_transfer.json")},
		{"project_rename", loadFixture(t, "testdata/systemhooks/project_rename.json")},
	}
	for _, tc := range tests {
		t.Run(tc.event, func(t *testing.T) {
			parsedEvent, err := ParseSystemhook(tc.payload)
			if err != nil {
				t.Errorf("Error parsing build hook: %s", err)
			}
			event, ok := parsedEvent.(*ProjectSystemEvent)
			if !ok {
				t.Errorf("Expected ProjectSystemHookEvent, but parsing produced %T", parsedEvent)
			}
			assert.Equal(t, tc.event, event.EventName)
		})
	}
}

func TestParseSystemhookGroup(t *testing.T) {
	tests := []struct {
		event   string
		payload []byte
	}{
		{"group_create", loadFixture(t, "testdata/systemhooks/group_create.json")},
		{"group_destroy", loadFixture(t, "testdata/systemhooks/group_destroy.json")},
		{"group_rename", loadFixture(t, "testdata/systemhooks/group_rename.json")},
	}
	for _, tc := range tests {
		t.Run(tc.event, func(t *testing.T) {
			parsedEvent, err := ParseSystemhook(tc.payload)
			if err != nil {
				t.Errorf("Error parsing build hook: %s", err)
			}
			event, ok := parsedEvent.(*GroupSystemEvent)
			if !ok {
				t.Errorf("Expected GroupSystemHookEvent, but parsing produced %T", parsedEvent)
			}
			assert.Equal(t, tc.event, event.EventName)
		})
	}
}

func TestParseSystemhookUser(t *testing.T) {
	tests := []struct {
		event   string
		payload []byte
	}{
		{"user_create", loadFixture(t, "testdata/systemhooks/user_create.json")},
		{"user_destroy", loadFixture(t, "testdata/systemhooks/user_destroy.json")},
		{"user_rename", loadFixture(t, "testdata/systemhooks/user_rename.json")},
		{"user_failed_login", loadFixture(t, "testdata/systemhooks/user_failed_login.json")},
	}
	for _, tc := range tests {
		t.Run(tc.event, func(t *testing.T) {
			parsedEvent, err := ParseSystemhook(tc.payload)
			if err != nil {
				t.Errorf("Error parsing build hook: %s", err)
			}
			event, ok := parsedEvent.(*UserSystemEvent)
			if !ok {
				t.Errorf("Expected UserSystemHookEvent, but parsing produced %T", parsedEvent)
			}
			assert.Equal(t, tc.event, event.EventName)
		})
	}
}

func TestParseSystemhookUserGroup(t *testing.T) {
	tests := []struct {
		event   string
		payload []byte
	}{
		{"user_add_to_group", loadFixture(t, "testdata/systemhooks/user_add_to_group.json")},
		{"user_remove_from_group", loadFixture(t, "testdata/systemhooks/user_remove_from_group.json")},
		{"user_update_for_group", loadFixture(t, "testdata/systemhooks/user_update_for_group.json")},
	}
	for _, tc := range tests {
		t.Run(tc.event, func(t *testing.T) {
			parsedEvent, err := ParseSystemhook(tc.payload)
			if err != nil {
				t.Errorf("Error parsing build hook: %s", err)
			}
			event, ok := parsedEvent.(*UserGroupSystemEvent)
			if !ok {
				t.Errorf("Expected UserGroupSystemHookEvent, but parsing produced %T", parsedEvent)
			}
			assert.Equal(t, tc.event, event.EventName)
		})
	}
}

func TestParseSystemhookUserTeam(t *testing.T) {
	tests := []struct {
		event   string
		payload []byte
	}{
		{"user_add_to_team", loadFixture(t, "testdata/systemhooks/user_add_to_team.json")},
		{"user_remove_from_team", loadFixture(t, "testdata/systemhooks/user_remove_from_team.json")},
		{"user_update_for_team", loadFixture(t, "testdata/systemhooks/user_update_for_team.json")},
	}
	for _, tc := range tests {
		t.Run(tc.event, func(t *testing.T) {
			parsedEvent, err := ParseSystemhook(tc.payload)
			if err != nil {
				t.Errorf("Error parsing build hook: %s", err)
			}
			event, ok := parsedEvent.(*UserTeamSystemEvent)
			if !ok {
				t.Errorf("Expected UserTeamSystemHookEvent, but parsing produced %T", parsedEvent)
			}
			assert.Equal(t, tc.event, event.EventName)
		})
	}
}

func TestParseHookSystemHook(t *testing.T) {
	parsedEvent1, err := ParseHook("System Hook", loadFixture(t, "testdata/systemhooks/merge_request.json"))
	if err != nil {
		t.Errorf("Error parsing build hook: %s", err)
	}
	parsedEvent2, err := ParseSystemhook(loadFixture(t, "testdata/systemhooks/merge_request.json"))
	if err != nil {
		t.Errorf("Error parsing build hook: %s", err)
	}
	assert.Equal(t, parsedEvent1, parsedEvent2)
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/event_parsing_webhook_test.go000066400000000000000000000437061475761473200270410ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"net/http"
	"reflect"
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestWebhookEventType(t *testing.T) {
	req, err := http.NewRequest(http.MethodGet, "https://gitlab.com", nil)
	if err != nil {
		t.Errorf("Error creating HTTP request: %s", err)
	}
	req.Header.Set("X-Gitlab-Event", "Push Hook")

	eventType := HookEventType(req)
	if eventType != "Push Hook" {
		t.Errorf("WebhookEventType is %s, want %s", eventType, "Push Hook")
	}
}

func TestWebhookEventToken(t *testing.T) {
	req, err := http.NewRequest(http.MethodGet, "https://gitlab.com", nil)
	if err != nil {
		t.Errorf("Error creating HTTP request: %s", err)
	}
	req.Header.Set("X-Gitlab-Token", "798d3dd3-67f5-41df-ad19-7882cc6263bf")

	actualToken := HookEventToken(req)
	if actualToken != "798d3dd3-67f5-41df-ad19-7882cc6263bf" {
		t.Errorf("WebhookEventToken is %q, want %q", actualToken, "798d3dd3-67f5-41df-ad19-7882cc6263bf")
	}
}

func TestParseBuildHook(t *testing.T) {
	raw := loadFixture(t, "testdata/webhooks/build.json")

	parsedEvent, err := ParseWebhook("Build Hook", raw)
	if err != nil {
		t.Errorf("Error parsing build hook: %s", err)
	}

	event, ok := parsedEvent.(*BuildEvent)
	if !ok {
		t.Errorf("Expected BuildEvent, but parsing produced %T", parsedEvent)
	}

	if event.ObjectKind != "build" {
		t.Errorf("ObjectKind is %v, want %v", event.ObjectKind, "build")
	}

	if event.BuildID != 1977 {
		t.Errorf("BuildID is %v, want %v", event.BuildID, 1977)
	}

	if event.BuildAllowFailure {
		t.Errorf("BuildAllowFailure is %v, want %v", event.BuildAllowFailure, false)
	}

	if event.Commit.SHA != "2293ada6b400935a1378653304eaf6221e0fdb8f" {
		t.Errorf("Commit SHA is %v, want %v", event.Commit.SHA, "2293ada6b400935a1378653304eaf6221e0fdb8f")
	}

	if event.BuildCreatedAt != "2021-02-23T02:41:37.886Z" {
		t.Errorf("BuildCreatedAt is %s, want %s", event.User.Name, expectedName)
	}
}

func TestParseCommitCommentHook(t *testing.T) {
	raw := loadFixture(t, "testdata/webhooks/note_commit.json")

	parsedEvent, err := ParseWebhook("Note Hook", raw)
	if err != nil {
		t.Errorf("Error parsing note hook: %s", err)
	}

	event, ok := parsedEvent.(*CommitCommentEvent)
	if !ok {
		t.Errorf("Expected CommitCommentEvent, but parsing produced %T", parsedEvent)
	}

	if event.ObjectKind != string(NoteEventTargetType) {
		t.Errorf("ObjectKind is %v, want %v", event.ObjectKind, NoteEventTargetType)
	}

	if event.ProjectID != 5 {
		t.Errorf("ProjectID is %v, want %v", event.ProjectID, 5)
	}

	if event.ObjectAttributes.NoteableType != "Commit" {
		t.Errorf("NoteableType is %v, want %v", event.ObjectAttributes.NoteableType, "Commit")
	}

	if event.Commit.ID != "cfe32cf61b73a0d5e9f13e774abde7ff789b1660" {
		t.Errorf("CommitID is %v, want %v", event.Commit.ID, "cfe32cf61b73a0d5e9f13e774abde7ff789b1660")
	}
}

func TestParseFeatureFlagHook(t *testing.T) {
	raw := loadFixture(t, "testdata/webhooks/feature_flag.json")

	parsedEvent, err := ParseWebhook("Feature Flag Hook", raw)
	if err != nil {
		t.Errorf("Error parsing feature flag hook: %s", err)
	}

	event, ok := parsedEvent.(*FeatureFlagEvent)
	if !ok {
		t.Errorf("Expected FeatureFlagEvent, but parsing produced %T", parsedEvent)
	}

	if event.ObjectKind != "feature_flag" {
		t.Errorf("ObjectKind is %s, want %s", event.ObjectKind, "feature_flag")
	}

	if event.Project.ID != 1 {
		t.Errorf("Project.ID is %v, want %v", event.Project.ID, 1)
	}

	if event.User.ID != 1 {
		t.Errorf("User ID is %d, want %d", event.User.ID, 1)
	}

	if event.User.Name != "Administrator" {
		t.Errorf("Username is %s, want %s", event.User.Name, "Administrator")
	}

	if event.ObjectAttributes.ID != 6 {
		t.Errorf("ObjectAttributes.ID is %d, want %d", event.ObjectAttributes.ID, 6)
	}

	if event.ObjectAttributes.Name != "test-feature-flag" {
		t.Errorf("ObjectAttributes.Name is %s, want %s", event.ObjectAttributes.Name, "test-feature-flag")
	}

	if event.ObjectAttributes.Description != "test-feature-flag-description" {
		t.Errorf("ObjectAttributes.Description is %s, want %s", event.ObjectAttributes.Description, "test-feature-flag-description")
	}

	if event.ObjectAttributes.Active != true {
		t.Errorf("ObjectAttributes.Active is %t, want %t", event.ObjectAttributes.Active, true)
	}
}

func TestParseGroupResourceAccessTokenHook(t *testing.T) {
	raw := loadFixture(t, "testdata/webhooks/resource_access_token_group.json")

	parsedEvent, err := ParseWebhook("Resource Access Token Hook", raw)
	if err != nil {
		t.Errorf("Error parsing group resource access token hook: %s", err)
	}

	event, ok := parsedEvent.(*GroupResourceAccessTokenEvent)
	if !ok {
		t.Errorf("Expected GroupResourceAccessTokenEvent, but parsing produced %T", parsedEvent)
	}

	expectedEventName := "expiring_access_token"

	if event.EventName != expectedEventName {
		t.Errorf("EventName is %v, want %v", event.EventName, expectedEventName)
	}
}

func TestParseHookWebHook(t *testing.T) {
	parsedEvent1, err := ParseHook("Merge Request Hook", loadFixture(t, "testdata/webhooks/merge_request.json"))
	if err != nil {
		t.Errorf("Error parsing build hook: %s", err)
	}
	parsedEvent2, err := ParseWebhook("Merge Request Hook", loadFixture(t, "testdata/webhooks/merge_request.json"))
	if err != nil {
		t.Errorf("Error parsing build hook: %s", err)
	}
	assert.Equal(t, parsedEvent1, parsedEvent2)
}

func TestParseIssueCommentHook(t *testing.T) {
	raw := loadFixture(t, "testdata/webhooks/note_issue.json")

	parsedEvent, err := ParseWebhook("Note Hook", raw)
	if err != nil {
		t.Errorf("Error parsing note hook: %s", err)
	}

	event, ok := parsedEvent.(*IssueCommentEvent)
	if !ok {
		t.Errorf("Expected IssueCommentEvent, but parsing produced %T", parsedEvent)
	}

	if event.ObjectKind != string(NoteEventTargetType) {
		t.Errorf("ObjectKind is %v, want %v", event.ObjectKind, NoteEventTargetType)
	}

	if event.ProjectID != 5 {
		t.Errorf("ProjectID is %v, want %v", event.ProjectID, 5)
	}

	if event.ObjectAttributes.NoteableType != "Issue" {
		t.Errorf("NoteableType is %v, want %v", event.ObjectAttributes.NoteableType, "Issue")
	}

	if event.Issue.Title != "test_issue" {
		t.Errorf("Issue title is %v, want %v", event.Issue.Title, "test_issue")
	}
	assert.Equal(t, 2, len(event.Issue.Labels))
}

func TestParseIssueHook(t *testing.T) {
	raw := loadFixture(t, "testdata/webhooks/issue.json")

	parsedEvent, err := ParseWebhook("Issue Hook", raw)
	if err != nil {
		t.Errorf("Error parsing issue hook: %s", err)
	}

	event, ok := parsedEvent.(*IssueEvent)
	if !ok {
		t.Errorf("Expected IssueEvent, but parsing produced %T", parsedEvent)
	}

	if event.ObjectKind != "issue" {
		t.Errorf("ObjectKind is %v, want %v", event.ObjectKind, "issue")
	}

	if event.Project.Name != "Gitlab Test" {
		t.Errorf("Project name is %v, want %v", event.Project.Name, "Gitlab Test")
	}

	if event.ObjectAttributes.State != "opened" {
		t.Errorf("Issue state is %v, want %v", event.ObjectAttributes.State, "opened")
	}

	if event.Assignee.Username != "user1" {
		t.Errorf("Assignee username is %v, want %v", event.Assignee.Username, "user1")
	}
	assert.Equal(t, 1, len(event.Labels))
	assert.Equal(t, 0, event.Changes.UpdatedByID.Previous)
	assert.Equal(t, 1, event.Changes.UpdatedByID.Current)
	assert.Equal(t, 1, len(event.Changes.Labels.Previous))
	assert.Equal(t, 1, len(event.Changes.Labels.Current))
	assert.Equal(t, "", event.Changes.Description.Previous)
	assert.Equal(t, "New description", event.Changes.Description.Current)
	assert.Equal(t, "", event.Changes.Title.Previous)
	assert.Equal(t, "New title", event.Changes.Title.Current)
}

func TestParseMergeRequestCommentHook(t *testing.T) {
	raw := loadFixture(t, "testdata/webhooks/note_merge_request.json")

	parsedEvent, err := ParseWebhook("Note Hook", raw)
	if err != nil {
		t.Errorf("Error parsing note hook: %s", err)
	}

	event, ok := parsedEvent.(*MergeCommentEvent)
	if !ok {
		t.Errorf("Expected MergeCommentEvent, but parsing produced %T", parsedEvent)
	}

	if event.ObjectKind != string(NoteEventTargetType) {
		t.Errorf("ObjectKind is %v, want %v", event.ObjectKind, "note")
	}

	if event.ProjectID != 5 {
		t.Errorf("ProjectID is %v, want %v", event.ProjectID, 5)
	}

	if event.ObjectAttributes.NoteableType != "MergeRequest" {
		t.Errorf("NoteableType is %v, want %v", event.ObjectAttributes.NoteableType, "MergeRequest")
	}

	if event.MergeRequest.ID != 7 {
		t.Errorf("MergeRequest ID is %v, want %v", event.MergeRequest.ID, 7)
	}

	expectedTitle := "Merge branch 'another-branch' into 'master'"
	if event.MergeRequest.LastCommit.Title != expectedTitle {
		t.Errorf("MergeRequest Title is %v, want %v", event.MergeRequest.Title, expectedTitle)
	}
}

func TestParseMemberHook(t *testing.T) {
	raw := loadFixture(t, "testdata/webhooks/member.json")

	parsedEvent, err := ParseWebhook("Member Hook", raw)
	if err != nil {
		t.Errorf("Error parsing member hook: %s", err)
	}

	event, ok := parsedEvent.(*MemberEvent)
	if !ok {
		t.Errorf("Expected MemberEvent, but parsing produced %T", parsedEvent)
	}

	if event.EventName != "user_add_to_group" {
		t.Errorf("EventName is %v, want %v", event.EventName, "user_add_to_group")
	}
}

func TestParseMergeRequestHook(t *testing.T) {
	raw := loadFixture(t, "testdata/webhooks/merge_request.json")

	parsedEvent, err := ParseWebhook("Merge Request Hook", raw)
	if err != nil {
		t.Errorf("Error parsing merge request hook: %s", err)
	}

	event, ok := parsedEvent.(*MergeEvent)
	if !ok {
		t.Errorf("Expected MergeEvent, but parsing produced %T", parsedEvent)
	}

	if event.ObjectKind != "merge_request" {
		t.Errorf("ObjectKind is %v, want %v", event.ObjectKind, "merge_request")
	}

	if event.ObjectAttributes.MergeStatus != "unchecked" {
		t.Errorf("MergeStatus is %v, want %v", event.ObjectAttributes.MergeStatus, "unchecked")
	}

	if event.ObjectAttributes.LastCommit.ID != "da1560886d4f094c3e6c9ef40349f7d38b5d27d7" {
		t.Errorf("LastCommit ID is %v, want %v", event.ObjectAttributes.LastCommit.ID, "da1560886d4f094c3e6c9ef40349f7d38b5d27d7")
	}

	if event.ObjectAttributes.WorkInProgress {
		t.Errorf("WorkInProgress is %v, want %v", event.ObjectAttributes.WorkInProgress, false)
	}
	assert.Equal(t, 1, len(event.Labels))
	assert.Equal(t, 0, event.Changes.UpdatedByID.Previous)
	assert.Equal(t, 1, event.Changes.UpdatedByID.Current)
	assert.Equal(t, 1, len(event.Changes.Labels.Previous))
	assert.Equal(t, 1, len(event.Changes.Labels.Current))
}

func TestParsePipelineHook(t *testing.T) {
	raw := loadFixture(t, "testdata/webhooks/pipeline.json")

	parsedEvent, err := ParseWebhook("Pipeline Hook", raw)
	if err != nil {
		t.Errorf("Error parsing pipeline hook: %s", err)
	}

	event, ok := parsedEvent.(*PipelineEvent)
	if !ok {
		t.Errorf("Expected PipelineEvent, but parsing produced %T", parsedEvent)
	}

	if event.ObjectKind != "pipeline" {
		t.Errorf("ObjectKind is %v, want %v", event.ObjectKind, "pipeline")
	}

	if event.ObjectAttributes.Duration != 63 {
		t.Errorf("Duration is %v, want %v", event.ObjectAttributes.Duration, 63)
	}

	if event.Commit.ID != "bcbb5ec396a2c0f828686f14fac9b80b780504f2" {
		t.Errorf("Commit ID is %v, want %v", event.Commit.ID, "bcbb5ec396a2c0f828686f14fac9b80b780504f2")
	}

	if event.Builds[0].ID != 380 {
		t.Errorf("Builds[0] ID is %v, want %v", event.Builds[0].ID, 380)
	}

	if event.Builds[0].Runner.RunnerType != "instance_type" {
		t.Errorf("Builds[0] Runner RunnerType is %v, want %v", event.Builds[0].Runner.RunnerType, "instance_type")
	}
}

func TestParseProjectResourceAccessTokenHook(t *testing.T) {
	raw := loadFixture(t, "testdata/webhooks/resource_access_token_project.json")

	parsedEvent, err := ParseWebhook("Resource Access Token Hook", raw)
	if err != nil {
		t.Errorf("Error parsing project resource access token hook: %s", err)
	}

	event, ok := parsedEvent.(*ProjectResourceAccessTokenEvent)
	if !ok {
		t.Errorf("Expected ProjectResourceAccessTokenEvent, but parsing produced %T", parsedEvent)
	}

	expectedEventName := "expiring_access_token"

	if event.EventName != expectedEventName {
		t.Errorf("EventName is %v, want %v", event.EventName, expectedEventName)
	}
}

func TestParsePushHook(t *testing.T) {
	raw := loadFixture(t, "testdata/webhooks/push.json")

	parsedEvent, err := ParseWebhook("Push Hook", raw)
	if err != nil {
		t.Errorf("Error parsing push hook: %s", err)
	}

	event, ok := parsedEvent.(*PushEvent)
	if !ok {
		t.Errorf("Expected PushEvent, but parsing produced %T", parsedEvent)
	}

	if event.ObjectKind != eventObjectKindPush {
		t.Errorf("ObjectKind is %v, want %v", event.ObjectKind, eventObjectKindPush)
	}

	if event.ProjectID != 15 {
		t.Errorf("ProjectID is %v, want %v", event.ProjectID, 15)
	}

	if event.UserName != exampleEventUserName {
		t.Errorf("Username is %s, want %s", event.UserName, exampleEventUserName)
	}

	if event.Commits[0] == nil || event.Commits[0].Timestamp == nil {
		t.Errorf("Commit Timestamp isn't nil")
	}

	if event.Commits[0] == nil || event.Commits[0].Author.Name != "Jordi Mallach" {
		t.Errorf("Commit Username is %s, want %s", event.UserName, "Jordi Mallach")
	}
}

func TestParseReleaseHook(t *testing.T) {
	raw := loadFixture(t, "testdata/webhooks/release.json")

	parsedEvent, err := ParseWebhook("Release Hook", raw)
	if err != nil {
		t.Errorf("Error parsing release hook: %s", err)
	}

	event, ok := parsedEvent.(*ReleaseEvent)
	if !ok {
		t.Errorf("Expected ReleaseEvent, but parsing produced %T", parsedEvent)
	}

	if event.ObjectKind != "release" {
		t.Errorf("ObjectKind is %v, want %v", event.ObjectKind, "release")
	}

	if event.Project.Name != "Project Name" {
		t.Errorf("Project name is %v, want %v", event.Project.Name, "Project Name")
	}
}

func TestParseServiceWebHook(t *testing.T) {
	parsedEvent, err := ParseWebhook("Service Hook", loadFixture(t, "testdata/webhooks/service_merge_request.json"))
	if err != nil {
		t.Errorf("Error parsing service hook merge request: %s", err)
	}

	switch event := parsedEvent.(type) {
	case *MergeEvent:
		assert.EqualValues(t, &EventUser{
			ID:        2,
			Name:      "the test",
			Username:  "test",
			Email:     "test@test.test",
			AvatarURL: "https://www.gravatar.com/avatar/dd46a756faad4727fb679320751f6dea?s=80&d=identicon",
		}, event.User)
		assert.EqualValues(t, "unchecked", event.ObjectAttributes.MergeStatus)
		assert.EqualValues(t, "next-feature", event.ObjectAttributes.SourceBranch)
		assert.EqualValues(t, "master", event.ObjectAttributes.TargetBranch)
	default:
		t.Errorf("unexpected event type: %s", reflect.TypeOf(parsedEvent))
	}
}

func TestParseSnippetCommentHook(t *testing.T) {
	raw := loadFixture(t, "testdata/webhooks/note_snippet.json")

	parsedEvent, err := ParseWebhook("Note Hook", raw)
	if err != nil {
		t.Errorf("Error parsing note hook: %s", err)
	}

	event, ok := parsedEvent.(*SnippetCommentEvent)
	if !ok {
		t.Errorf("Expected SnippetCommentEvent, but parsing produced %T", parsedEvent)
	}

	if event.ObjectKind != string(NoteEventTargetType) {
		t.Errorf("ObjectKind is %v, want %v", event.ObjectKind, NoteEventTargetType)
	}

	if event.ProjectID != 5 {
		t.Errorf("ProjectID is %v, want %v", event.ProjectID, 5)
	}

	if event.ObjectAttributes.NoteableType != "Snippet" {
		t.Errorf("NoteableType is %v, want %v", event.ObjectAttributes.NoteableType, "Snippet")
	}

	if event.Snippet.Title != "test" {
		t.Errorf("Snippet title is %v, want %v", event.Snippet.Title, "test")
	}
}

func TestParseSubGroupHook(t *testing.T) {
	raw := loadFixture(t, "testdata/webhooks/subgroup.json")

	parsedEvent, err := ParseWebhook("Subgroup Hook", raw)
	if err != nil {
		t.Errorf("Error parsing subgroup hook: %s", err)
	}

	event, ok := parsedEvent.(*SubGroupEvent)
	if !ok {
		t.Errorf("Expected SubGroupEvent, but parsing produced %T", parsedEvent)
	}

	if event.EventName != "subgroup_create" {
		t.Errorf("EventName is %v, want %v", event.EventName, "subgroup_create")
	}
}

func TestParseTagHook(t *testing.T) {
	raw := loadFixture(t, "testdata/webhooks/tag_push.json")

	parsedEvent, err := ParseWebhook("Tag Push Hook", raw)
	if err != nil {
		t.Errorf("Error parsing tag hook: %s", err)
	}

	event, ok := parsedEvent.(*TagEvent)
	if !ok {
		t.Errorf("Expected TagEvent, but parsing produced %T", parsedEvent)
	}

	if event.ObjectKind != eventObjectKindTagPush {
		t.Errorf("ObjectKind is %v, want %v", event.ObjectKind, eventObjectKindTagPush)
	}

	if event.ProjectID != 1 {
		t.Errorf("ProjectID is %v, want %v", event.ProjectID, 1)
	}

	if event.UserName != exampleEventUserName {
		t.Errorf("Name is %s, want %s", event.UserName, exampleEventUserName)
	}

	if event.UserUsername != exampleEventUserUsername {
		t.Errorf("Username is %s, want %s", event.UserUsername, exampleEventUserUsername)
	}

	if event.Ref != "refs/tags/v1.0.0" {
		t.Errorf("Ref is %s, want %s", event.Ref, "refs/tags/v1.0.0")
	}
}

func TestParseWikiPageHook(t *testing.T) {
	raw := loadFixture(t, "testdata/webhooks/wiki_page.json")

	parsedEvent, err := ParseWebhook("Wiki Page Hook", raw)
	if err != nil {
		t.Errorf("Error parsing wiki page hook: %s", err)
	}

	event, ok := parsedEvent.(*WikiPageEvent)
	if !ok {
		t.Errorf("Expected WikiPageEvent, but parsing produced %T", parsedEvent)
	}

	if event.ObjectKind != "wiki_page" {
		t.Errorf("ObjectKind is %v, want %v", event.ObjectKind, "wiki_page")
	}

	if event.Project.Name != "awesome-project" {
		t.Errorf("Project name is %v, want %v", event.Project.Name, "awesome-project")
	}

	if event.Wiki.WebURL != "http://example.com/root/awesome-project/wikis/home" {
		t.Errorf("Wiki web URL is %v, want %v", event.Wiki.WebURL, "http://example.com/root/awesome-project/wikis/home")
	}

	if event.ObjectAttributes.Message != "adding an awesome page to the wiki" {
		t.Errorf("Message is %v, want %v", event.ObjectAttributes.Message, "adding an awesome page to the wiki")
	}
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/event_systemhook_types.go000066400000000000000000000211031475761473200262350ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import "time"

// systemHookEvent is used to pre-process events to determine the
// system hook event type.
type systemHookEvent struct {
	BaseSystemEvent
	ObjectKind string `json:"object_kind"`
}

// BaseSystemEvent contains system hook's common properties.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/administration/system_hooks.html
type BaseSystemEvent struct {
	EventName string `json:"event_name"`
	CreatedAt string `json:"created_at"`
	UpdatedAt string `json:"updated_at"`
}

// ProjectSystemEvent represents a project system event.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/administration/system_hooks.html
type ProjectSystemEvent struct {
	BaseSystemEvent
	Name                 string `json:"name"`
	Path                 string `json:"path"`
	PathWithNamespace    string `json:"path_with_namespace"`
	ProjectID            int    `json:"project_id"`
	OwnerName            string `json:"owner_name"`
	OwnerEmail           string `json:"owner_email"`
	ProjectVisibility    string `json:"project_visibility"`
	OldPathWithNamespace string `json:"old_path_with_namespace,omitempty"`
}

// GroupSystemEvent represents a group system event.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/administration/system_hooks.html
type GroupSystemEvent struct {
	BaseSystemEvent
	Name                 string `json:"name"`
	Path                 string `json:"path"`
	PathWithNamespace    string `json:"full_path"`
	GroupID              int    `json:"group_id"`
	OwnerName            string `json:"owner_name"`
	OwnerEmail           string `json:"owner_email"`
	ProjectVisibility    string `json:"project_visibility"`
	OldPath              string `json:"old_path,omitempty"`
	OldPathWithNamespace string `json:"old_full_path,omitempty"`
}

// KeySystemEvent represents a key system event.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/administration/system_hooks.html
type KeySystemEvent struct {
	BaseSystemEvent
	ID       int    `json:"id"`
	Username string `json:"username"`
	Key      string `json:"key"`
}

// UserSystemEvent represents a user system event.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/administration/system_hooks.html
type UserSystemEvent struct {
	BaseSystemEvent
	ID          int    `json:"user_id"`
	Name        string `json:"name"`
	Username    string `json:"username"`
	OldUsername string `json:"old_username,omitempty"`
	Email       string `json:"email"`
	State       string `json:"state,omitempty"`
}

// UserGroupSystemEvent represents a user group system event.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/administration/system_hooks.html
type UserGroupSystemEvent struct {
	BaseSystemEvent
	ID          int    `json:"user_id"`
	Name        string `json:"user_name"`
	Username    string `json:"user_username"`
	Email       string `json:"user_email"`
	GroupID     int    `json:"group_id"`
	GroupName   string `json:"group_name"`
	GroupPath   string `json:"group_path"`
	GroupAccess string `json:"group_access"`
}

// UserTeamSystemEvent represents a user team system event.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/administration/system_hooks.html
type UserTeamSystemEvent struct {
	BaseSystemEvent
	ID                       int    `json:"user_id"`
	Name                     string `json:"user_name"`
	Username                 string `json:"user_username"`
	Email                    string `json:"user_email"`
	ProjectID                int    `json:"project_id"`
	ProjectName              string `json:"project_name"`
	ProjectPath              string `json:"project_path"`
	ProjectPathWithNamespace string `json:"project_path_with_namespace"`
	ProjectVisibility        string `json:"project_visibility"`
	AccessLevel              string `json:"access_level"`
}

// PushSystemEvent represents a push system event.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/administration/system_hooks.html#push-events
type PushSystemEvent struct {
	BaseSystemEvent
	Before       string `json:"before"`
	After        string `json:"after"`
	Ref          string `json:"ref"`
	CheckoutSHA  string `json:"checkout_sha"`
	UserID       int    `json:"user_id"`
	UserName     string `json:"user_name"`
	UserUsername string `json:"user_username"`
	UserEmail    string `json:"user_email"`
	UserAvatar   string `json:"user_avatar"`
	ProjectID    int    `json:"project_id"`
	Project      struct {
		Name              string `json:"name"`
		Description       string `json:"description"`
		WebURL            string `json:"web_url"`
		AvatarURL         string `json:"avatar_url"`
		GitHTTPURL        string `json:"git_http_url"`
		GitSSHURL         string `json:"git_ssh_url"`
		Namespace         string `json:"namespace"`
		VisibilityLevel   int    `json:"visibility_level"`
		PathWithNamespace string `json:"path_with_namespace"`
		DefaultBranch     string `json:"default_branch"`
		Homepage          string `json:"homepage"`
		URL               string `json:"url"`
	} `json:"project"`
	Commits []struct {
		ID        string    `json:"id"`
		Message   string    `json:"message"`
		Timestamp time.Time `json:"timestamp"`
		URL       string    `json:"url"`
		Author    struct {
			Name  string `json:"name"`
			Email string `json:"email"`
		} `json:"author"`
	} `json:"commits"`
	TotalCommitsCount int `json:"total_commits_count"`
}

// TagPushSystemEvent represents a tag push system event.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/administration/system_hooks.html#tag-events
type TagPushSystemEvent struct {
	BaseSystemEvent
	Before       string `json:"before"`
	After        string `json:"after"`
	Ref          string `json:"ref"`
	CheckoutSHA  string `json:"checkout_sha"`
	UserID       int    `json:"user_id"`
	UserName     string `json:"user_name"`
	UserUsername string `json:"user_username"`
	UserEmail    string `json:"user_email"`
	UserAvatar   string `json:"user_avatar"`
	ProjectID    int    `json:"project_id"`
	Project      struct {
		Name              string `json:"name"`
		Description       string `json:"description"`
		WebURL            string `json:"web_url"`
		AvatarURL         string `json:"avatar_url"`
		GitHTTPURL        string `json:"git_http_url"`
		GitSSHURL         string `json:"git_ssh_url"`
		Namespace         string `json:"namespace"`
		VisibilityLevel   int    `json:"visibility_level"`
		PathWithNamespace string `json:"path_with_namespace"`
		DefaultBranch     string `json:"default_branch"`
		Homepage          string `json:"homepage"`
		URL               string `json:"url"`
	} `json:"project"`
	Commits []struct {
		ID        string    `json:"id"`
		Message   string    `json:"message"`
		Timestamp time.Time `json:"timestamp"`
		URL       string    `json:"url"`
		Author    struct {
			Name  string `json:"name"`
			Email string `json:"email"`
		} `json:"author"`
	} `json:"commits"`
	TotalCommitsCount int `json:"total_commits_count"`
}

// RepositoryUpdateSystemEvent represents a repository updated system event.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/administration/system_hooks.html#repository-update-events
type RepositoryUpdateSystemEvent struct {
	BaseSystemEvent
	UserID     int    `json:"user_id"`
	UserName   string `json:"user_name"`
	UserEmail  string `json:"user_email"`
	UserAvatar string `json:"user_avatar"`
	ProjectID  int    `json:"project_id"`
	Project    struct {
		ID                int    `json:"id"`
		Name              string `json:"name"`
		Description       string `json:"description"`
		WebURL            string `json:"web_url"`
		AvatarURL         string `json:"avatar_url"`
		GitHTTPURL        string `json:"git_http_url"`
		GitSSHURL         string `json:"git_ssh_url"`
		Namespace         string `json:"namespace"`
		VisibilityLevel   int    `json:"visibility_level"`
		PathWithNamespace string `json:"path_with_namespace"`
		DefaultBranch     string `json:"default_branch"`
		CiConfigPath      string `json:"ci_config_path"`
		Homepage          string `json:"homepage"`
		URL               string `json:"url"`
	} `json:"project"`
	Changes []struct {
		Before string `json:"before"`
		After  string `json:"after"`
		Ref    string `json:"ref"`
	} `json:"changes"`
	Refs []string `json:"refs"`
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/event_webhook_types.go000066400000000000000000001466471475761473200255130ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"encoding/json"
	"fmt"
	"strconv"
	"time"
)

// StateID identifies the state of an issue or merge request.
//
// There are no GitLab API docs on the subject, but the mappings can be found in
// GitLab's codebase:
// https://gitlab.com/gitlab-org/gitlab-foss/-/blob/ba5be4989e/app/models/concerns/issuable.rb#L39-42
type StateID int

const (
	StateIDNone   StateID = 0
	StateIDOpen   StateID = 1
	StateIDClosed StateID = 2
	StateIDMerged StateID = 3
	StateIDLocked StateID = 4
)

// BuildEvent represents a build event.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/user/project/integrations/webhook_events.html#job-events
type BuildEvent struct {
	ObjectKind        string     `json:"object_kind"`
	Ref               string     `json:"ref"`
	Tag               bool       `json:"tag"`
	BeforeSHA         string     `json:"before_sha"`
	SHA               string     `json:"sha"`
	BuildID           int        `json:"build_id"`
	BuildName         string     `json:"build_name"`
	BuildStage        string     `json:"build_stage"`
	BuildStatus       string     `json:"build_status"`
	BuildCreatedAt    string     `json:"build_created_at"`
	BuildStartedAt    string     `json:"build_started_at"`
	BuildFinishedAt   string     `json:"build_finished_at"`
	BuildDuration     float64    `json:"build_duration"`
	BuildAllowFailure bool       `json:"build_allow_failure"`
	ProjectID         int        `json:"project_id"`
	ProjectName       string     `json:"project_name"`
	User              *EventUser `json:"user"`
	Commit            struct {
		ID          int    `json:"id"`
		SHA         string `json:"sha"`
		Message     string `json:"message"`
		AuthorName  string `json:"author_name"`
		AuthorEmail string `json:"author_email"`
		Status      string `json:"status"`
		Duration    int    `json:"duration"`
		StartedAt   string `json:"started_at"`
		FinishedAt  string `json:"finished_at"`
	} `json:"commit"`
	Repository *Repository `json:"repository"`
}

// CommitCommentEvent represents a comment on a commit event.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/user/project/integrations/webhook_events.html#comment-on-a-commit
type CommitCommentEvent struct {
	ObjectKind string `json:"object_kind"`
	EventType  string `json:"event_type"`
	User       *User  `json:"user"`
	ProjectID  int    `json:"project_id"`
	Project    struct {
		ID                int             `json:"id"`
		Name              string          `json:"name"`
		Description       string          `json:"description"`
		AvatarURL         string          `json:"avatar_url"`
		GitSSHURL         string          `json:"git_ssh_url"`
		GitHTTPURL        string          `json:"git_http_url"`
		Namespace         string          `json:"namespace"`
		PathWithNamespace string          `json:"path_with_namespace"`
		DefaultBranch     string          `json:"default_branch"`
		Homepage          string          `json:"homepage"`
		URL               string          `json:"url"`
		SSHURL            string          `json:"ssh_url"`
		HTTPURL           string          `json:"http_url"`
		WebURL            string          `json:"web_url"`
		Visibility        VisibilityValue `json:"visibility"`
	} `json:"project"`
	Repository       *Repository `json:"repository"`
	ObjectAttributes struct {
		ID           int                `json:"id"`
		Note         string             `json:"note"`
		NoteableType string             `json:"noteable_type"`
		AuthorID     int                `json:"author_id"`
		CreatedAt    string             `json:"created_at"`
		UpdatedAt    string             `json:"updated_at"`
		ProjectID    int                `json:"project_id"`
		Attachment   string             `json:"attachment"`
		LineCode     string             `json:"line_code"`
		CommitID     string             `json:"commit_id"`
		NoteableID   int                `json:"noteable_id"`
		System       bool               `json:"system"`
		StDiff       *Diff              `json:"st_diff"`
		Description  string             `json:"description"`
		Action       CommentEventAction `json:"action"`
		URL          string             `json:"url"`
	} `json:"object_attributes"`
	Commit *struct {
		ID        string     `json:"id"`
		Title     string     `json:"title"`
		Message   string     `json:"message"`
		Timestamp *time.Time `json:"timestamp"`
		URL       string     `json:"url"`
		Author    struct {
			Name  string `json:"name"`
			Email string `json:"email"`
		} `json:"author"`
	} `json:"commit"`
}

// DeploymentEvent represents a deployment event.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/user/project/integrations/webhook_events.html#deployment-events
type DeploymentEvent struct {
	ObjectKind             string `json:"object_kind"`
	Status                 string `json:"status"`
	StatusChangedAt        string `json:"status_changed_at"`
	DeploymentID           int    `json:"deployment_id"`
	DeployableID           int    `json:"deployable_id"`
	DeployableURL          string `json:"deployable_url"`
	Environment            string `json:"environment"`
	EnvironmentSlug        string `json:"environment_slug"`
	EnvironmentExternalURL string `json:"environment_external_url"`
	Project                struct {
		ID                int     `json:"id"`
		Name              string  `json:"name"`
		Description       string  `json:"description"`
		WebURL            string  `json:"web_url"`
		AvatarURL         *string `json:"avatar_url"`
		GitSSHURL         string  `json:"git_ssh_url"`
		GitHTTPURL        string  `json:"git_http_url"`
		Namespace         string  `json:"namespace"`
		VisibilityLevel   int     `json:"visibility_level"`
		PathWithNamespace string  `json:"path_with_namespace"`
		DefaultBranch     string  `json:"default_branch"`
		CIConfigPath      string  `json:"ci_config_path"`
		Homepage          string  `json:"homepage"`
		URL               string  `json:"url"`
		SSHURL            string  `json:"ssh_url"`
		HTTPURL           string  `json:"http_url"`
	} `json:"project"`
	Ref         string     `json:"ref"`
	ShortSHA    string     `json:"short_sha"`
	User        *EventUser `json:"user"`
	UserURL     string     `json:"user_url"`
	CommitURL   string     `json:"commit_url"`
	CommitTitle string     `json:"commit_title"`
}

// FeatureFlagEvent represents a feature flag event.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/user/project/integrations/webhook_events.html#feature-flag-events
type FeatureFlagEvent struct {
	ObjectKind string `json:"object_kind"`
	Project    struct {
		ID                int     `json:"id"`
		Name              string  `json:"name"`
		Description       string  `json:"description"`
		WebURL            string  `json:"web_url"`
		AvatarURL         *string `json:"avatar_url"`
		GitSSHURL         string  `json:"git_ssh_url"`
		GitHTTPURL        string  `json:"git_http_url"`
		Namespace         string  `json:"namespace"`
		VisibilityLevel   int     `json:"visibility_level"`
		PathWithNamespace string  `json:"path_with_namespace"`
		DefaultBranch     string  `json:"default_branch"`
		CIConfigPath      string  `json:"ci_config_path"`
		Homepage          string  `json:"homepage"`
		URL               string  `json:"url"`
		SSHURL            string  `json:"ssh_url"`
		HTTPURL           string  `json:"http_url"`
	} `json:"project"`
	User             *EventUser `json:"user"`
	UserURL          string     `json:"user_url"`
	ObjectAttributes struct {
		ID          int    `json:"id"`
		Name        string `json:"name"`
		Description string `json:"description"`
		Active      bool   `json:"active"`
	} `json:"object_attributes"`
}

// GroupResourceAccessTokenEvent represents a resource access token event for a
// group.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/user/project/integrations/webhook_events.html#project-and-group-access-token-events
type GroupResourceAccessTokenEvent struct {
	EventName  string `json:"event_name"`
	ObjectKind string `json:"object_kind"`
	Group      struct {
		GroupID   int    `json:"group_id"`
		GroupName string `json:"group_name"`
		GroupPath string `json:"group_path"`
	} `json:"group"`
	ObjectAttributes struct {
		ID        int      `json:"id"`
		UserID    int      `json:"user_id"`
		Name      string   `json:"name"`
		CreatedAt string   `json:"created_at"`
		ExpiresAt *ISOTime `json:"expires_at"`
	} `json:"object_attributes"`
}

// IssueCommentEvent represents a comment on an issue event.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/user/project/integrations/webhook_events.html#comment-on-an-issue
type IssueCommentEvent struct {
	ObjectKind string `json:"object_kind"`
	EventType  string `json:"event_type"`
	User       *User  `json:"user"`
	ProjectID  int    `json:"project_id"`
	Project    struct {
		Name              string          `json:"name"`
		Description       string          `json:"description"`
		AvatarURL         string          `json:"avatar_url"`
		GitSSHURL         string          `json:"git_ssh_url"`
		GitHTTPURL        string          `json:"git_http_url"`
		Namespace         string          `json:"namespace"`
		PathWithNamespace string          `json:"path_with_namespace"`
		DefaultBranch     string          `json:"default_branch"`
		Homepage          string          `json:"homepage"`
		URL               string          `json:"url"`
		SSHURL            string          `json:"ssh_url"`
		HTTPURL           string          `json:"http_url"`
		WebURL            string          `json:"web_url"`
		Visibility        VisibilityValue `json:"visibility"`
	} `json:"project"`
	Repository       *Repository `json:"repository"`
	ObjectAttributes struct {
		ID           int                `json:"id"`
		Note         string             `json:"note"`
		NoteableType string             `json:"noteable_type"`
		AuthorID     int                `json:"author_id"`
		CreatedAt    string             `json:"created_at"`
		UpdatedAt    string             `json:"updated_at"`
		ProjectID    int                `json:"project_id"`
		Attachment   string             `json:"attachment"`
		LineCode     string             `json:"line_code"`
		CommitID     string             `json:"commit_id"`
		DiscussionID string             `json:"discussion_id"`
		NoteableID   int                `json:"noteable_id"`
		System       bool               `json:"system"`
		StDiff       []*Diff            `json:"st_diff"`
		Description  string             `json:"description"`
		Action       CommentEventAction `json:"action"`
		URL          string             `json:"url"`
	} `json:"object_attributes"`
	Issue struct {
		ID                  int           `json:"id"`
		IID                 int           `json:"iid"`
		ProjectID           int           `json:"project_id"`
		MilestoneID         int           `json:"milestone_id"`
		AuthorID            int           `json:"author_id"`
		Position            int           `json:"position"`
		BranchName          string        `json:"branch_name"`
		Description         string        `json:"description"`
		State               string        `json:"state"`
		Title               string        `json:"title"`
		Labels              []*EventLabel `json:"labels"`
		LastEditedAt        string        `json:"last_edit_at"`
		LastEditedByID      int           `json:"last_edited_by_id"`
		UpdatedAt           string        `json:"updated_at"`
		UpdatedByID         int           `json:"updated_by_id"`
		CreatedAt           string        `json:"created_at"`
		ClosedAt            string        `json:"closed_at"`
		DueDate             *ISOTime      `json:"due_date"`
		URL                 string        `json:"url"`
		TimeEstimate        int           `json:"time_estimate"`
		Confidential        bool          `json:"confidential"`
		TotalTimeSpent      int           `json:"total_time_spent"`
		HumanTotalTimeSpent string        `json:"human_total_time_spent"`
		HumanTimeEstimate   string        `json:"human_time_estimate"`
		AssigneeIDs         []int         `json:"assignee_ids"`
		AssigneeID          int           `json:"assignee_id"`
	} `json:"issue"`
}

// IssueEvent represents a issue event.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/user/project/integrations/webhook_events.html#issue-events
type IssueEvent struct {
	ObjectKind string     `json:"object_kind"`
	EventType  string     `json:"event_type"`
	User       *EventUser `json:"user"`
	Project    struct {
		ID                int             `json:"id"`
		Name              string          `json:"name"`
		Description       string          `json:"description"`
		AvatarURL         string          `json:"avatar_url"`
		GitSSHURL         string          `json:"git_ssh_url"`
		GitHTTPURL        string          `json:"git_http_url"`
		Namespace         string          `json:"namespace"`
		PathWithNamespace string          `json:"path_with_namespace"`
		DefaultBranch     string          `json:"default_branch"`
		Homepage          string          `json:"homepage"`
		URL               string          `json:"url"`
		SSHURL            string          `json:"ssh_url"`
		HTTPURL           string          `json:"http_url"`
		WebURL            string          `json:"web_url"`
		Visibility        VisibilityValue `json:"visibility"`
	} `json:"project"`
	Repository       *Repository `json:"repository"`
	ObjectAttributes struct {
		ID                  int      `json:"id"`
		Title               string   `json:"title"`
		AssigneeIDs         []int    `json:"assignee_ids"`
		AssigneeID          int      `json:"assignee_id"`
		AuthorID            int      `json:"author_id"`
		ProjectID           int      `json:"project_id"`
		CreatedAt           string   `json:"created_at"` // Should be *time.Time (see Gitlab issue #21468)
		UpdatedAt           string   `json:"updated_at"` // Should be *time.Time (see Gitlab issue #21468)
		UpdatedByID         int      `json:"updated_by_id"`
		LastEditedAt        string   `json:"last_edited_at"`
		LastEditedByID      int      `json:"last_edited_by_id"`
		RelativePosition    int      `json:"relative_position"`
		BranchName          string   `json:"branch_name"`
		Description         string   `json:"description"`
		MilestoneID         int      `json:"milestone_id"`
		StateID             StateID  `json:"state_id"`
		Confidential        bool     `json:"confidential"`
		DiscussionLocked    bool     `json:"discussion_locked"`
		DueDate             *ISOTime `json:"due_date"`
		MovedToID           int      `json:"moved_to_id"`
		DuplicatedToID      int      `json:"duplicated_to_id"`
		TimeEstimate        int      `json:"time_estimate"`
		TotalTimeSpent      int      `json:"total_time_spent"`
		TimeChange          int      `json:"time_change"`
		HumanTotalTimeSpent string   `json:"human_total_time_spent"`
		HumanTimeEstimate   string   `json:"human_time_estimate"`
		HumanTimeChange     string   `json:"human_time_change"`
		Weight              int      `json:"weight"`
		IID                 int      `json:"iid"`
		URL                 string   `json:"url"`
		State               string   `json:"state"`
		Action              string   `json:"action"`
		Severity            string   `json:"severity"`
		EscalationStatus    string   `json:"escalation_status"`
		EscalationPolicy    struct {
			ID   int    `json:"id"`
			Name string `json:"name"`
		} `json:"escalation_policy"`
		Labels []*EventLabel `json:"labels"`
	} `json:"object_attributes"`
	Assignee  *EventUser    `json:"assignee"`
	Assignees *[]EventUser  `json:"assignees"`
	Labels    []*EventLabel `json:"labels"`
	Changes   struct {
		Assignees struct {
			Previous []*EventUser `json:"previous"`
			Current  []*EventUser `json:"current"`
		} `json:"assignees"`
		Description struct {
			Previous string `json:"previous"`
			Current  string `json:"current"`
		} `json:"description"`
		Labels struct {
			Previous []*EventLabel `json:"previous"`
			Current  []*EventLabel `json:"current"`
		} `json:"labels"`
		Title struct {
			Previous string `json:"previous"`
			Current  string `json:"current"`
		} `json:"title"`
		ClosedAt struct {
			Previous string `json:"previous"`
			Current  string `json:"current"`
		} `json:"closed_at"`
		StateID struct {
			Previous StateID `json:"previous"`
			Current  StateID `json:"current"`
		} `json:"state_id"`
		UpdatedAt struct {
			Previous string `json:"previous"`
			Current  string `json:"current"`
		} `json:"updated_at"`
		UpdatedByID struct {
			Previous int `json:"previous"`
			Current  int `json:"current"`
		} `json:"updated_by_id"`
		TotalTimeSpent struct {
			Previous int `json:"previous"`
			Current  int `json:"current"`
		} `json:"total_time_spent"`
	} `json:"changes"`
}

// JobEvent represents a job event.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/user/project/integrations/webhook_events.html#job-events
type JobEvent struct {
	ObjectKind          string     `json:"object_kind"`
	Ref                 string     `json:"ref"`
	Tag                 bool       `json:"tag"`
	BeforeSHA           string     `json:"before_sha"`
	SHA                 string     `json:"sha"`
	BuildID             int        `json:"build_id"`
	BuildName           string     `json:"build_name"`
	BuildStage          string     `json:"build_stage"`
	BuildStatus         string     `json:"build_status"`
	BuildCreatedAt      string     `json:"build_created_at"`
	BuildStartedAt      string     `json:"build_started_at"`
	BuildFinishedAt     string     `json:"build_finished_at"`
	BuildDuration       float64    `json:"build_duration"`
	BuildQueuedDuration float64    `json:"build_queued_duration"`
	BuildAllowFailure   bool       `json:"build_allow_failure"`
	BuildFailureReason  string     `json:"build_failure_reason"`
	RetriesCount        int        `json:"retries_count"`
	PipelineID          int        `json:"pipeline_id"`
	ProjectID           int        `json:"project_id"`
	ProjectName         string     `json:"project_name"`
	User                *EventUser `json:"user"`
	Commit              struct {
		ID          int    `json:"id"`
		Name        string `json:"name"`
		SHA         string `json:"sha"`
		Message     string `json:"message"`
		AuthorName  string `json:"author_name"`
		AuthorEmail string `json:"author_email"`
		AuthorURL   string `json:"author_url"`
		Status      string `json:"status"`
		Duration    int    `json:"duration"`
		StartedAt   string `json:"started_at"`
		FinishedAt  string `json:"finished_at"`
	} `json:"commit"`
	Repository *Repository `json:"repository"`
	Runner     struct {
		ID          int      `json:"id"`
		Active      bool     `json:"active"`
		RunnerType  string   `json:"runner_type"`
		IsShared    bool     `json:"is_shared"`
		Description string   `json:"description"`
		Tags        []string `json:"tags"`
	} `json:"runner"`
	Environment struct {
		Name           string `json:"name"`
		Action         string `json:"action"`
		DeploymentTier string `json:"deployment_tier"`
	} `json:"environment"`
}

// MemberEvent represents a member event.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/user/project/integrations/webhook_events.html#group-member-events
type MemberEvent struct {
	CreatedAt    *time.Time `json:"created_at"`
	UpdatedAt    *time.Time `json:"updated_at"`
	GroupName    string     `json:"group_name"`
	GroupPath    string     `json:"group_path"`
	GroupID      int        `json:"group_id"`
	UserUsername string     `json:"user_username"`
	UserName     string     `json:"user_name"`
	UserEmail    string     `json:"user_email"`
	UserID       int        `json:"user_id"`
	GroupAccess  string     `json:"group_access"`
	GroupPlan    string     `json:"group_plan"`
	ExpiresAt    *time.Time `json:"expires_at"`
	EventName    string     `json:"event_name"`
}

// MergeCommentEvent represents a comment on a merge event.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/user/project/integrations/webhook_events.html#comment-on-a-merge-request
type MergeCommentEvent struct {
	ObjectKind string     `json:"object_kind"`
	EventType  string     `json:"event_type"`
	User       *EventUser `json:"user"`
	ProjectID  int        `json:"project_id"`
	Project    struct {
		ID                int             `json:"id"`
		Name              string          `json:"name"`
		Description       string          `json:"description"`
		AvatarURL         string          `json:"avatar_url"`
		GitSSHURL         string          `json:"git_ssh_url"`
		GitHTTPURL        string          `json:"git_http_url"`
		Namespace         string          `json:"namespace"`
		PathWithNamespace string          `json:"path_with_namespace"`
		DefaultBranch     string          `json:"default_branch"`
		Homepage          string          `json:"homepage"`
		URL               string          `json:"url"`
		SSHURL            string          `json:"ssh_url"`
		HTTPURL           string          `json:"http_url"`
		WebURL            string          `json:"web_url"`
		Visibility        VisibilityValue `json:"visibility"`
	} `json:"project"`
	ObjectAttributes struct {
		Attachment       string             `json:"attachment"`
		AuthorID         int                `json:"author_id"`
		ChangePosition   *NotePosition      `json:"change_position"`
		CommitID         string             `json:"commit_id"`
		CreatedAt        string             `json:"created_at"`
		DiscussionID     string             `json:"discussion_id"`
		ID               int                `json:"id"`
		LineCode         string             `json:"line_code"`
		Note             string             `json:"note"`
		NoteableID       int                `json:"noteable_id"`
		NoteableType     string             `json:"noteable_type"`
		OriginalPosition *NotePosition      `json:"original_position"`
		Position         *NotePosition      `json:"position"`
		ProjectID        int                `json:"project_id"`
		ResolvedAt       string             `json:"resolved_at"`
		ResolvedByID     int                `json:"resolved_by_id"`
		ResolvedByPush   bool               `json:"resolved_by_push"`
		StDiff           *Diff              `json:"st_diff"`
		System           bool               `json:"system"`
		Type             string             `json:"type"`
		UpdatedAt        string             `json:"updated_at"`
		UpdatedByID      int                `json:"updated_by_id"`
		Description      string             `json:"description"`
		Action           CommentEventAction `json:"action"`
		URL              string             `json:"url"`
	} `json:"object_attributes"`
	Repository   *Repository `json:"repository"`
	MergeRequest struct {
		ID                        int           `json:"id"`
		TargetBranch              string        `json:"target_branch"`
		SourceBranch              string        `json:"source_branch"`
		SourceProjectID           int           `json:"source_project_id"`
		AuthorID                  int           `json:"author_id"`
		AssigneeID                int           `json:"assignee_id"`
		AssigneeIDs               []int         `json:"assignee_ids"`
		Title                     string        `json:"title"`
		CreatedAt                 string        `json:"created_at"`
		UpdatedAt                 string        `json:"updated_at"`
		MilestoneID               int           `json:"milestone_id"`
		State                     string        `json:"state"`
		MergeStatus               string        `json:"merge_status"`
		TargetProjectID           int           `json:"target_project_id"`
		IID                       int           `json:"iid"`
		Description               string        `json:"description"`
		Position                  int           `json:"position"`
		Labels                    []*EventLabel `json:"labels"`
		LockedAt                  string        `json:"locked_at"`
		UpdatedByID               int           `json:"updated_by_id"`
		MergeError                string        `json:"merge_error"`
		MergeParams               *MergeParams  `json:"merge_params"`
		MergeWhenPipelineSucceeds bool          `json:"merge_when_pipeline_succeeds"`
		MergeUserID               int           `json:"merge_user_id"`
		MergeCommitSHA            string        `json:"merge_commit_sha"`
		DeletedAt                 string        `json:"deleted_at"`
		InProgressMergeCommitSHA  string        `json:"in_progress_merge_commit_sha"`
		LockVersion               int           `json:"lock_version"`
		ApprovalsBeforeMerge      string        `json:"approvals_before_merge"`
		RebaseCommitSHA           string        `json:"rebase_commit_sha"`
		TimeEstimate              int           `json:"time_estimate"`
		Squash                    bool          `json:"squash"`
		LastEditedAt              string        `json:"last_edited_at"`
		LastEditedByID            int           `json:"last_edited_by_id"`
		Source                    *Repository   `json:"source"`
		Target                    *Repository   `json:"target"`
		LastCommit                struct {
			ID        string     `json:"id"`
			Title     string     `json:"title"`
			Message   string     `json:"message"`
			Timestamp *time.Time `json:"timestamp"`
			URL       string     `json:"url"`
			Author    struct {
				Name  string `json:"name"`
				Email string `json:"email"`
			} `json:"author"`
		} `json:"last_commit"`
		WorkInProgress      bool       `json:"work_in_progress"`
		TotalTimeSpent      int        `json:"total_time_spent"`
		HeadPipelineID      int        `json:"head_pipeline_id"`
		Assignee            *EventUser `json:"assignee"`
		DetailedMergeStatus string     `json:"detailed_merge_status"`
	} `json:"merge_request"`
}

// MergeEvent represents a merge event.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/user/project/integrations/webhook_events.html#merge-request-events
type MergeEvent struct {
	ObjectKind string     `json:"object_kind"`
	EventType  string     `json:"event_type"`
	User       *EventUser `json:"user"`
	Project    struct {
		ID                int             `json:"id"`
		Name              string          `json:"name"`
		Description       string          `json:"description"`
		AvatarURL         string          `json:"avatar_url"`
		GitSSHURL         string          `json:"git_ssh_url"`
		GitHTTPURL        string          `json:"git_http_url"`
		Namespace         string          `json:"namespace"`
		PathWithNamespace string          `json:"path_with_namespace"`
		DefaultBranch     string          `json:"default_branch"`
		CIConfigPath      string          `json:"ci_config_path"`
		Homepage          string          `json:"homepage"`
		URL               string          `json:"url"`
		SSHURL            string          `json:"ssh_url"`
		HTTPURL           string          `json:"http_url"`
		WebURL            string          `json:"web_url"`
		Visibility        VisibilityValue `json:"visibility"`
	} `json:"project"`
	ObjectAttributes struct {
		ID                       int          `json:"id"`
		TargetBranch             string       `json:"target_branch"`
		SourceBranch             string       `json:"source_branch"`
		SourceProjectID          int          `json:"source_project_id"`
		AuthorID                 int          `json:"author_id"`
		AssigneeID               int          `json:"assignee_id"`
		AssigneeIDs              []int        `json:"assignee_ids"`
		ReviewerIDs              []int        `json:"reviewer_ids"`
		Title                    string       `json:"title"`
		CreatedAt                string       `json:"created_at"` // Should be *time.Time (see Gitlab issue #21468)
		UpdatedAt                string       `json:"updated_at"` // Should be *time.Time (see Gitlab issue #21468)
		StCommits                []*Commit    `json:"st_commits"`
		StDiffs                  []*Diff      `json:"st_diffs"`
		LastEditedAt             string       `json:"last_edited_at"`
		LastEditedByID           int          `json:"last_edited_by_id"`
		MilestoneID              int          `json:"milestone_id"`
		StateID                  StateID      `json:"state_id"`
		State                    string       `json:"state"`
		MergeStatus              string       `json:"merge_status"`
		TargetProjectID          int          `json:"target_project_id"`
		IID                      int          `json:"iid"`
		Description              string       `json:"description"`
		Position                 int          `json:"position"`
		LockedAt                 string       `json:"locked_at"`
		UpdatedByID              int          `json:"updated_by_id"`
		MergeError               string       `json:"merge_error"`
		MergeParams              *MergeParams `json:"merge_params"`
		MergeWhenBuildSucceeds   bool         `json:"merge_when_build_succeeds"`
		MergeUserID              int          `json:"merge_user_id"`
		MergeCommitSHA           string       `json:"merge_commit_sha"`
		DeletedAt                string       `json:"deleted_at"`
		ApprovalsBeforeMerge     string       `json:"approvals_before_merge"`
		RebaseCommitSHA          string       `json:"rebase_commit_sha"`
		InProgressMergeCommitSHA string       `json:"in_progress_merge_commit_sha"`
		LockVersion              int          `json:"lock_version"`
		TimeEstimate             int          `json:"time_estimate"`
		Source                   *Repository  `json:"source"`
		Target                   *Repository  `json:"target"`
		HeadPipelineID           *int         `json:"head_pipeline_id"`
		LastCommit               struct {
			ID        string     `json:"id"`
			Message   string     `json:"message"`
			Title     string     `json:"title"`
			Timestamp *time.Time `json:"timestamp"`
			URL       string     `json:"url"`
			Author    struct {
				Name  string `json:"name"`
				Email string `json:"email"`
			} `json:"author"`
		} `json:"last_commit"`
		BlockingDiscussionsResolved bool          `json:"blocking_discussions_resolved"`
		WorkInProgress              bool          `json:"work_in_progress"`
		Draft                       bool          `json:"draft"`
		TotalTimeSpent              int           `json:"total_time_spent"`
		TimeChange                  int           `json:"time_change"`
		HumanTotalTimeSpent         string        `json:"human_total_time_spent"`
		HumanTimeChange             string        `json:"human_time_change"`
		HumanTimeEstimate           string        `json:"human_time_estimate"`
		FirstContribution           bool          `json:"first_contribution"`
		URL                         string        `json:"url"`
		Labels                      []*EventLabel `json:"labels"`
		Action                      string        `json:"action"`
		DetailedMergeStatus         string        `json:"detailed_merge_status"`
		OldRev                      string        `json:"oldrev"`
	} `json:"object_attributes"`
	Repository *Repository   `json:"repository"`
	Labels     []*EventLabel `json:"labels"`
	Changes    struct {
		Assignees struct {
			Previous []*EventUser `json:"previous"`
			Current  []*EventUser `json:"current"`
		} `json:"assignees"`
		Reviewers struct {
			Previous []*EventUser `json:"previous"`
			Current  []*EventUser `json:"current"`
		} `json:"reviewers"`
		Description struct {
			Previous string `json:"previous"`
			Current  string `json:"current"`
		} `json:"description"`
		Draft struct {
			Previous bool `json:"previous"`
			Current  bool `json:"current"`
		} `json:"draft"`
		Labels struct {
			Previous []*EventLabel `json:"previous"`
			Current  []*EventLabel `json:"current"`
		} `json:"labels"`
		LastEditedAt struct {
			Previous string `json:"previous"`
			Current  string `json:"current"`
		} `json:"last_edited_at"`
		LastEditedByID struct {
			Previous int `json:"previous"`
			Current  int `json:"current"`
		} `json:"last_edited_by_id"`
		MergeStatus struct {
			Previous string `json:"previous"`
			Current  string `json:"current"`
		} `json:"merge_status"`
		MilestoneID struct {
			Previous int `json:"previous"`
			Current  int `json:"current"`
		} `json:"milestone_id"`
		SourceBranch struct {
			Previous string `json:"previous"`
			Current  string `json:"current"`
		} `json:"source_branch"`
		SourceProjectID struct {
			Previous int `json:"previous"`
			Current  int `json:"current"`
		} `json:"source_project_id"`
		StateID struct {
			Previous StateID `json:"previous"`
			Current  StateID `json:"current"`
		} `json:"state_id"`
		TargetBranch struct {
			Previous string `json:"previous"`
			Current  string `json:"current"`
		} `json:"target_branch"`
		TargetProjectID struct {
			Previous int `json:"previous"`
			Current  int `json:"current"`
		} `json:"target_project_id"`
		Title struct {
			Previous string `json:"previous"`
			Current  string `json:"current"`
		} `json:"title"`
		UpdatedAt struct {
			Previous string `json:"previous"`
			Current  string `json:"current"`
		} `json:"updated_at"`
		UpdatedByID struct {
			Previous int `json:"previous"`
			Current  int `json:"current"`
		} `json:"updated_by_id"`
	} `json:"changes"`
	Assignees []*EventUser `json:"assignees"`
	Reviewers []*EventUser `json:"reviewers"`
}

// EventUser represents a user record in an event and is used as an even
// initiator or a merge assignee.
type EventUser struct {
	ID        int    `json:"id"`
	Name      string `json:"name"`
	Username  string `json:"username"`
	AvatarURL string `json:"avatar_url"`
	Email     string `json:"email"`
}

// MergeParams represents the merge params.
type MergeParams struct {
	ForceRemoveSourceBranch bool `json:"force_remove_source_branch"`
}

// UnmarshalJSON decodes the merge parameters
//
// This allows support of ForceRemoveSourceBranch for both type
// bool (>11.9) and string (<11.9)
func (p *MergeParams) UnmarshalJSON(b []byte) error {
	type Alias MergeParams
	raw := struct {
		*Alias
		ForceRemoveSourceBranch interface{} `json:"force_remove_source_branch"`
	}{
		Alias: (*Alias)(p),
	}

	err := json.Unmarshal(b, &raw)
	if err != nil {
		return err
	}

	switch v := raw.ForceRemoveSourceBranch.(type) {
	case nil:
		// No action needed.
	case bool:
		p.ForceRemoveSourceBranch = v
	case string:
		p.ForceRemoveSourceBranch, err = strconv.ParseBool(v)
		if err != nil {
			return err
		}
	default:
		return fmt.Errorf("failed to unmarshal ForceRemoveSourceBranch of type: %T", v)
	}

	return nil
}

// PipelineEvent represents a pipeline event.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/user/project/integrations/webhook_events.html#pipeline-events
type PipelineEvent struct {
	ObjectKind       string `json:"object_kind"`
	ObjectAttributes struct {
		ID             int      `json:"id"`
		IID            int      `json:"iid"`
		Name           string   `json:"name"`
		Ref            string   `json:"ref"`
		Tag            bool     `json:"tag"`
		SHA            string   `json:"sha"`
		BeforeSHA      string   `json:"before_sha"`
		Source         string   `json:"source"`
		Status         string   `json:"status"`
		DetailedStatus string   `json:"detailed_status"`
		Stages         []string `json:"stages"`
		CreatedAt      string   `json:"created_at"`
		FinishedAt     string   `json:"finished_at"`
		Duration       int      `json:"duration"`
		QueuedDuration int      `json:"queued_duration"`
		URL            string   `json:"url"`
		Variables      []struct {
			Key   string `json:"key"`
			Value string `json:"value"`
		} `json:"variables"`
	} `json:"object_attributes"`
	MergeRequest struct {
		ID                  int    `json:"id"`
		IID                 int    `json:"iid"`
		Title               string `json:"title"`
		SourceBranch        string `json:"source_branch"`
		SourceProjectID     int    `json:"source_project_id"`
		TargetBranch        string `json:"target_branch"`
		TargetProjectID     int    `json:"target_project_id"`
		State               string `json:"state"`
		MergeRequestStatus  string `json:"merge_status"`
		DetailedMergeStatus string `json:"detailed_merge_status"`
		URL                 string `json:"url"`
	} `json:"merge_request"`
	User    *EventUser `json:"user"`
	Project struct {
		ID                int             `json:"id"`
		Name              string          `json:"name"`
		Description       string          `json:"description"`
		AvatarURL         string          `json:"avatar_url"`
		GitSSHURL         string          `json:"git_ssh_url"`
		GitHTTPURL        string          `json:"git_http_url"`
		Namespace         string          `json:"namespace"`
		PathWithNamespace string          `json:"path_with_namespace"`
		DefaultBranch     string          `json:"default_branch"`
		Homepage          string          `json:"homepage"`
		URL               string          `json:"url"`
		SSHURL            string          `json:"ssh_url"`
		HTTPURL           string          `json:"http_url"`
		WebURL            string          `json:"web_url"`
		Visibility        VisibilityValue `json:"visibility"`
	} `json:"project"`
	Commit struct {
		ID        string     `json:"id"`
		Message   string     `json:"message"`
		Title     string     `json:"title"`
		Timestamp *time.Time `json:"timestamp"`
		URL       string     `json:"url"`
		Author    struct {
			Name  string `json:"name"`
			Email string `json:"email"`
		} `json:"author"`
	} `json:"commit"`
	SourcePipeline struct {
		Project struct {
			ID                int    `json:"id"`
			WebURL            string `json:"web_url"`
			PathWithNamespace string `json:"path_with_namespace"`
		} `json:"project"`
		PipelineID int `json:"pipeline_id"`
		JobID      int `json:"job_id"`
	} `json:"source_pipeline"`
	Builds []struct {
		ID             int        `json:"id"`
		Stage          string     `json:"stage"`
		Name           string     `json:"name"`
		Status         string     `json:"status"`
		CreatedAt      string     `json:"created_at"`
		StartedAt      string     `json:"started_at"`
		FinishedAt     string     `json:"finished_at"`
		Duration       float64    `json:"duration"`
		QueuedDuration float64    `json:"queued_duration"`
		FailureReason  string     `json:"failure_reason"`
		When           string     `json:"when"`
		Manual         bool       `json:"manual"`
		AllowFailure   bool       `json:"allow_failure"`
		User           *EventUser `json:"user"`
		Runner         struct {
			ID          int      `json:"id"`
			Description string   `json:"description"`
			Active      bool     `json:"active"`
			IsShared    bool     `json:"is_shared"`
			RunnerType  string   `json:"runner_type"`
			Tags        []string `json:"tags"`
		} `json:"runner"`
		ArtifactsFile struct {
			Filename string `json:"filename"`
			Size     int    `json:"size"`
		} `json:"artifacts_file"`
		Environment struct {
			Name           string `json:"name"`
			Action         string `json:"action"`
			DeploymentTier string `json:"deployment_tier"`
		} `json:"environment"`
	} `json:"builds"`
}

// ProjectResourceAccessTokenEvent represents a resource access token event for
// a project.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/user/project/integrations/webhook_events.html#project-and-group-access-token-events
type ProjectResourceAccessTokenEvent struct {
	EventName  string `json:"event_name"`
	ObjectKind string `json:"object_kind"`
	Project    struct {
		ID                int    `json:"id"`
		Name              string `json:"name"`
		Description       string `json:"description"`
		WebURL            string `json:"web_url"`
		AvatarURL         string `json:"avatar_url"`
		GitSSHURL         string `json:"git_ssh_url"`
		GitHTTPURL        string `json:"git_http_url"`
		Namespace         string `json:"namespace"`
		VisibilityLevel   int    `json:"visibility_level"`
		PathWithNamespace string `json:"path_with_namespace"`
		DefaultBranch     string `json:"default_branch"`
		CIConfigPath      string `json:"ci_config_path"`
		Homepage          string `json:"homepage"`
		URL               string `json:"url"`
		SSHURL            string `json:"ssh_url"`
		HTTPURL           string `json:"http_url"`
	} `json:"project"`
	ObjectAttributes struct {
		ID        int      `json:"id"`
		UserID    int      `json:"user_id"`
		Name      string   `json:"name"`
		CreatedAt string   `json:"created_at"`
		ExpiresAt *ISOTime `json:"expires_at"`
	} `json:"object_attributes"`
}

// PushEvent represents a push event.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/user/project/integrations/webhook_events.html#push-events
type PushEvent struct {
	ObjectKind   string `json:"object_kind"`
	EventName    string `json:"event_name"`
	Before       string `json:"before"`
	After        string `json:"after"`
	Ref          string `json:"ref"`
	CheckoutSHA  string `json:"checkout_sha"`
	UserID       int    `json:"user_id"`
	UserName     string `json:"user_name"`
	UserUsername string `json:"user_username"`
	UserEmail    string `json:"user_email"`
	UserAvatar   string `json:"user_avatar"`
	ProjectID    int    `json:"project_id"`
	Project      struct {
		ID                int             `json:"id"`
		Name              string          `json:"name"`
		Description       string          `json:"description"`
		AvatarURL         string          `json:"avatar_url"`
		GitSSHURL         string          `json:"git_ssh_url"`
		GitHTTPURL        string          `json:"git_http_url"`
		Namespace         string          `json:"namespace"`
		PathWithNamespace string          `json:"path_with_namespace"`
		DefaultBranch     string          `json:"default_branch"`
		Homepage          string          `json:"homepage"`
		URL               string          `json:"url"`
		SSHURL            string          `json:"ssh_url"`
		HTTPURL           string          `json:"http_url"`
		WebURL            string          `json:"web_url"`
		Visibility        VisibilityValue `json:"visibility"`
	} `json:"project"`
	Repository *Repository `json:"repository"`
	Commits    []*struct {
		ID        string     `json:"id"`
		Message   string     `json:"message"`
		Title     string     `json:"title"`
		Timestamp *time.Time `json:"timestamp"`
		URL       string     `json:"url"`
		Author    struct {
			Name  string `json:"name"`
			Email string `json:"email"`
		} `json:"author"`
		Added    []string `json:"added"`
		Modified []string `json:"modified"`
		Removed  []string `json:"removed"`
	} `json:"commits"`
	TotalCommitsCount int `json:"total_commits_count"`
}

// ReleaseEvent represents a release event
//
// GitLab API docs:
// https://docs.gitlab.com/ee/user/project/integrations/webhook_events.html#release-events
type ReleaseEvent struct {
	ID          int    `json:"id"`
	CreatedAt   string `json:"created_at"` // Should be *time.Time (see Gitlab issue #21468)
	Description string `json:"description"`
	Name        string `json:"name"`
	Tag         string `json:"tag"`
	ReleasedAt  string `json:"released_at"` // Should be *time.Time (see Gitlab issue #21468)
	ObjectKind  string `json:"object_kind"`
	Project     struct {
		ID                int     `json:"id"`
		Name              string  `json:"name"`
		Description       string  `json:"description"`
		WebURL            string  `json:"web_url"`
		AvatarURL         *string `json:"avatar_url"`
		GitSSHURL         string  `json:"git_ssh_url"`
		GitHTTPURL        string  `json:"git_http_url"`
		Namespace         string  `json:"namespace"`
		VisibilityLevel   int     `json:"visibility_level"`
		PathWithNamespace string  `json:"path_with_namespace"`
		DefaultBranch     string  `json:"default_branch"`
		CIConfigPath      string  `json:"ci_config_path"`
		Homepage          string  `json:"homepage"`
		URL               string  `json:"url"`
		SSHURL            string  `json:"ssh_url"`
		HTTPURL           string  `json:"http_url"`
	} `json:"project"`
	URL    string `json:"url"`
	Action string `json:"action"`
	Assets struct {
		Count int `json:"count"`
		Links []struct {
			ID       int    `json:"id"`
			External bool   `json:"external"`
			LinkType string `json:"link_type"`
			Name     string `json:"name"`
			URL      string `json:"url"`
		} `json:"links"`
		Sources []struct {
			Format string `json:"format"`
			URL    string `json:"url"`
		} `json:"sources"`
	} `json:"assets"`
	Commit struct {
		ID        string `json:"id"`
		Message   string `json:"message"`
		Title     string `json:"title"`
		Timestamp string `json:"timestamp"` // Should be *time.Time (see Gitlab issue #21468)
		URL       string `json:"url"`
		Author    struct {
			Name  string `json:"name"`
			Email string `json:"email"`
		} `json:"author"`
	} `json:"commit"`
}

// SnippetCommentEvent represents a comment on a snippet event.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/user/project/integrations/webhook_events.html#comment-on-a-code-snippet
type SnippetCommentEvent struct {
	ObjectKind string     `json:"object_kind"`
	EventType  string     `json:"event_type"`
	User       *EventUser `json:"user"`
	ProjectID  int        `json:"project_id"`
	Project    struct {
		Name              string          `json:"name"`
		Description       string          `json:"description"`
		AvatarURL         string          `json:"avatar_url"`
		GitSSHURL         string          `json:"git_ssh_url"`
		GitHTTPURL        string          `json:"git_http_url"`
		Namespace         string          `json:"namespace"`
		PathWithNamespace string          `json:"path_with_namespace"`
		DefaultBranch     string          `json:"default_branch"`
		Homepage          string          `json:"homepage"`
		URL               string          `json:"url"`
		SSHURL            string          `json:"ssh_url"`
		HTTPURL           string          `json:"http_url"`
		WebURL            string          `json:"web_url"`
		Visibility        VisibilityValue `json:"visibility"`
	} `json:"project"`
	Repository       *Repository `json:"repository"`
	ObjectAttributes struct {
		ID           int                `json:"id"`
		Note         string             `json:"note"`
		NoteableType string             `json:"noteable_type"`
		AuthorID     int                `json:"author_id"`
		CreatedAt    string             `json:"created_at"`
		UpdatedAt    string             `json:"updated_at"`
		ProjectID    int                `json:"project_id"`
		Attachment   string             `json:"attachment"`
		LineCode     string             `json:"line_code"`
		CommitID     string             `json:"commit_id"`
		NoteableID   int                `json:"noteable_id"`
		System       bool               `json:"system"`
		StDiff       *Diff              `json:"st_diff"`
		Description  string             `json:"description"`
		Action       CommentEventAction `json:"action"`
		URL          string             `json:"url"`
	} `json:"object_attributes"`
	Snippet *struct {
		ID                 int    `json:"id"`
		Title              string `json:"title"`
		Content            string `json:"content"`
		AuthorID           int    `json:"author_id"`
		ProjectID          int    `json:"project_id"`
		CreatedAt          string `json:"created_at"`
		UpdatedAt          string `json:"updated_at"`
		Filename           string `json:"file_name"`
		ExpiresAt          string `json:"expires_at"`
		Type               string `json:"type"`
		VisibilityLevel    int    `json:"visibility_level"`
		Description        string `json:"description"`
		Secret             bool   `json:"secret"`
		RepositoryReadOnly bool   `json:"repository_read_only"`
	} `json:"snippet"`
}

// SubGroupEvent represents a subgroup event.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/user/project/integrations/webhook_events.html#subgroup-events
type SubGroupEvent struct {
	CreatedAt      *time.Time `json:"created_at"`
	UpdatedAt      *time.Time `json:"updated_at"`
	EventName      string     `json:"event_name"`
	Name           string     `json:"name"`
	Path           string     `json:"path"`
	FullPath       string     `json:"full_path"`
	GroupID        int        `json:"group_id"`
	ParentGroupID  int        `json:"parent_group_id"`
	ParentName     string     `json:"parent_name"`
	ParentPath     string     `json:"parent_path"`
	ParentFullPath string     `json:"parent_full_path"`
}

// TagEvent represents a tag event.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/user/project/integrations/webhook_events.html#tag-events
type TagEvent struct {
	ObjectKind   string `json:"object_kind"`
	EventName    string `json:"event_name"`
	Before       string `json:"before"`
	After        string `json:"after"`
	Ref          string `json:"ref"`
	CheckoutSHA  string `json:"checkout_sha"`
	UserID       int    `json:"user_id"`
	UserName     string `json:"user_name"`
	UserUsername string `json:"user_username"`
	UserAvatar   string `json:"user_avatar"`
	UserEmail    string `json:"user_email"`
	ProjectID    int    `json:"project_id"`
	Message      string `json:"message"`
	Project      struct {
		ID                int             `json:"id"`
		Name              string          `json:"name"`
		Description       string          `json:"description"`
		AvatarURL         string          `json:"avatar_url"`
		GitSSHURL         string          `json:"git_ssh_url"`
		GitHTTPURL        string          `json:"git_http_url"`
		Namespace         string          `json:"namespace"`
		PathWithNamespace string          `json:"path_with_namespace"`
		DefaultBranch     string          `json:"default_branch"`
		Homepage          string          `json:"homepage"`
		URL               string          `json:"url"`
		SSHURL            string          `json:"ssh_url"`
		HTTPURL           string          `json:"http_url"`
		WebURL            string          `json:"web_url"`
		Visibility        VisibilityValue `json:"visibility"`
	} `json:"project"`
	Repository *Repository `json:"repository"`
	Commits    []*struct {
		ID        string     `json:"id"`
		Message   string     `json:"message"`
		Title     string     `json:"title"`
		Timestamp *time.Time `json:"timestamp"`
		URL       string     `json:"url"`
		Author    struct {
			Name  string `json:"name"`
			Email string `json:"email"`
		} `json:"author"`
		Added    []string `json:"added"`
		Modified []string `json:"modified"`
		Removed  []string `json:"removed"`
	} `json:"commits"`
	TotalCommitsCount int `json:"total_commits_count"`
}

// WikiPageEvent represents a wiki page event.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/user/project/integrations/webhook_events.html#wiki-page-events
type WikiPageEvent struct {
	ObjectKind string     `json:"object_kind"`
	User       *EventUser `json:"user"`
	Project    struct {
		Name              string          `json:"name"`
		Description       string          `json:"description"`
		AvatarURL         string          `json:"avatar_url"`
		GitSSHURL         string          `json:"git_ssh_url"`
		GitHTTPURL        string          `json:"git_http_url"`
		Namespace         string          `json:"namespace"`
		PathWithNamespace string          `json:"path_with_namespace"`
		DefaultBranch     string          `json:"default_branch"`
		Homepage          string          `json:"homepage"`
		URL               string          `json:"url"`
		SSHURL            string          `json:"ssh_url"`
		HTTPURL           string          `json:"http_url"`
		WebURL            string          `json:"web_url"`
		Visibility        VisibilityValue `json:"visibility"`
	} `json:"project"`
	Wiki struct {
		WebURL            string `json:"web_url"`
		GitSSHURL         string `json:"git_ssh_url"`
		GitHTTPURL        string `json:"git_http_url"`
		PathWithNamespace string `json:"path_with_namespace"`
		DefaultBranch     string `json:"default_branch"`
	} `json:"wiki"`
	ObjectAttributes struct {
		Title   string `json:"title"`
		Content string `json:"content"`
		Format  string `json:"format"`
		Message string `json:"message"`
		Slug    string `json:"slug"`
		URL     string `json:"url"`
		Action  string `json:"action"`
		DiffURL string `json:"diff_url"`
	} `json:"object_attributes"`
}

// EventLabel represents a label inside a webhook event.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/user/project/integrations/webhook_events.html#issue-events
type EventLabel struct {
	ID          int    `json:"id"`
	Title       string `json:"title"`
	Color       string `json:"color"`
	ProjectID   int    `json:"project_id"`
	CreatedAt   string `json:"created_at"`
	UpdatedAt   string `json:"updated_at"`
	Template    bool   `json:"template"`
	Description string `json:"description"`
	Type        string `json:"type"`
	GroupID     int    `json:"group_id"`
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/event_webhook_types_test.go000066400000000000000000001151721475761473200265370ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"encoding/json"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
)

const (
	ExpectedGroup     = "webhook-test"
	excpectedAvatar   = "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon"
	expectedEmail     = "user1@example.com"
	expectedEventName = "user_add_to_group"
	expectedID        = 1
	expectedName      = "User1"
	expectedUsername  = "user1"
)

func TestBuildEventUnmarshal(t *testing.T) {
	jsonObject := loadFixture(t, "testdata/webhooks/build.json")

	var event *BuildEvent
	err := json.Unmarshal(jsonObject, &event)
	if err != nil {
		t.Errorf("Build Event can not unmarshaled: %v\n ", err.Error())
	}

	if event == nil {
		t.Errorf("Build Event is null")
	}

	if event.BuildID != 1977 {
		t.Errorf("BuildID is %v, want %v", event.BuildID, 1977)
	}

	if event.User.ID != 42 {
		t.Errorf("User ID is %d, want %d", event.User.ID, 42)
	}

	if event.User.Name != expectedName {
		t.Errorf("Username is %s, want %s", event.User.Name, expectedName)
	}

	if event.BuildCreatedAt != "2021-02-23T02:41:37.886Z" {
		t.Errorf("BuildCreatedAt is %s, want 2021-02-23T02:41:37.886Z", event.BuildCreatedAt)
	}
}

func TestCommitCommentEventUnmarshal(t *testing.T) {
	jsonObject := loadFixture(t, "testdata/webhooks/note_commit.json")

	var event *CommitCommentEvent
	err := json.Unmarshal(jsonObject, &event)
	if err != nil {
		t.Errorf("Commit Comment Event can not unmarshaled: %v\n ", err.Error())
	}

	if event == nil {
		t.Errorf("Commit Comment Event is null")
	}

	if event.ObjectKind != string(NoteEventTargetType) {
		t.Errorf("ObjectKind is %v, want %v", event.ObjectKind, NoteEventTargetType)
	}

	if event.EventType != "note" {
		t.Errorf("EventType is %v, want %v", event.EventType, "note")
	}

	if event.ProjectID != 5 {
		t.Errorf("ProjectID is %v, want %v", event.ProjectID, 5)
	}

	if event.User.ID != 42 {
		t.Errorf("User ID is %d, want %d", event.User.ID, 42)
	}

	if event.Repository.Name != "Gitlab Test" {
		t.Errorf("Repository name is %v, want %v", event.Repository.Name, "Gitlab Test")
	}

	if event.ObjectAttributes.NoteableType != "Commit" {
		t.Errorf("NoteableType is %v, want %v", event.ObjectAttributes.NoteableType, "Commit")
	}

	if event.ObjectAttributes.Action != CommentEventActionCreate {
		t.Errorf("Action is %v, want %v", event.ObjectAttributes.Action, "create")
	}

	if event.Commit.Title != "Add submodule" {
		t.Errorf("Issue title is %v, want %v", event.Commit.Title, "Add submodule")
	}
}

func TestJobEventUnmarshal(t *testing.T) {
	jsonObject := loadFixture(t, "testdata/webhooks/job.json")

	var event *JobEvent
	err := json.Unmarshal(jsonObject, &event)
	if err != nil {
		t.Errorf("Job Event can not unmarshaled: %v\n ", err.Error())
	}

	if event == nil {
		t.Errorf("Job Event is null")
	}

	expectedEvent := JobEvent{
		ObjectKind:          "build",
		Ref:                 "main",
		Tag:                 false,
		BeforeSHA:           "0000000000000000000000000000000000000000",
		SHA:                 "95d49d1efbd941908580e79d65e4b5ecaf4a8305",
		BuildID:             3580121225,
		BuildName:           "auto_deploy:start",
		BuildStage:          "coordinated:tag",
		BuildStatus:         "success",
		BuildCreatedAt:      "2023-01-10 13:50:02 UTC",
		BuildStartedAt:      "2023-01-10 13:50:05 UTC",
		BuildFinishedAt:     "2023-01-10 13:50:54 UTC",
		BuildDuration:       49.503592,
		BuildQueuedDuration: 0.193009,
		BuildAllowFailure:   false,
		BuildFailureReason:  "unknown_failure",
		RetriesCount:        1,
		PipelineID:          743121198,
		ProjectID:           31537070,
		ProjectName:         "John Smith / release-tools-fake",
		User: &EventUser{
			ID:        2967854,
			Name:      "John Smith",
			Username:  "jsmithy2",
			AvatarURL: "https://gitlab.com/uploads/-/system/user/avatar/2967852/avatar.png",
			Email:     "john@smith.com",
		},
		Repository: &Repository{
			Name:              "release-tools-fake",
			Description:       "",
			WebURL:            "",
			AvatarURL:         "",
			GitSSHURL:         "git@gitlab.com:jsmithy2/release-tools-fake.git",
			GitHTTPURL:        "https://gitlab.com/jsmithy2/release-tools-fake.git",
			Namespace:         "",
			Visibility:        "",
			PathWithNamespace: "",
			DefaultBranch:     "",
			Homepage:          "https://gitlab.com/jsmithy2/release-tools-fake",
			URL:               "git@gitlab.com:jsmithy2/release-tools-fake.git",
			SSHURL:            "",
			HTTPURL:           "",
		},
	}
	expectedEvent.Commit.ID = 743121198
	expectedEvent.Commit.Name = "Build pipeline"
	expectedEvent.Commit.SHA = "95d49d1efbd941908580e79d65e4b5ecaf4a8305"
	expectedEvent.Commit.Message = "Remove test jobs and add back other jobs"
	expectedEvent.Commit.AuthorName = "John Smith"
	expectedEvent.Commit.AuthorEmail = "john@smith.com"
	expectedEvent.Commit.AuthorURL = "https://gitlab.com/jsmithy2"
	expectedEvent.Commit.Status = "running"
	expectedEvent.Commit.Duration = 128
	expectedEvent.Commit.StartedAt = "2023-01-10 13:50:05 UTC"
	expectedEvent.Commit.FinishedAt = "2022-10-12 08:09:29 UTC"

	expectedEvent.Runner.ID = 12270837
	expectedEvent.Runner.Description = "4-blue.shared.runners-manager.gitlab.com/default"
	expectedEvent.Runner.RunnerType = "instance_type"
	expectedEvent.Runner.Active = true
	expectedEvent.Runner.IsShared = true
	expectedEvent.Runner.Tags = []string{"linux", "docker"}

	expectedEvent.Environment.Name = "production"
	expectedEvent.Environment.Action = "start"
	expectedEvent.Environment.DeploymentTier = "production"

	assert.Equal(t, expectedEvent, *event, "event should be equal to the expected one")
}

func TestDeploymentEventUnmarshal(t *testing.T) {
	jsonObject := loadFixture(t, "testdata/webhooks/deployment.json")

	var event *DeploymentEvent
	err := json.Unmarshal(jsonObject, &event)
	if err != nil {
		t.Errorf("Deployment Event can not unmarshaled: %v\n ", err.Error())
	}

	if event == nil {
		t.Errorf("Deployment Event is null")
	}

	if event.Project.ID != 30 {
		t.Errorf("Project.ID is %v, want %v", event.Project.ID, 30)
	}

	if event.User.ID != 42 {
		t.Errorf("User ID is %d, want %d", event.User.ID, 42)
	}

	if event.User.Name != expectedName {
		t.Errorf("Username is %s, want %s", event.User.Name, expectedName)
	}

	if event.CommitTitle != "Add new file" {
		t.Errorf("CommitTitle is %s, want %s", event.CommitTitle, "Add new file")
	}

	if event.Ref != "1.0.0" {
		t.Errorf("Ref is %s, want %s", event.Ref, "1.0.0")
	}

	if event.StatusChangedAt != "2021-04-28 21:50:00 +0200" {
		t.Errorf("StatusChangedAt is %s, want %s", event.StatusChangedAt, "2021-04-28 21:50:00 +0200")
	}

	if event.DeploymentID != 15 {
		t.Errorf("DeploymentID is %d, want %d", event.DeploymentID, 15)
	}

	if event.EnvironmentSlug != "staging" {
		t.Errorf("EnvironmentSlug is %s, want %s", event.EnvironmentSlug, "staging")
	}

	if event.EnvironmentExternalURL != "https://staging.example.com" {
		t.Errorf("EnvironmentExternalURL is %s, want %s", event.EnvironmentExternalURL, "https://staging.example.com")
	}
}

func TestFeatureFlagEventUnmarshal(t *testing.T) {
	jsonObject := loadFixture(t, "testdata/webhooks/feature_flag.json")

	var event *FeatureFlagEvent
	err := json.Unmarshal(jsonObject, &event)
	if err != nil {
		t.Errorf("FeatureFlag Event can not unmarshaled: %v\n ", err.Error())
	}

	if event == nil {
		t.Errorf("FeatureFlag Event is null")
	}

	if event.ObjectKind != "feature_flag" {
		t.Errorf("ObjectKind is %s, want %s", event.ObjectKind, "feature_flag")
	}

	if event.Project.ID != 1 {
		t.Errorf("Project.ID is %v, want %v", event.Project.ID, 1)
	}

	if event.User.ID != 1 {
		t.Errorf("User ID is %d, want %d", event.User.ID, 1)
	}

	if event.User.Name != "Administrator" {
		t.Errorf("Username is %s, want %s", event.User.Name, "Administrator")
	}

	if event.ObjectAttributes.ID != 6 {
		t.Errorf("ObjectAttributes.ID is %d, want %d", event.ObjectAttributes.ID, 6)
	}

	if event.ObjectAttributes.Name != "test-feature-flag" {
		t.Errorf("ObjectAttributes.Name is %s, want %s", event.ObjectAttributes.Name, "test-feature-flag")
	}

	if event.ObjectAttributes.Description != "test-feature-flag-description" {
		t.Errorf("ObjectAttributes.Description is %s, want %s", event.ObjectAttributes.Description, "test-feature-flag-description")
	}

	if event.ObjectAttributes.Active != true {
		t.Errorf("ObjectAttributes.Active is %t, want %t", event.ObjectAttributes.Active, true)
	}
}

func TestGroupResourceAccessTokenEventUnmarshal(t *testing.T) {
	jsonObject := loadFixture(t, "testdata/webhooks/resource_access_token_group.json")
	var event *GroupResourceAccessTokenEvent
	err := json.Unmarshal(jsonObject, &event)
	if err != nil {
		t.Errorf("could not unmarshal event: %v\n ", err.Error())
	}

	if event == nil {
		t.Errorf("event is null")
	}

	expiresAt, err := ParseISOTime("2024-01-26")
	if err != nil {
		t.Fatalf("could not parse ISO time: %v", err)
	}

	expected := &GroupResourceAccessTokenEvent{
		ObjectKind: "access_token",
		EventName:  "expiring_access_token",
	}

	expected.Group.GroupID = 35
	expected.Group.GroupName = "Twitter"
	expected.Group.GroupPath = "twitter"

	expected.ObjectAttributes.ID = 25
	expected.ObjectAttributes.UserID = 90
	expected.ObjectAttributes.Name = "acd"
	expected.ObjectAttributes.CreatedAt = "2024-01-24 16:27:40 UTC"
	expected.ObjectAttributes.ExpiresAt = &expiresAt

	assert.Equal(t, expected, event)
}

func TestIssueCommentEventUnmarshal(t *testing.T) {
	jsonObject := loadFixture(t, "testdata/webhooks/note_issue.json")

	var event *IssueCommentEvent
	err := json.Unmarshal(jsonObject, &event)
	if err != nil {
		t.Errorf("Issue Comment Event can not unmarshaled: %v\n ", err.Error())
	}

	if event.ObjectKind != string(NoteEventTargetType) {
		t.Errorf("ObjectKind is %v, want %v", event.ObjectKind, NoteEventTargetType)
	}

	if event.EventType != "note" {
		t.Errorf("EventType is %v, want %v", event.EventType, "note")
	}

	if event.ProjectID != 5 {
		t.Errorf("ProjectID is %v, want %v", event.ProjectID, 5)
	}

	if event.User.ID != 42 {
		t.Errorf("User ID is %d, want %d", event.User.ID, 42)
	}

	if event.ObjectAttributes.NoteableType != "Issue" {
		t.Errorf("NoteableType is %v, want %v", event.ObjectAttributes.NoteableType, "Issue")
	}

	if event.ObjectAttributes.Action != CommentEventActionCreate {
		t.Errorf("Action is %v, want %v", event.ObjectAttributes.Action, "create")
	}

	if event.Issue.Title != "test_issue" {
		t.Errorf("Issue title is %v, want %v", event.Issue.Title, "test_issue")
	}

	if event.Issue.Position != 0 {
		t.Errorf("Issue position is %v, want %v", event.Issue.Position, 0)
	}

	if event.Issue.BranchName != "" {
		t.Errorf("Issue branch name is %v, want %v", event.Issue.BranchName, "")
	}

	if len(event.Issue.Labels) == 0 || event.Issue.Labels[0].ID != 25 {
		t.Errorf("Label id is null")
	}

	assert.Equal(t, []*EventLabel{
		{
			ID:          25,
			Title:       "Afterpod",
			Color:       "#3e8068",
			ProjectID:   0,
			CreatedAt:   "2019-06-05T14:32:20.211Z",
			UpdatedAt:   "2019-06-05T14:32:20.211Z",
			Template:    false,
			Description: "",
			Type:        "GroupLabel",
			GroupID:     4,
		},
		{
			ID:          86,
			Title:       "Element",
			Color:       "#231afe",
			ProjectID:   4,
			CreatedAt:   "2019-06-05T14:32:20.637Z",
			UpdatedAt:   "2019-06-05T14:32:20.637Z",
			Template:    false,
			Description: "",
			Type:        "ProjectLabel",
			GroupID:     0,
		},
	}, event.Issue.Labels)
}

func TestIssueEventUnmarshal(t *testing.T) {
	jsonObject := loadFixture(t, "testdata/webhooks/issue.json")

	var event *IssueEvent
	err := json.Unmarshal(jsonObject, &event)
	if err != nil {
		t.Errorf("Issue Event can not unmarshaled: %v\n ", err.Error())
	}

	if event.ObjectKind != string(IssueEventTargetType) {
		t.Errorf("ObjectKind is %v, want %v", event.ObjectKind, IssueEventTargetType)
	}

	if event.EventType != "issue" {
		t.Errorf("EventType is %v, want %v", event.EventType, "issue")
	}

	if event.Project.ID != 1 {
		t.Errorf("Project.ID is %v, want %v", event.Project.ID, 1)
	}

	if event.User.ID != 1 {
		t.Errorf("User ID is %d, want %d", event.User.ID, 1)
	}

	if event.Assignee.Username != "user1" {
		t.Errorf("Assignee username is %s, want %s", event.Assignee.Username, "user1")
	}

	if event.ObjectAttributes.ID != 301 {
		t.Errorf("ObjectAttributes.ID is %v, want %v", event.ObjectAttributes.ID, 301)
	}

	if event.ObjectAttributes.Title != "New API: create/update/delete file" {
		t.Errorf("ObjectAttributes.Title is %v, want %v", event.ObjectAttributes.Title, "New API: create/update/delete file")
	}

	if event.ObjectAttributes.StateID != StateIDOpen {
		t.Errorf("ObjectAttributes.StateID is %v, want %v", event.ObjectAttributes.StateID, StateIDOpen)
	}

	if event.ObjectAttributes.State != "opened" {
		t.Errorf("ObjectAttributes.State is %v, want %v", event.ObjectAttributes.State, "opened")
	}
	if event.ObjectAttributes.Confidential != false {
		t.Errorf("ObjectAttributes.Confidential is %v, want %v", event.ObjectAttributes.Confidential, false)
	}

	if event.ObjectAttributes.TotalTimeSpent != 0 {
		t.Errorf("ObjectAttributes.TotalTimeSpent is %v, want %v", event.ObjectAttributes.TotalTimeSpent, 0)
	}

	if event.ObjectAttributes.Action != "open" {
		t.Errorf("ObjectAttributes.Action is %v, want %v", event.ObjectAttributes.Action, "open")
	}

	if event.ObjectAttributes.EscalationStatus != "triggered" {
		t.Errorf("ObjectAttributes.EscalationStatus is %v, want %v", event.ObjectAttributes.EscalationStatus, "triggered")
	}

	if event.ObjectAttributes.EscalationPolicy.ID != 18 {
		t.Errorf("ObjectAttributes.EscalationPolicy.ID is %v, want %v", event.ObjectAttributes.EscalationPolicy.ID, 18)
	}

	if event.Changes.TotalTimeSpent.Previous != 8100 {
		t.Errorf("Changes.TotalTimeSpent.Previous is %v , want %v", event.Changes.TotalTimeSpent.Previous, 8100)
	}

	if event.Changes.TotalTimeSpent.Current != 9900 {
		t.Errorf("Changes.TotalTimeSpent.Current is %v , want %v", event.Changes.TotalTimeSpent.Current, 8100)
	}

	assert.Equal(t, []*EventLabel{
		{
			ID:          206,
			Title:       "API",
			Color:       "#ffffff",
			ProjectID:   14,
			CreatedAt:   "2013-12-03T17:15:43Z",
			UpdatedAt:   "2013-12-03T17:15:43Z",
			Template:    false,
			Description: "API related issues",
			Type:        "ProjectLabel",
			GroupID:     41,
		},
	}, event.Labels)

	assert.Equal(t, []*EventLabel{
		{
			ID:          206,
			Title:       "API",
			Color:       "#ffffff",
			ProjectID:   14,
			CreatedAt:   "2013-12-03T17:15:43Z",
			UpdatedAt:   "2013-12-03T17:15:43Z",
			Template:    false,
			Description: "API related issues",
			Type:        "ProjectLabel",
			GroupID:     41,
		},
	}, event.Changes.Labels.Previous)

	assert.Equal(t, []*EventLabel{
		{
			ID:          205,
			Title:       "Platform",
			Color:       "#123123",
			ProjectID:   14,
			CreatedAt:   "2013-12-03T17:15:43Z",
			UpdatedAt:   "2013-12-03T17:15:43Z",
			Template:    false,
			Description: "Platform related issues",
			Type:        "ProjectLabel",
			GroupID:     41,
		},
	}, event.Changes.Labels.Current)

	assert.Equal(t, "2017-09-15 16:54:55 UTC", event.Changes.ClosedAt.Previous)
	assert.Equal(t, "2017-09-15 16:56:00 UTC", event.Changes.ClosedAt.Current)

	assert.Equal(t, StateIDNone, event.Changes.StateID.Previous)
	assert.Equal(t, StateIDOpen, event.Changes.StateID.Current)

	assert.Equal(t, "2017-09-15 16:50:55 UTC", event.Changes.UpdatedAt.Previous)
	assert.Equal(t, "2017-09-15 16:52:00 UTC", event.Changes.UpdatedAt.Current)
}

// Generate unit test for MergeCommentEvent
func TestMergeCommentEventUnmarshal(t *testing.T) {
	jsonObject := loadFixture(t, "testdata/webhooks/note_merge_request.json")

	var event *MergeCommentEvent
	err := json.Unmarshal(jsonObject, &event)
	if err != nil {
		t.Errorf("Merge Comment Event can not unmarshaled: %v\n ", err.Error())
	}

	if event == nil {
		t.Errorf("Merge Comment Event is null")
	}

	if event.ObjectAttributes.ID != 1244 {
		t.Errorf("ObjectAttributes.ID is %v, want %v", event.ObjectAttributes.ID, 1244)
	}

	if event.ObjectAttributes.Note != "This MR needs work." {
		t.Errorf("ObjectAttributes.Note is %v, want %v", event.ObjectAttributes.Note, "This MR needs work.")
	}

	if event.ObjectAttributes.NoteableType != "MergeRequest" {
		t.Errorf("ObjectAttributes.NoteableType is %v, want %v", event.ObjectAttributes.NoteableType, "MergeRequest")
	}

	if event.ObjectAttributes.Action != CommentEventActionCreate {
		t.Errorf("Action is %v, want %v", event.ObjectAttributes.Action, "create")
	}

	if event.ObjectAttributes.AuthorID != 1 {
		t.Errorf("ObjectAttributes.AuthorID is %v, want %v", event.ObjectAttributes.AuthorID, 1)
	}

	if event.ObjectAttributes.CreatedAt != "2015-05-17 18:21:36 UTC" {
		t.Errorf("ObjectAttributes.CreatedAt is %v, want %v", event.ObjectAttributes.CreatedAt, "2015-05-17 18:21:36 UTC")
	}

	if event.ObjectAttributes.UpdatedAt != "2015-05-17 18:21:36 UTC" {
		t.Errorf("ObjectAttributes.UpdatedAt is %v, want %v", event.ObjectAttributes.UpdatedAt, "2015-05-17 18:21:36 UTC")
	}

	if event.ObjectAttributes.ProjectID != 5 {
		t.Errorf("ObjectAttributes.ProjectID is %v, want %v", event.ObjectAttributes.ProjectID, 5)
	}

	if event.MergeRequest.ID != 7 {
		t.Errorf("MergeRequest.ID is %v, want %v", event.MergeRequest.ID, 7)
	}

	if event.MergeRequest.TargetBranch != "markdown" {
		t.Errorf("MergeRequest.TargetBranch is %v, want %v", event.MergeRequest.TargetBranch, "markdown")
	}

	// generate test code for rest of the event.MergeRequest fields
	if event.MergeRequest.SourceBranch != "master" {
		t.Errorf("MergeRequest.SourceBranch is %v, want %v", event.MergeRequest.SourceBranch, "ms-viewport")
	}

	if event.MergeRequest.SourceProjectID != 5 {
		t.Errorf("MergeRequest.SourceProjectID is %v, want %v", event.MergeRequest.SourceProjectID, 5)
	}

	if event.MergeRequest.AuthorID != 8 {
		t.Errorf("MergeRequest.AuthorID is %v, want %v", event.MergeRequest.AuthorID, 8)
	}

	if event.MergeRequest.AssigneeID != 28 {
		t.Errorf("MergeRequest.AssigneeID is %v, want %v", event.MergeRequest.AssigneeID, 28)
	}

	if event.MergeRequest.State != "opened" {
		t.Errorf("MergeRequest.state is %v, want %v", event.MergeRequest.State, "opened")
	}

	if event.MergeRequest.MergeStatus != "cannot_be_merged" {
		t.Errorf("MergeRequest.merge_status is %v, want %v", event.MergeRequest.MergeStatus, "cannot_be_merged")
	}

	if event.MergeRequest.TargetProjectID != 5 {
		t.Errorf("MergeRequest.target_project_id is %v, want %v", event.MergeRequest.TargetProjectID, 5)
	}

	assert.Equal(t, []*EventLabel{
		{
			ID:          206,
			Title:       "Afterpod",
			Color:       "#3e8068",
			ProjectID:   0,
			CreatedAt:   "2019-06-05T14:32:20.211Z",
			UpdatedAt:   "2019-06-05T14:32:20.211Z",
			Template:    false,
			Description: "",
			Type:        "GroupLabel",
			GroupID:     4,
		},
		{
			ID:          86,
			Title:       "Element",
			Color:       "#231afe",
			ProjectID:   4,
			CreatedAt:   "2019-06-05T14:32:20.637Z",
			UpdatedAt:   "2019-06-05T14:32:20.637Z",
			Template:    false,
			Description: "",
			Type:        "ProjectLabel",
			GroupID:     0,
		},
	}, event.MergeRequest.Labels)

	assert.Equal(t, &EventUser{
		ID:        0,
		Name:      "User1",
		Username:  "user1",
		AvatarURL: "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon",
		Email:     "",
	}, event.MergeRequest.Assignee)

	if event.MergeRequest.DetailedMergeStatus != "checking" {
		t.Errorf("MergeRequest.DetailedMergeStatus is %v, want %v", event.MergeRequest.DetailedMergeStatus, "checking")
	}
}

func TestMergeEventUnmarshal(t *testing.T) {
	jsonObject := loadFixture(t, "testdata/webhooks/merge_request.json")

	var event *MergeEvent
	err := json.Unmarshal(jsonObject, &event)
	if err != nil {
		t.Errorf("Merge Event can not unmarshaled: %v\n ", err.Error())
	}

	if event == nil {
		t.Errorf("Merge Event is null")
	}

	if event.EventType != "merge_request" {
		t.Errorf("EventType is %v, want %v", event.EventType, "merge_request")
	}

	if event.Project.CIConfigPath != "" {
		t.Errorf("Project.CIConfigPath is %v, want %v", event.Project.CIConfigPath, "")
	}

	if event.ObjectAttributes.ID != 99 {
		t.Errorf("ObjectAttributes.ID is %v, want %v", event.ObjectAttributes.ID, 99)
	}

	if event.ObjectAttributes.Source.Homepage != "http://example.com/awesome_space/awesome_project" {
		t.Errorf("ObjectAttributes.Source.Homepage is %v, want %v", event.ObjectAttributes.Source.Homepage, "http://example.com/awesome_space/awesome_project")
	}

	if event.ObjectAttributes.LastCommit.ID != "da1560886d4f094c3e6c9ef40349f7d38b5d27d7" {
		t.Errorf("ObjectAttributes.LastCommit.ID is %v, want %s", event.ObjectAttributes.LastCommit.ID, "da1560886d4f094c3e6c9ef40349f7d38b5d27d7")
	}

	if event.ObjectAttributes.TotalTimeSpent != 0 {
		t.Errorf("ObjectAttributes.TotalTimeSpent is %v, want %v", event.ObjectAttributes.TotalTimeSpent, 0)
	}

	if event.ObjectAttributes.TimeChange != 0 {
		t.Errorf("ObjectAttributes.TimeChange is %v, want %v", event.ObjectAttributes.TimeChange, 0)
	}

	if event.ObjectAttributes.HumanTotalTimeSpent != "30m" {
		t.Errorf("ObjectAttributes.HumanTotalTimeSpent is %v, want %v", event.ObjectAttributes.HumanTotalTimeSpent, "30m")
	}

	if event.ObjectAttributes.HumanTimeChange != "30m" {
		t.Errorf("ObjectAttributes.HumanTimeChange is %v, want %v", event.ObjectAttributes.HumanTimeChange, "30m")
	}

	if event.ObjectAttributes.HumanTimeEstimate != "1h" {
		t.Errorf("ObjectAttributes.HumanTimeEstimate is %v, want %v", event.ObjectAttributes.HumanTimeEstimate, "1h")
	}

	if event.Assignees[0].Name != expectedName {
		t.Errorf("Assignee.Name is %v, want %v", event.Assignees[0].Name, expectedName)
	}

	if event.Assignees[0].Username != expectedUsername {
		t.Errorf("ObjectAttributes is %v, want %v", event.Assignees[0].Username, expectedUsername)
	}

	if event.User.ID != expectedID {
		t.Errorf("User ID is %d, want %d", event.User.ID, expectedID)
	}

	if event.User.Name != expectedName {
		t.Errorf("Username is %s, want %s", event.User.Name, expectedName)
	}

	if event.User.Email != expectedEmail {
		t.Errorf("User email is %s, want %s", event.User.Email, expectedEmail)
	}

	if event.ObjectAttributes.LastCommit.Timestamp == nil {
		t.Errorf("Timestamp isn't nil")
	}

	if name := event.ObjectAttributes.LastCommit.Author.Name; name != "GitLab dev user" {
		t.Errorf("Commit Username is %s, want %s", name, "GitLab dev user")
	}

	if event.ObjectAttributes.BlockingDiscussionsResolved != true {
		t.Errorf("BlockingDiscussionsResolved isn't true")
	}

	if event.ObjectAttributes.FirstContribution != true {
		t.Errorf("FirstContribution isn't true")
	}

	if event.Assignees[0].ID != expectedID {
		t.Errorf("Assignees[0].ID is %v, want %v", event.Assignees[0].ID, expectedID)
	}

	if event.Assignees[0].Name != expectedName {
		t.Errorf("Assignees[0].Name is %v, want %v", event.Assignees[0].Name, expectedName)
	}

	if event.Assignees[0].Username != expectedUsername {
		t.Errorf("Assignees[0].Username is %v, want %v", event.Assignees[0].Username, expectedName)
	}

	if event.Assignees[0].AvatarURL != excpectedAvatar {
		t.Errorf("Assignees[0].AvatarURL is %v, want %v", event.Assignees[0].AvatarURL, excpectedAvatar)
	}

	if len(event.Reviewers) < 1 {
		t.Errorf("Reviewers length is %d, want %d", len(event.Reviewers), 1)
	}

	if event.Reviewers[0].Name != expectedName {
		t.Errorf("Reviewers[0].Name is %v, want %v", event.Reviewers[0].Name, expectedName)
	}

	if event.Reviewers[0].Username != expectedUsername {
		t.Errorf("Reviewer[0].Username is %v, want %v", event.Reviewers[0].Username, expectedUsername)
	}

	if event.Reviewers[0].AvatarURL != excpectedAvatar {
		t.Errorf("Reviewers[0].AvatarURL is %v, want %v", event.Reviewers[0].AvatarURL, excpectedAvatar)
	}

	if event.ObjectAttributes.DetailedMergeStatus != "mergeable" {
		t.Errorf("DetailedMergeStatus is %s, want %s", event.ObjectAttributes.DetailedMergeStatus, "mergeable")
	}

	assert.Equal(t, []*EventLabel{
		{
			ID:          206,
			Title:       "API",
			Color:       "#ffffff",
			ProjectID:   14,
			CreatedAt:   "2013-12-03T17:15:43Z",
			UpdatedAt:   "2013-12-03T17:15:43Z",
			Template:    false,
			Description: "API related issues",
			Type:        "ProjectLabel",
			GroupID:     41,
		},
	}, event.Labels)

	assert.Equal(t, []*EventLabel{
		{
			ID:          206,
			Title:       "API",
			Color:       "#ffffff",
			ProjectID:   14,
			CreatedAt:   "2013-12-03T17:15:43Z",
			UpdatedAt:   "2013-12-03T17:15:43Z",
			Template:    false,
			Description: "API related issues",
			Type:        "ProjectLabel",
			GroupID:     41,
		},
	}, event.ObjectAttributes.Labels)

	assert.Equal(t, []*EventLabel{
		{
			ID:          206,
			Title:       "API",
			Color:       "#ffffff",
			ProjectID:   14,
			CreatedAt:   "2013-12-03T17:15:43Z",
			UpdatedAt:   "2013-12-03T17:15:43Z",
			Template:    false,
			Description: "API related issues",
			Type:        "ProjectLabel",
			GroupID:     41,
		},
	}, event.Changes.Labels.Previous)

	assert.Equal(t, []*EventLabel{
		{
			ID:          205,
			Title:       "Platform",
			Color:       "#123123",
			ProjectID:   14,
			CreatedAt:   "2013-12-03T17:15:43Z",
			UpdatedAt:   "2013-12-03T17:15:43Z",
			Template:    false,
			Description: "Platform related issues",
			Type:        "ProjectLabel",
			GroupID:     41,
		},
	}, event.Changes.Labels.Current)

	assert.Equal(t, StateIDLocked, event.Changes.StateID.Previous)
	assert.Equal(t, StateIDMerged, event.Changes.StateID.Current)

	assert.Equal(t, "2017-09-15 16:50:55 UTC", event.Changes.UpdatedAt.Previous)
	assert.Equal(t, "2017-09-15 16:52:00 UTC", event.Changes.UpdatedAt.Current)
}

func TestMemberEventUnmarshal(t *testing.T) {
	jsonObject := loadFixture(t, "testdata/webhooks/member.json")

	var event *MemberEvent
	err := json.Unmarshal(jsonObject, &event)
	if err != nil {
		t.Errorf("Member Event can not unmarshaled: %v\n ", err.Error())
	}

	if event == nil {
		t.Errorf("Member Event is null")
	}

	if event.GroupName != ExpectedGroup {
		t.Errorf("Name is %v, want %v", event.GroupName, ExpectedGroup)
	}

	if event.GroupPath != ExpectedGroup {
		t.Errorf("GroupPath is %v, want %v", event.GroupPath, ExpectedGroup)
	}

	if event.GroupID != 100 {
		t.Errorf(
			"GroupID is %v, want %v", event.GroupID, 100)
	}

	if event.UserUsername != expectedUsername {
		t.Errorf(
			"UserUsername is %v, want %v", event.UserUsername, expectedUsername)
	}

	if event.UserName != expectedName {
		t.Errorf(
			"UserName is %v, want %v", event.UserName, expectedName)
	}

	if event.UserEmail != "testuser@webhooktest.com" {
		t.Errorf(
			"UserEmail is %v, want %v", event.UserEmail, "testuser@webhooktest.com")
	}

	if event.UserID != 64 {
		t.Errorf(
			"UserID is %v, want %v", event.UserID, 64)
	}

	if event.GroupAccess != "Guest" {
		t.Errorf(
			"GroupAccess is %v, want %v", event.GroupAccess, "Guest")
	}

	if event.EventName != expectedEventName {
		t.Errorf(
			"EventName is %v, want %v", event.EventName, expectedEventName)
	}

	if event.CreatedAt.Format(time.RFC3339) != "2020-12-11T04:57:22Z" {
		t.Errorf("CreatedAt is %v, want %v", event.CreatedAt.Format(time.RFC3339), "2020-12-11T04:57:22Z")
	}

	if event.UpdatedAt.Format(time.RFC3339) != "2020-12-11T04:57:22Z" {
		t.Errorf("UpdatedAt is %v, want %v", event.UpdatedAt.Format(time.RFC3339), "2020-12-11T04:57:22Z")
	}

	if event.ExpiresAt.Format(time.RFC3339) != "2020-12-14T00:00:00Z" {
		t.Errorf("ExpiresAt is %v, want %v", event.ExpiresAt.Format(time.RFC3339), "2020-12-14T00:00:00Z")
	}
}

func TestMergeEventUnmarshalFromGroup(t *testing.T) {
	jsonObject := loadFixture(t, "testdata/webhooks/group_merge_request.json")

	var event *MergeEvent
	err := json.Unmarshal(jsonObject, &event)
	if err != nil {
		t.Errorf("Group Merge Event can not unmarshaled: %v\n ", err.Error())
	}

	if event == nil {
		t.Errorf("Group Merge Event is null")
	}

	if event.ObjectKind != eventObjectKindMergeRequest {
		t.Errorf("ObjectKind is %v, want %v", event.ObjectKind, eventObjectKindMergeRequest)
	}

	if event.User.Username != expectedUsername {
		t.Errorf("User.Username is %v, want %v", event.User.Username, expectedUsername)
	}

	if event.Project.Name != exampleProjectName {
		t.Errorf("Project.Name is %v, want %v", event.Project.Name, exampleProjectName)
	}

	if event.ObjectAttributes.ID != 15917 {
		t.Errorf("ObjectAttributes.ID is %v, want %v", event.ObjectAttributes.ID, 15917)
	}

	if event.ObjectAttributes.Source.Name != exampleProjectName {
		t.Errorf("ObjectAttributes.Source.Name is %v, want %v", event.ObjectAttributes.Source.Name, exampleProjectName)
	}

	if event.ObjectAttributes.LastCommit.Author.Email != "test.user@mail.com" {
		t.Errorf("ObjectAttributes.LastCommit.Author.Email is %v, want %v", event.ObjectAttributes.LastCommit.Author.Email, "test.user@mail.com")
	}

	if event.Repository.Name != exampleProjectName {
		t.Errorf("Repository.Name is %v, want %v", event.Repository.Name, exampleProjectName)
	}

	if event.User.Name != expectedName {
		t.Errorf("Username is %s, want %s", event.User.Name, expectedName)
	}

	if event.ObjectAttributes.LastCommit.Timestamp == nil {
		t.Errorf("Timestamp isn't nil")
	}

	if name := event.ObjectAttributes.LastCommit.Author.Name; name != "Test User" {
		t.Errorf("Commit Username is %s, want %s", name, "Test User")
	}
}

func TestPipelineEventUnmarshal(t *testing.T) {
	jsonObject := loadFixture(t, "testdata/webhooks/pipeline.json")

	var event *PipelineEvent
	err := json.Unmarshal(jsonObject, &event)
	if err != nil {
		t.Errorf("Pipeline Event can not unmarshaled: %v\n ", err.Error())
	}

	if event == nil {
		t.Errorf("Pipeline Event is null")
	}

	if event.ObjectAttributes.ID != 31 {
		t.Errorf("ObjectAttributes.ID is %v, want %v", event.ObjectAttributes.ID, 31)
	}

	if event.ObjectAttributes.IID != 123 {
		t.Errorf("ObjectAttributes.IID is %v, want %v", event.ObjectAttributes.ID, 123)
	}

	if event.ObjectAttributes.DetailedStatus != "passed" {
		t.Errorf("ObjectAttributes.DetailedStatus is %v, want %v", event.ObjectAttributes.DetailedStatus, "passed")
	}

	if event.ObjectAttributes.QueuedDuration != 12 {
		t.Errorf("ObjectAttributes.QueuedDuration is %v, want %v", event.ObjectAttributes.QueuedDuration, 12)
	}

	if event.ObjectAttributes.Variables[0].Key != "NESTOR_PROD_ENVIRONMENT" {
		t.Errorf("ObjectAttributes.Variables[0].Key is %v, want %v", event.ObjectAttributes.Variables[0].Key, "NESTOR_PROD_ENVIRONMENT")
	}

	if event.User.ID != 42 {
		t.Errorf("User ID is %d, want %d", event.User.ID, 42)
	}

	if event.User.Name != expectedName {
		t.Errorf("Username is %s, want %s", event.User.Name, expectedName)
	}

	if event.Commit.Timestamp == nil {
		t.Errorf("Timestamp isn't nil")
	}

	if name := event.Commit.Author.Name; name != "User" {
		t.Errorf("Commit Username is %s, want %s", name, "User")
	}

	if len(event.Builds) != 5 {
		t.Errorf("Builds length is %d, want %d", len(event.Builds), 5)
	}

	if event.Builds[0].AllowFailure != true {
		t.Errorf("Builds.0.AllowFailure is %v, want %v", event.Builds[0].AllowFailure, true)
	}

	if event.Builds[0].Environment.Name != "production" {
		t.Errorf("Builds.0.Environment.Name is %v, want %v", event.Builds[0].Environment.Name, "production")
	}

	if event.Builds[0].Duration != 17.1 {
		t.Errorf("Builds[0].Duration is %v, want %v", event.Builds[0].Duration, 17.1)
	}

	if event.Builds[0].QueuedDuration != 3.5 {
		t.Errorf("Builds[0].QueuedDuration is %v, want %v", event.Builds[0].QueuedDuration, 3.5)
	}

	if event.Builds[0].FailureReason != "script_failure" {
		t.Errorf("Builds[0].Failurereason is %v, want %v", event.Builds[0].FailureReason, "script_failure")
	}

	if event.Builds[1].FailureReason != "" {
		t.Errorf("Builds[0].Failurereason is %v, want %v", event.Builds[0].FailureReason, "''")
	}

	if event.SourcePipeline.PipelineID != 30 {
		t.Errorf("Source Pipline ID is %v, want %v", event.SourcePipeline.PipelineID, 30)
	}

	if event.SourcePipeline.JobID != 3401 {
		t.Errorf("Source Pipline JobID is %v, want %v", event.SourcePipeline.JobID, 3401)
	}

	if event.SourcePipeline.Project.ID != 41 {
		t.Errorf("Source Pipline Project ID is %v, want %v", event.SourcePipeline.Project.ID, 41)
	}

	if event.MergeRequest.DetailedMergeStatus != "mergeable" {
		t.Errorf("MergeRequest.DetailedMergeStatus is %v, want %v", event.MergeRequest.DetailedMergeStatus, "mergeable")
	}
}

func TestProjectResourceAccessTokenEventUnmarshal(t *testing.T) {
	jsonObject := loadFixture(t, "testdata/webhooks/resource_access_token_project.json")
	var event *ProjectResourceAccessTokenEvent
	err := json.Unmarshal(jsonObject, &event)
	if err != nil {
		t.Errorf("could not unmarshal event: %v\n ", err.Error())
	}

	if event == nil {
		t.Errorf("event is null")
	}

	expiresAt, err := ParseISOTime("2024-01-26")
	if err != nil {
		t.Fatalf("could not parse ISO time: %v", err)
	}

	expected := &ProjectResourceAccessTokenEvent{
		ObjectKind: "access_token",
		EventName:  "expiring_access_token",
	}

	expected.ObjectAttributes.ID = 25
	expected.ObjectAttributes.UserID = 90
	expected.ObjectAttributes.Name = "acd"
	expected.ObjectAttributes.CreatedAt = "2024-01-24 16:27:40 UTC"
	expected.ObjectAttributes.ExpiresAt = &expiresAt

	expected.Project.ID = 7
	expected.Project.Name = "Flight"
	expected.Project.Description = "Eum dolore maxime atque reprehenderit voluptatem."
	expected.Project.WebURL = "https://example.com/flightjs/Flight"
	expected.Project.AvatarURL = ""
	expected.Project.GitSSHURL = "ssh://git@example.com/flightjs/Flight.git"
	expected.Project.GitHTTPURL = "https://example.com/flightjs/Flight.git"
	expected.Project.Namespace = "Flightjs"
	expected.Project.VisibilityLevel = 0
	expected.Project.PathWithNamespace = "flightjs/Flight"
	expected.Project.DefaultBranch = "master"
	expected.Project.CIConfigPath = ""
	expected.Project.Homepage = "https://example.com/flightjs/Flight"
	expected.Project.URL = "ssh://git@example.com/flightjs/Flight.git"
	expected.Project.SSHURL = "ssh://git@example.com/flightjs/Flight.git"
	expected.Project.HTTPURL = "https://example.com/flightjs/Flight.git"

	assert.Equal(t, expected, event)
}

func TestPushEventUnmarshal(t *testing.T) {
	jsonObject := loadFixture(t, "testdata/webhooks/push.json")
	var event *PushEvent
	err := json.Unmarshal(jsonObject, &event)
	if err != nil {
		t.Errorf("Push Event can not unmarshaled: %v\n ", err.Error())
	}

	if event == nil {
		t.Errorf("Push Event is null")
	}

	if event.EventName != "push" {
		t.Errorf("EventName is %v, want %v", event.EventName, "push")
	}

	if event.ProjectID != 15 {
		t.Errorf("ProjectID is %v, want %v", event.ProjectID, 15)
	}

	if event.UserName != exampleEventUserName {
		t.Errorf("Username is %s, want %s", event.UserName, exampleEventUserName)
	}

	if event.Project.ID != 15 {
		t.Errorf("Project.ID is %v, want %v", event.Project.ID, 15)
	}

	if event.Commits[0] == nil || event.Commits[0].Timestamp == nil {
		t.Errorf("Commit Timestamp isn't nil")
	}

	if event.Commits[0] == nil || event.Commits[0].Message != exampleCommitMessage {
		t.Errorf("Commit Message is %s, want %s", event.Commits[0].Message, exampleCommitMessage)
	}

	if event.Commits[0] == nil || event.Commits[0].Title != exampleCommitTitle {
		t.Errorf("Commit Title is %s, want %s", event.Commits[0].Title, exampleCommitTitle)
	}

	if event.Commits[0] == nil || event.Commits[0].Author.Name != "Jordi Mallach" {
		t.Errorf("Commit Username is %s, want %s", event.UserName, "Jordi Mallach")
	}
}

func TestReleaseEventUnmarshal(t *testing.T) {
	jsonObject := loadFixture(t, "testdata/webhooks/release.json")

	var event *ReleaseEvent
	err := json.Unmarshal(jsonObject, &event)
	if err != nil {
		t.Errorf("Release Event can not unmarshaled: %v\n ", err.Error())
	}

	if event == nil {
		t.Errorf("Release Event is null")
	}

	if event.Project.ID != 327622 {
		t.Errorf("Project.ID is %v, want %v", event.Project.ID, 327622)
	}

	if event.Commit.Title != "Merge branch 'example-branch' into 'master'" {
		t.Errorf("Commit title is %s, want %s", event.Commit.Title, "Merge branch 'example-branch' into 'master'")
	}

	if len(event.Assets.Sources) != 4 {
		t.Errorf("Asset sources length is %d, want %d", len(event.Assets.Sources), 4)
	}

	if event.Assets.Sources[0].Format != "zip" {
		t.Errorf("First asset source format is %s, want %s", event.Assets.Sources[0].Format, "zip")
	}

	if len(event.Assets.Links) != 1 {
		t.Errorf("Asset links length is %d, want %d", len(event.Assets.Links), 1)
	}

	if event.Assets.Links[0].Name != "Changelog" {
		t.Errorf("First asset link name is %s, want %s", event.Assets.Links[0].Name, "Changelog")
	}

	if event.Commit.Author.Name != "User" {
		t.Errorf("Commit author name is %s, want %s", event.Commit.Author.Name, "User")
	}
}

func TestSubGroupEventUnmarshal(t *testing.T) {
	jsonObject := loadFixture(t, "testdata/webhooks/subgroup.json")

	var event *SubGroupEvent
	err := json.Unmarshal(jsonObject, &event)
	if err != nil {
		t.Errorf("SubGroup Event can not unmarshaled: %v\n ", err.Error())
	}

	if event == nil {
		t.Errorf("SubGroup Event is null")
	}

	if event.Name != "SubGroup 1" {
		t.Errorf("Name is %v, want %v", event.Name, "SubGroup 1")
	}

	if event.GroupID != 2 {
		t.Errorf("GroupID is %v, want %v", event.GroupID, 2)
	}

	if event.ParentGroupID != 1 {
		t.Errorf("ParentGroupID is %v, want %v", event.ParentGroupID, 1)
	}

	if event.CreatedAt.Format(time.RFC3339) != "2022-01-24T14:23:59Z" {
		t.Errorf("CreatedAt is %v, want %v", event.CreatedAt.Format(time.RFC3339), "2022-01-24T14:23:59Z")
	}
}

func TestTagEventUnmarshal(t *testing.T) {
	jsonObject := loadFixture(t, "testdata/webhooks/tag_push.json")
	var event *TagEvent
	err := json.Unmarshal(jsonObject, &event)
	if err != nil {
		t.Errorf("Tag Event can not unmarshaled: %v\n ", err.Error())
	}

	if event == nil {
		t.Errorf("Tag Event is null")
	}

	if event.EventName != "tag_push" {
		t.Errorf("EventName is %v, want %v", event.EventName, "tag_push")
	}

	if event.ProjectID != 1 {
		t.Errorf("ProjectID is %v, want %v", event.ProjectID, 1)
	}

	if event.Project.ID != 1 {
		t.Errorf("Project.ID is %v, want %v", event.Project.ID, 1)
	}

	if event.UserName != exampleEventUserName {
		t.Errorf("Username is %s, want %s", event.UserName, exampleEventUserName)
	}

	if event.Commits[0] == nil || event.Commits[0].Timestamp == nil {
		t.Errorf("Commit Timestamp isn't nil")
	}

	if event.Commits[0] == nil || event.Commits[0].Message != exampleCommitMessage {
		t.Errorf("Commit Message is %s, want %s", event.Commits[0].Message, exampleCommitMessage)
	}

	if event.Commits[0] == nil || event.Commits[0].Title != exampleCommitTitle {
		t.Errorf("Commit Title is %s, want %s", event.Commits[0].Title, exampleCommitTitle)
	}

	if event.Commits[0] == nil || event.Commits[0].Author.Name != exampleEventUserName {
		t.Errorf("Commit Username is %s, want %s", event.UserName, exampleEventUserName)
	}
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/events.go000066400000000000000000000173451475761473200227240ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"fmt"
	"net/http"
	"time"
)

// EventsService handles communication with the event related methods of
// the GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/events.html
type EventsService struct {
	client *Client
}

// ContributionEvent represents a user's contribution
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/events.html#get-user-contribution-events
type ContributionEvent struct {
	ID          int        `json:"id"`
	Title       string     `json:"title"`
	ProjectID   int        `json:"project_id"`
	ActionName  string     `json:"action_name"`
	TargetID    int        `json:"target_id"`
	TargetIID   int        `json:"target_iid"`
	TargetType  string     `json:"target_type"`
	AuthorID    int        `json:"author_id"`
	TargetTitle string     `json:"target_title"`
	CreatedAt   *time.Time `json:"created_at"`
	PushData    struct {
		CommitCount int    `json:"commit_count"`
		Action      string `json:"action"`
		RefType     string `json:"ref_type"`
		CommitFrom  string `json:"commit_from"`
		CommitTo    string `json:"commit_to"`
		Ref         string `json:"ref"`
		CommitTitle string `json:"commit_title"`
	} `json:"push_data"`
	Note   *Note `json:"note"`
	Author struct {
		Name      string `json:"name"`
		Username  string `json:"username"`
		ID        int    `json:"id"`
		State     string `json:"state"`
		AvatarURL string `json:"avatar_url"`
		WebURL    string `json:"web_url"`
	} `json:"author"`
	AuthorUsername string `json:"author_username"`
}

// ListContributionEventsOptions represents the options for GetUserContributionEvents
//
// GitLap API docs:
// https://docs.gitlab.com/ee/api/events.html#get-user-contribution-events
type ListContributionEventsOptions struct {
	ListOptions
	Action     *EventTypeValue       `url:"action,omitempty" json:"action,omitempty"`
	TargetType *EventTargetTypeValue `url:"target_type,omitempty" json:"target_type,omitempty"`
	Before     *ISOTime              `url:"before,omitempty" json:"before,omitempty"`
	After      *ISOTime              `url:"after,omitempty" json:"after,omitempty"`
	Sort       *string               `url:"sort,omitempty" json:"sort,omitempty"`
}

// ListUserContributionEvents retrieves user contribution events
// for the specified user, sorted from newest to oldest.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/events.html#get-user-contribution-events
func (s *UsersService) ListUserContributionEvents(uid interface{}, opt *ListContributionEventsOptions, options ...RequestOptionFunc) ([]*ContributionEvent, *Response, error) {
	user, err := parseID(uid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("users/%s/events", user)

	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var cs []*ContributionEvent
	resp, err := s.client.Do(req, &cs)
	if err != nil {
		return nil, resp, err
	}

	return cs, resp, nil
}

// ListCurrentUserContributionEvents gets a list currently authenticated user's events
//
// GitLab API docs: https://docs.gitlab.com/ee/api/events.html#list-currently-authenticated-users-events
func (s *EventsService) ListCurrentUserContributionEvents(opt *ListContributionEventsOptions, options ...RequestOptionFunc) ([]*ContributionEvent, *Response, error) {
	req, err := s.client.NewRequest(http.MethodGet, "events", opt, options)
	if err != nil {
		return nil, nil, err
	}

	var cs []*ContributionEvent
	resp, err := s.client.Do(req, &cs)
	if err != nil {
		return nil, resp, err
	}

	return cs, resp, nil
}

// ProjectEvent represents a GitLab project event.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/events.html#list-a-projects-visible-events
type ProjectEvent struct {
	ID          int    `json:"id"`
	Title       string `json:"title"`
	ProjectID   int    `json:"project_id"`
	ActionName  string `json:"action_name"`
	TargetID    int    `json:"target_id"`
	TargetIID   int    `json:"target_iid"`
	TargetType  string `json:"target_type"`
	AuthorID    int    `json:"author_id"`
	TargetTitle string `json:"target_title"`
	CreatedAt   string `json:"created_at"`
	Author      struct {
		Name      string `json:"name"`
		Username  string `json:"username"`
		ID        int    `json:"id"`
		State     string `json:"state"`
		AvatarURL string `json:"avatar_url"`
		WebURL    string `json:"web_url"`
	} `json:"author"`
	AuthorUsername string `json:"author_username"`
	Data           struct {
		Before            string      `json:"before"`
		After             string      `json:"after"`
		Ref               string      `json:"ref"`
		UserID            int         `json:"user_id"`
		UserName          string      `json:"user_name"`
		Repository        *Repository `json:"repository"`
		Commits           []*Commit   `json:"commits"`
		TotalCommitsCount int         `json:"total_commits_count"`
	} `json:"data"`
	Note struct {
		ID         int    `json:"id"`
		Body       string `json:"body"`
		Attachment string `json:"attachment"`
		Author     struct {
			ID        int    `json:"id"`
			Username  string `json:"username"`
			Email     string `json:"email"`
			Name      string `json:"name"`
			State     string `json:"state"`
			AvatarURL string `json:"avatar_url"`
			WebURL    string `json:"web_url"`
		} `json:"author"`
		CreatedAt    *time.Time `json:"created_at"`
		System       bool       `json:"system"`
		NoteableID   int        `json:"noteable_id"`
		NoteableType string     `json:"noteable_type"`
		NoteableIID  int        `json:"noteable_iid"`
	} `json:"note"`
	PushData struct {
		CommitCount int    `json:"commit_count"`
		Action      string `json:"action"`
		RefType     string `json:"ref_type"`
		CommitFrom  string `json:"commit_from"`
		CommitTo    string `json:"commit_to"`
		Ref         string `json:"ref"`
		CommitTitle string `json:"commit_title"`
	} `json:"push_data"`
}

func (s ProjectEvent) String() string {
	return Stringify(s)
}

// ListProjectVisibleEventsOptions represents the available
// ListProjectVisibleEvents() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/events.html#list-a-projects-visible-events
type ListProjectVisibleEventsOptions struct {
	ListOptions
	Action     *EventTypeValue       `url:"action,omitempty" json:"action,omitempty"`
	TargetType *EventTargetTypeValue `url:"target_type,omitempty" json:"target_type,omitempty"`
	Before     *ISOTime              `url:"before,omitempty" json:"before,omitempty"`
	After      *ISOTime              `url:"after,omitempty" json:"after,omitempty"`
	Sort       *string               `url:"sort,omitempty" json:"sort,omitempty"`
}

// ListProjectVisibleEvents gets the events for the specified project.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/events.html#list-a-projects-visible-events
func (s *EventsService) ListProjectVisibleEvents(pid interface{}, opt *ListProjectVisibleEventsOptions, options ...RequestOptionFunc) ([]*ProjectEvent, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/events", PathEscape(project))

	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var p []*ProjectEvent
	resp, err := s.client.Do(req, &p)
	if err != nil {
		return nil, resp, err
	}

	return p, resp, nil
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/events_test.go000066400000000000000000000222321475761473200237520ustar00rootroot00000000000000package gitlab

import (
	"fmt"
	"net/http"
	"testing"

	"github.com/stretchr/testify/require"
)

func TestUsersService_ListUserContributionEvents(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/users/1/events", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
			[
			  {
				"id": 3,
				"title": null,
				"project_id": 15,
				"action_name": "closed",
				"target_id": 830,
				"target_type": "Issue",
				"author_id": 1,
				"target_title": "Public project search field",
				"author": {
				  "name": "Venkatesh Thalluri",
				  "username": "venky333",
				  "id": 1,
				  "state": "active",
				  "avatar_url": "http://localhost:3000/uploads/user/avatar/1/fox_avatar.png",
				  "web_url": "http://localhost:3000/venky333"
				},
				"author_username": "venky333"
			  }
			]
		`)
	})

	want := []*ContributionEvent{
		{
			ID:          3,
			Title:       "",
			ProjectID:   15,
			ActionName:  "closed",
			TargetID:    830,
			TargetIID:   0,
			TargetType:  "Issue",
			AuthorID:    1,
			TargetTitle: "Public project search field",
			Note:        nil,
			Author: struct {
				Name      string `json:"name"`
				Username  string `json:"username"`
				ID        int    `json:"id"`
				State     string `json:"state"`
				AvatarURL string `json:"avatar_url"`
				WebURL    string `json:"web_url"`
			}{
				Name:      "Venkatesh Thalluri",
				Username:  "venky333",
				ID:        1,
				State:     "active",
				AvatarURL: "http://localhost:3000/uploads/user/avatar/1/fox_avatar.png",
				WebURL:    "http://localhost:3000/venky333",
			},
			AuthorUsername: "venky333",
		},
	}

	ces, resp, err := client.Users.ListUserContributionEvents(1, nil, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, ces)

	ces, resp, err = client.Users.ListUserContributionEvents(1.01, nil, nil)
	require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, ces)

	ces, resp, err = client.Users.ListUserContributionEvents(1, nil, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, ces)

	ces, resp, err = client.Users.ListUserContributionEvents(3, nil, nil)
	require.Error(t, err)
	require.Nil(t, ces)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestEventsService_ListCurrentUserContributionEvents(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/events", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
			[
			  {
				"id": 1,
				"title":null,
				"project_id":1,
				"action_name":"opened",
				"target_id":160,
				"target_type":"Issue",
				"author_id":25,
				"target_title":"Qui natus eos odio tempore et quaerat consequuntur ducimus cupiditate quis.",
				"author":{
				  "name":"Venkatesh Thalluri",
				  "username":"venky333",
				  "id":25,
				  "state":"active",
				  "avatar_url":"http://www.gravatar.com/avatar/97d6d9441ff85fdc730e02a6068d267b?s=80u0026d=identicon",
				  "web_url":"https://gitlab.example.com/venky333"
				},
				"author_username":"venky333"
			  }
			]
		`)
	})

	want := []*ContributionEvent{
		{
			ID:          1,
			Title:       "",
			ProjectID:   1,
			ActionName:  "opened",
			TargetID:    160,
			TargetIID:   0,
			TargetType:  "Issue",
			AuthorID:    25,
			TargetTitle: "Qui natus eos odio tempore et quaerat consequuntur ducimus cupiditate quis.",
			Note:        nil,
			Author: struct {
				Name      string `json:"name"`
				Username  string `json:"username"`
				ID        int    `json:"id"`
				State     string `json:"state"`
				AvatarURL string `json:"avatar_url"`
				WebURL    string `json:"web_url"`
			}{
				Name:      "Venkatesh Thalluri",
				Username:  "venky333",
				ID:        25,
				State:     "active",
				AvatarURL: "http://www.gravatar.com/avatar/97d6d9441ff85fdc730e02a6068d267b?s=80u0026d=identicon",
				WebURL:    "https://gitlab.example.com/venky333",
			},
			AuthorUsername: "venky333",
		},
	}

	ces, resp, err := client.Events.ListCurrentUserContributionEvents(nil, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, ces)

	ces, resp, err = client.Events.ListCurrentUserContributionEvents(nil, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, ces)
}

func TestEventsService_ListCurrentUserContributionEvents_StatusNotFound(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/events", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		w.WriteHeader(http.StatusNotFound)
	})

	ces, resp, err := client.Events.ListCurrentUserContributionEvents(nil, nil)
	require.Error(t, err)
	require.Nil(t, ces)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestEventsService_ListProjectVisibleEvents(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/15/events", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
			[
			  {
				"id": 3,
				"title": null,
				"project_id": 15,
				"action_name": "closed",
				"target_id": 830,
				"target_type": "Issue",
				"author_id": 1,
				"target_title": "Public project search field",
				"author": {
				  "name": "Venkatesh Thalluri",
				  "username": "venky333",
				  "id": 1,
				  "state": "active",
				  "avatar_url": "http://localhost:3000/uploads/user/avatar/1/fox_avatar.png",
				  "web_url": "http://localhost:3000/venky333"
				},
				"author_username": "venky333"
			},
			{
				"id": 4,
				"title": null,
				"project_id": 15,
				"action_name": "pushed",
				"target_id": null,
				"target_type": null,
				"author_id": 1,
				"author": {
				"name": "Dmitriy Zaporozhets",
				"username": "root",
				"id": 1,
				"state": "active",
				"avatar_url": "http://localhost:3000/uploads/user/avatar/1/fox_avatar.png",
				"web_url": "http://localhost:3000/root"
				},
				"author_username": "john",
				"push_data": {
				"commit_count": 1,
				"action": "pushed",
				"ref_type": "branch",
				"commit_from": "50d4420237a9de7be1304607147aec22e4a14af7",
				"commit_to": "c5feabde2d8cd023215af4d2ceeb7a64839fc428",
				"ref": "master",
				"commit_title": "Add simple search to projects in public area"
				},
				"target_title": null
			  }
			]
		`)
	})

	want := []*ProjectEvent{
		{
			ID:          3,
			Title:       "",
			ProjectID:   15,
			ActionName:  "closed",
			TargetID:    830,
			TargetIID:   0,
			TargetType:  "Issue",
			AuthorID:    1,
			TargetTitle: "Public project search field",
			Author: struct {
				Name      string `json:"name"`
				Username  string `json:"username"`
				ID        int    `json:"id"`
				State     string `json:"state"`
				AvatarURL string `json:"avatar_url"`
				WebURL    string `json:"web_url"`
			}{
				Name:      "Venkatesh Thalluri",
				Username:  "venky333",
				ID:        1,
				State:     "active",
				AvatarURL: "http://localhost:3000/uploads/user/avatar/1/fox_avatar.png",
				WebURL:    "http://localhost:3000/venky333",
			},
			AuthorUsername: "venky333",
		},
		// example from https://docs.gitlab.com/ee/api/events.html#get-user-contribution-events
		{
			ID:          4,
			Title:       "",
			ProjectID:   15,
			ActionName:  "pushed",
			TargetID:    0,
			TargetIID:   0,
			TargetType:  "",
			AuthorID:    1,
			TargetTitle: "",
			CreatedAt:   "",
			Author: struct {
				Name      string `json:"name"`
				Username  string `json:"username"`
				ID        int    `json:"id"`
				State     string `json:"state"`
				AvatarURL string `json:"avatar_url"`
				WebURL    string `json:"web_url"`
			}{
				Name:      "Dmitriy Zaporozhets",
				Username:  "root",
				ID:        1,
				State:     "active",
				AvatarURL: "http://localhost:3000/uploads/user/avatar/1/fox_avatar.png",
				WebURL:    "http://localhost:3000/root",
			},
			AuthorUsername: "john",
			PushData: struct {
				CommitCount int    `json:"commit_count"`
				Action      string `json:"action"`
				RefType     string `json:"ref_type"`
				CommitFrom  string `json:"commit_from"`
				CommitTo    string `json:"commit_to"`
				Ref         string `json:"ref"`
				CommitTitle string `json:"commit_title"`
			}{
				CommitCount: 1,
				Action:      "pushed",
				RefType:     "branch",
				CommitFrom:  "50d4420237a9de7be1304607147aec22e4a14af7",
				CommitTo:    "c5feabde2d8cd023215af4d2ceeb7a64839fc428",
				Ref:         "master",
				CommitTitle: "Add simple search to projects in public area",
			},
		},
	}

	ces, resp, err := client.Events.ListProjectVisibleEvents(15, nil, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, ces)

	ces, resp, err = client.Events.ListProjectVisibleEvents(15.01, nil, nil)
	require.EqualError(t, err, "invalid ID type 15.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, ces)

	ces, resp, err = client.Events.ListProjectVisibleEvents(15, nil, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, ces)

	ces, resp, err = client.Events.ListProjectVisibleEvents(3, nil, nil)
	require.Error(t, err)
	require.Nil(t, ces)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/examples/000077500000000000000000000000001475761473200226755ustar00rootroot00000000000000golang-gitlab-gitlab-org-api-client-go-0.123.0/examples/applications.go000066400000000000000000000030271475761473200257140ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package main

import (
	"log"

	"gitlab.com/gitlab-org/api/client-go"
)

func applicationsExample() {
	git, err := gitlab.NewClient("yourtokengoeshere")
	if err != nil {
		log.Fatal(err)
	}

	// Create an application
	opts := &gitlab.CreateApplicationOptions{
		Name:        gitlab.Ptr("Travis"),
		RedirectURI: gitlab.Ptr("http://example.org"),
		Scopes:      gitlab.Ptr("api"),
	}
	created, _, err := git.Applications.CreateApplication(opts)
	if err != nil {
		log.Fatal(err)
	}
	log.Printf("Last created application : %v", created)

	// List all applications
	applications, _, err := git.Applications.ListApplications(&gitlab.ListApplicationsOptions{})
	if err != nil {
		log.Fatal(err)
	}

	for _, app := range applications {
		log.Printf("Found app : %v", app)
	}

	// Delete an application
	resp, err := git.Applications.DeleteApplication(created.ID)
	if err != nil {
		log.Fatal(err)
	}

	log.Printf("Status code response : %d", resp.StatusCode)
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/examples/basic_auth.go000066400000000000000000000021171475761473200253270ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package main

import (
	"log"

	"gitlab.com/gitlab-org/api/client-go"
)

// This example shows how to create a client with username and password.
func basicAuthExample() {
	git, err := gitlab.NewBasicAuthClient(
		"svanharmelen",
		"password",
		gitlab.WithBaseURL("https://gitlab.company.com"),
	)
	if err != nil {
		log.Fatal(err)
	}

	// List all projects
	projects, _, err := git.Projects.ListProjects(nil)
	if err != nil {
		log.Fatal(err)
	}

	log.Printf("Found %d projects", len(projects))
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/examples/bitbucket_import.go000066400000000000000000000026451475761473200266010ustar00rootroot00000000000000package main

import (
	"log"

	"gitlab.com/gitlab-org/api/client-go"
)

func bitbucketCloudExample() {
	git, err := gitlab.NewClient("yourtokengoeshere")
	if err != nil {
		log.Fatal(err)
	}

	cloudOpt := &gitlab.ImportRepositoryFromBitbucketCloudOptions{
		BitbucketUsername:    gitlab.Ptr("username"),
		BitbucketAppPassword: gitlab.Ptr("password"),
		RepoPath:             gitlab.Ptr("some/repo"),
		TargetNamespace:      gitlab.Ptr("some-group"),
		NewName:              gitlab.Ptr("some-repo"),
	}
	cloudResp, _, err := git.Import.ImportRepositoryFromBitbucketCloud(cloudOpt)
	if err != nil {
		log.Fatal(err)
	}
	log.Print(cloudResp.String())
}

func bitbucketServerExample() {
	git, err := gitlab.NewClient("yourtokengoeshere")
	if err != nil {
		log.Fatal(err)
	}

	serverOpt := &gitlab.ImportRepositoryFromBitbucketServerOptions{
		BitbucketServerUrl:      gitlab.Ptr("https://bitbucket.example.com"),
		BitbucketServerUsername: gitlab.Ptr("username"),
		PersonalAccessToken:     gitlab.Ptr("access-token"),
		BitbucketServerProject:  gitlab.Ptr("some-project"),
		BitbucketServerRepo:     gitlab.Ptr("some-repo"),
		NewName:                 gitlab.Ptr("some-other-repo"),
		NewNamespace:            gitlab.Ptr("some-group"),
		TimeoutStrategy:         gitlab.Ptr("pessimistic"),
	}
	serverResp, _, err := git.Import.ImportRepositoryFromBitbucketServer(serverOpt)
	if err != nil {
		log.Fatal(err)
	}
	log.Print(serverResp.String())
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/examples/cluster_agents.go000066400000000000000000000023641475761473200262530ustar00rootroot00000000000000//
// Copyright 2021, Timo Furrer 
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package main

import (
	"log"

	"gitlab.com/gitlab-org/api/client-go"
)

func clusterAgentsExample() {
	git, err := gitlab.NewClient("tokengoeshere")
	if err != nil {
		log.Fatal(err)
	}

	projectID := 33
	opt := &gitlab.RegisterAgentOptions{
		Name: gitlab.Ptr("agent-2"),
	}

	// Register Cluster Agent
	clusterAgent, _, err := git.ClusterAgents.RegisterAgent(projectID, opt)
	if err != nil {
		log.Fatal(err)
	}

	log.Printf("Cluster Agent: %+v\n", clusterAgent)

	// List Cluster Agents
	clusterAgents, _, err := git.ClusterAgents.ListAgents(projectID, nil)
	if err != nil {
		log.Fatal(err)
	}

	log.Printf("Cluster Agents: %+v", clusterAgents)
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/examples/deployments_merge_requests.go000066400000000000000000000020721475761473200307020ustar00rootroot00000000000000//
// Copyright 2022, Daniela Filipe Bento
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package main

import (
	"log"

	gitlab "gitlab.com/gitlab-org/api/client-go"
)

func deploymentExample() {
	git, err := gitlab.NewClient("yourtokengoeshere")
	if err != nil {
		log.Fatal(err)
	}

	opt := &gitlab.ListMergeRequestsOptions{}
	mergeRequests, _, err := git.DeploymentMergeRequests.ListDeploymentMergeRequests(1, 1, opt)
	if err != nil {
		log.Fatal(err)
	}

	for _, mergeRequest := range mergeRequests {
		log.Printf("Found merge request: %v\n", mergeRequest)
	}
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/examples/impersonation.go000066400000000000000000000025331475761473200261160ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package main

import (
	"log"

	"gitlab.com/gitlab-org/api/client-go"
)

func impersonationExample() {
	git, err := gitlab.NewClient("yourtokengoeshere")
	if err != nil {
		log.Fatal(err)
	}

	uid := 1

	// list impersonation token from an user
	tokens, _, err := git.Users.GetAllImpersonationTokens(
		uid,
		&gitlab.GetAllImpersonationTokensOptions{State: gitlab.Ptr("active")},
	)
	if err != nil {
		log.Fatal(err)
	}

	for _, token := range tokens {
		log.Printf("Found token: %s", token.Token)
	}

	// create an impersonation token of an user
	token, _, err := git.Users.CreateImpersonationToken(
		uid,
		&gitlab.CreateImpersonationTokenOptions{Scopes: &[]string{"api"}},
	)
	if err != nil {
		log.Fatal(err)
	}

	log.Printf("Created token: %s", token.Token)
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/examples/labels.go000066400000000000000000000024221475761473200244660ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package main

import (
	"log"

	"gitlab.com/gitlab-org/api/client-go"
)

func labelExample() {
	git, err := gitlab.NewClient("yourtokengoeshere")
	if err != nil {
		log.Fatal(err)
	}

	// Create new label
	opt := &gitlab.CreateLabelOptions{
		Name:  gitlab.Ptr("My Label"),
		Color: gitlab.Ptr("#11FF22"),
	}
	label, _, err := git.Labels.CreateLabel("myname/myproject", opt)
	if err != nil {
		log.Fatal(err)
	}
	log.Printf("Created label: %s\nWith color: %s\n", label.Name, label.Color)

	// List all labels
	labels, _, err := git.Labels.ListLabels("myname/myproject", nil)
	if err != nil {
		log.Fatal(err)
	}

	for _, label := range labels {
		log.Printf("Found label: %s", label.Name)
	}
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/examples/languages.go000066400000000000000000000016571475761473200252030ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package main

import (
	"log"

	"gitlab.com/gitlab-org/api/client-go"
)

func languagesExample() {
	git, err := gitlab.NewClient("yourtokengoeshere")
	if err != nil {
		log.Fatal(err)
	}

	languages, _, err := git.Projects.GetProjectLanguages("2743054")
	if err != nil {
		log.Fatal(err)
	}
	log.Printf("Found languages: %v", languages)
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/examples/main.go000066400000000000000000000014421475761473200241510ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package main

func main() {
	// See the separate files in this directory for the examples. This file is only
	// here to provide a main() function for the `example` package, keeping Travis happy.
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/examples/pagination.go000066400000000000000000000043461475761473200253640ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package main

import (
	"log"

	"gitlab.com/gitlab-org/api/client-go"
)

func pagination() {
	git, err := gitlab.NewClient("yourtokengoeshere")
	if err != nil {
		log.Fatal(err)
	}

	opt := &gitlab.ListProjectsOptions{
		ListOptions: gitlab.ListOptions{
			PerPage: 10,
			Page:    1,
		},
	}

	for {
		// Get the first page with projects.
		ps, resp, err := git.Projects.ListProjects(opt)
		if err != nil {
			log.Fatal(err)
		}

		// List all the projects we've found so far.
		for _, p := range ps {
			log.Printf("Found project: %s", p.Name)
		}

		// Exit the loop when we've seen all pages.
		if resp.NextPage == 0 {
			break
		}

		// Update the page number to get the next page.
		opt.Page = resp.NextPage
	}
}

func keysetPagination() {
	git, err := gitlab.NewClient("yourtokengoeshere")
	if err != nil {
		log.Fatal(err)
	}

	opt := &gitlab.ListProjectsOptions{
		ListOptions: gitlab.ListOptions{
			OrderBy:    "id",
			Pagination: "keyset",
			PerPage:    5,
			Sort:       "asc",
		},
		Owned: gitlab.Bool(true),
	}

	options := []gitlab.RequestOptionFunc{}

	for {
		// Get the first page with projects.
		ps, resp, err := git.Projects.ListProjects(opt, options...)
		if err != nil {
			log.Fatal(err)
		}

		// List all the projects we've found so far.
		for _, p := range ps {
			log.Printf("Found project: %s", p.Name)
		}

		// Exit the loop when we've seen all pages.
		if resp.NextLink == "" {
			break
		}

		// Set all query parameters in the next request to values in the
		// returned parameters. This could go along with any existing options.
		options = []gitlab.RequestOptionFunc{
			gitlab.WithKeysetPaginationParameters(resp.NextLink),
		}
	}
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/examples/personal_access_tokens.go000066400000000000000000000037261475761473200277630ustar00rootroot00000000000000//
// Copyright 2022, Ryan Glab 
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package main

import (
	"encoding/json"
	"fmt"
	"log"
	"time"

	"gitlab.com/gitlab-org/api/client-go"
)

func patRevokeExample() {
	git, err := gitlab.NewClient("glpat-123xyz")
	if err != nil {
		log.Fatal(err)
	}

	resp, err := git.PersonalAccessTokens.RevokePersonalAccessToken(99999999)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("Response Code: %s", resp.Status)
}

func patListExampleWithUserFilter() {
	git, err := gitlab.NewClient("glpat-123xyz")
	if err != nil {
		log.Fatal(err)
	}

	opt := &gitlab.ListPersonalAccessTokensOptions{
		ListOptions: gitlab.ListOptions{Page: 1, PerPage: 10},
		UserID:      gitlab.Ptr(12345),
	}

	personalAccessTokens, _, err := git.PersonalAccessTokens.ListPersonalAccessTokens(opt)
	if err != nil {
		log.Fatal(err)
	}

	data, err := json.Marshal(personalAccessTokens)
	if err != nil {
		log.Fatal(err)
	}

	log.Printf("Found personal access tokens: %s", data)
}

func patRotateExample() {
	git, err := gitlab.NewClient("glpat-123xyz")
	if err != nil {
		log.Fatal(err)
	}

	expiry := gitlab.ISOTime(time.Date(2023, time.August, 15, 0, 0, 0, 0, time.UTC))
	opts := &gitlab.RotatePersonalAccessTokenOptions{
		ExpiresAt: &expiry,
	}
	newPersonalAccessToken, _, err := git.PersonalAccessTokens.RotatePersonalAccessToken(12345, opts)
	if err != nil {
		log.Fatal(err)
	}

	log.Printf("Your new token is %s\n", newPersonalAccessToken.Token)
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/examples/pipelines.go000066400000000000000000000027241475761473200252210ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package main

import (
	"log"
	"time"

	"gitlab.com/gitlab-org/api/client-go"
)

func pipelineExample() {
	git, err := gitlab.NewClient("yourtokengoeshere")
	if err != nil {
		log.Fatal(err)
	}

	opt := &gitlab.ListProjectPipelinesOptions{
		Scope:         gitlab.Ptr("branches"),
		Status:        gitlab.Ptr(gitlab.Running),
		Ref:           gitlab.Ptr("master"),
		YamlErrors:    gitlab.Ptr(true),
		Name:          gitlab.Ptr("name"),
		Username:      gitlab.Ptr("username"),
		UpdatedAfter:  gitlab.Ptr(time.Now().Add(-24 * 365 * time.Hour)),
		UpdatedBefore: gitlab.Ptr(time.Now().Add(-7 * 24 * time.Hour)),
		OrderBy:       gitlab.Ptr("status"),
		Sort:          gitlab.Ptr("asc"),
	}

	pipelines, _, err := git.Pipelines.ListProjectPipelines(2743054, opt)
	if err != nil {
		log.Fatal(err)
	}

	for _, pipeline := range pipelines {
		log.Printf("Found pipeline: %v", pipeline)
	}
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/examples/pipelinestestreport.go000066400000000000000000000023531475761473200273530ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package main

import (
	"log"

	"gitlab.com/gitlab-org/api/client-go"
)

func pipelineTestReportExample() {
	git, err := gitlab.NewClient("yourtokengoeshere")
	if err != nil {
		log.Fatal(err)
	}

	opt := &gitlab.ListProjectPipelinesOptions{Ref: gitlab.Ptr("master")}
	projectID := 1234

	pipelines, _, err := git.Pipelines.ListProjectPipelines(projectID, opt)
	if err != nil {
		log.Fatal(err)
	}

	for _, pipeline := range pipelines {
		log.Printf("Found pipeline: %v", pipeline)

		report, _, err := git.Pipelines.GetPipelineTestReport(projectID, pipeline.ID)
		if err != nil {
			log.Fatal(err)
		}

		log.Printf("Found test report: %v", report)
	}
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/examples/projects.go000066400000000000000000000035521475761473200250620ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package main

import (
	"log"

	"gitlab.com/gitlab-org/api/client-go"
)

func projectExample() {
	git, err := gitlab.NewClient("yourtokengoeshere")
	if err != nil {
		log.Fatal(err)
	}

	// Create new project
	p := &gitlab.CreateProjectOptions{
		Name:                     gitlab.Ptr("My Project"),
		Description:              gitlab.Ptr("Just a test project to play with"),
		MergeRequestsAccessLevel: gitlab.Ptr(gitlab.EnabledAccessControl),
		SnippetsAccessLevel:      gitlab.Ptr(gitlab.EnabledAccessControl),
		Visibility:               gitlab.Ptr(gitlab.PublicVisibility),
	}
	project, _, err := git.Projects.CreateProject(p)
	if err != nil {
		log.Fatal(err)
	}

	// Add a new snippet
	s := &gitlab.CreateProjectSnippetOptions{
		Title:      gitlab.Ptr("Dummy Snippet"),
		FileName:   gitlab.Ptr("snippet.go"),
		Content:    gitlab.Ptr("package main...."),
		Visibility: gitlab.Ptr(gitlab.PublicVisibility),
	}
	_, _, err = git.ProjectSnippets.CreateSnippet(project.ID, s)
	if err != nil {
		log.Fatal(err)
	}

	// List all project snippets
	snippets, _, err := git.ProjectSnippets.ListSnippets(project.PathWithNamespace, &gitlab.ListProjectSnippetsOptions{})
	if err != nil {
		log.Fatal(err)
	}

	for _, snippet := range snippets {
		log.Printf("Found snippet: %s", snippet.Title)
	}
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/examples/repository_archive.go000066400000000000000000000021331475761473200271430ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package main

import (
	"log"

	"gitlab.com/gitlab-org/api/client-go"
)

func repositoryArchiveExample() {
	git, err := gitlab.NewClient("yourtokengoeshere")
	if err != nil {
		log.Fatal(err)
	}

	// Get repository archive
	opt := &gitlab.ArchiveOptions{
		Format: gitlab.String("tar.gz"),
		Path:   gitlab.String("mydir"),
	}
	content, _, err := git.Repositories.Archive("mygroup/myproject", opt, nil)
	if err != nil {
		log.Fatal(err)
	}

	log.Printf("Repository archive contains %d byte(s)", len(content))
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/examples/repository_files.go000066400000000000000000000035731475761473200266350ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package main

import (
	"log"

	"gitlab.com/gitlab-org/api/client-go"
)

func repositoryFileExample() {
	git, err := gitlab.NewClient("yourtokengoeshere")
	if err != nil {
		log.Fatal(err)
	}

	// Create a new repository file
	cf := &gitlab.CreateFileOptions{
		Branch:        gitlab.Ptr("master"),
		Content:       gitlab.Ptr("My file contents"),
		CommitMessage: gitlab.Ptr("Adding a test file"),
	}
	file, _, err := git.RepositoryFiles.CreateFile("myname/myproject", "file.go", cf)
	if err != nil {
		log.Fatal(err)
	}

	// Update a repository file
	uf := &gitlab.UpdateFileOptions{
		Branch:        gitlab.Ptr("master"),
		Content:       gitlab.Ptr("My file content"),
		CommitMessage: gitlab.Ptr("Fixing typo"),
	}
	_, _, err = git.RepositoryFiles.UpdateFile("myname/myproject", file.FilePath, uf)
	if err != nil {
		log.Fatal(err)
	}

	gf := &gitlab.GetFileOptions{
		Ref: gitlab.Ptr("master"),
	}
	f, _, err := git.RepositoryFiles.GetFile("myname/myproject", file.FilePath, gf)
	if err != nil {
		log.Fatal(err)
	}

	log.Printf("File contains: %s", f.Content)

	gfb := &gitlab.GetFileBlameOptions{
		Ref: gitlab.Ptr("master"),
	}
	fb, _, err := git.RepositoryFiles.GetFileBlame("myname/myproject", file.FilePath, gfb)
	if err != nil {
		log.Fatal(err)
	}

	log.Printf("Found %d blame ranges", len(fb))
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/examples/services.go000066400000000000000000000031441475761473200250510ustar00rootroot00000000000000//
// Copyright 2023, Joel Gerber
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package main

import (
	"fmt"
	"log"

	"gitlab.com/gitlab-org/api/client-go"
)

func dataDogExample() {
	git, err := gitlab.NewClient("yourtokengoeshere")
	if err != nil {
		log.Fatal(err)
	}

	// Create new DataDog integration
	opts := &gitlab.SetDataDogServiceOptions{
		APIKey:             gitlab.String("testing"),
		DataDogEnv:         gitlab.String("sandbox"),
		DataDogService:     gitlab.String("test"),
		DataDogSite:        gitlab.String("datadoghq.com"),
		DataDogTags:        gitlab.String("country:canada\nprovince:ontario"),
		ArchiveTraceEvents: gitlab.Bool(true),
	}

	svc, _, err := git.Services.SetDataDogService(1, opts)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf(
		"api_url: %s, datadog_env: %s, datadog_service: %s, datadog_site: %s, datadog_tags: %s",
		svc.Properties.APIURL, svc.Properties.DataDogEnv, svc.Properties.DataDogService,
		svc.Properties.DataDogSite, svc.Properties.DataDogTags,
	)

	// Delete the integration
	_, err = git.Services.DeleteDataDogService(1)
	if err != nil {
		log.Fatal(err)
	}
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/examples/topics.go000066400000000000000000000032471475761473200245330ustar00rootroot00000000000000//
// Copyright 2021, Timo Furrer 
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package main

import (
	"log"
	"os"

	"gitlab.com/gitlab-org/api/client-go"
)

func topicExample() {
	git, err := gitlab.NewClient("yourtokengoeshere")
	if err != nil {
		log.Fatal(err)
	}

	// New topic
	topic, _, err := git.Topics.CreateTopic(&gitlab.CreateTopicOptions{
		Name:        gitlab.Ptr("My Topic 2"),
		Description: gitlab.Ptr("Some description"),
	})
	if err != nil {
		panic(err)
	}

	log.Printf("Topic: %+v\n", topic)

	// Set topic avatar
	avatarFile, err := os.Open("5746961_detect_direction_gps_location_map_icon.png")
	if err != nil {
		panic(err)
	}
	topic, _, err = git.Topics.UpdateTopic(topic.ID, &gitlab.UpdateTopicOptions{
		Avatar: &gitlab.TopicAvatar{
			Filename: "5746961_detect_direction_gps_location_map_icon.png",
			Image:    avatarFile,
		},
	})
	if err != nil {
		panic(err)
	}
	log.Printf("Topic with Avatar: %+v\n", topic)

	// Remove topic avatar
	topic, _, err = git.Topics.UpdateTopic(topic.ID, &gitlab.UpdateTopicOptions{
		Avatar: &gitlab.TopicAvatar{},
	})
	if err != nil {
		panic(err)
	}

	log.Printf("Topic without Avatar: %+v\n", topic)
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/examples/webhook.go000066400000000000000000000056151475761473200246710ustar00rootroot00000000000000package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"log"
	"net/http"
	"strings"

	"gitlab.com/gitlab-org/api/client-go"
)

// webhook is a HTTP Handler for Gitlab Webhook events.
type webhook struct {
	Secret         string
	EventsToAccept []gitlab.EventType
}

// webhookExample shows how to create a Webhook server to parse Gitlab events.
func webhookExample() {
	wh := webhook{
		Secret:         "your-gitlab-secret",
		EventsToAccept: []gitlab.EventType{gitlab.EventTypeMergeRequest, gitlab.EventTypePipeline},
	}

	mux := http.NewServeMux()
	mux.Handle("/webhook", wh)
	if err := http.ListenAndServe("0.0.0.0:8080", mux); err != nil {
		log.Fatalf("HTTP server ListenAndServe: %v", err)
	}
}

// ServeHTTP tries to parse Gitlab events sent and calls handle function
// with the successfully parsed events.
func (hook webhook) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
	event, err := hook.parse(request)
	if err != nil {
		writer.WriteHeader(500)
		fmt.Fprintf(writer, "could parse the webhook event: %v", err)
		return
	}

	// Handle the event before we return.
	if err := hook.handle(event); err != nil {
		writer.WriteHeader(500)
		fmt.Fprintf(writer, "error handling the event: %v", err)
		return
	}

	// Write a response when were done.
	writer.WriteHeader(204)
}

func (hook webhook) handle(event interface{}) error {
	str, err := json.Marshal(event)
	if err != nil {
		return fmt.Errorf("could not marshal json event for logging: %v", err)
	}

	// Just write the event for this example.
	fmt.Println(string(str))

	return nil
}

// parse verifies and parses the events specified in the request and
// returns the parsed event or an error.
func (hook webhook) parse(r *http.Request) (interface{}, error) {
	defer func() {
		if _, err := io.Copy(io.Discard, r.Body); err != nil {
			log.Printf("could discard request body: %v", err)
		}
		if err := r.Body.Close(); err != nil {
			log.Printf("could not close request body: %v", err)
		}
	}()

	if r.Method != http.MethodPost {
		return nil, errors.New("invalid HTTP Method")
	}

	// If we have a secret set, we should check if the request matches it.
	if len(hook.Secret) > 0 {
		signature := r.Header.Get("X-Gitlab-Token")
		if signature != hook.Secret {
			return nil, errors.New("token validation failed")
		}
	}

	event := r.Header.Get("X-Gitlab-Event")
	if strings.TrimSpace(event) == "" {
		return nil, errors.New("missing X-Gitlab-Event Header")
	}

	eventType := gitlab.EventType(event)
	if !isEventSubscribed(eventType, hook.EventsToAccept) {
		return nil, errors.New("event not defined to be parsed")
	}

	payload, err := io.ReadAll(r.Body)
	if err != nil || len(payload) == 0 {
		return nil, errors.New("error reading request body")
	}

	return gitlab.ParseWebhook(eventType, payload)
}

func isEventSubscribed(event gitlab.EventType, events []gitlab.EventType) bool {
	for _, e := range events {
		if event == e {
			return true
		}
	}
	return false
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/external_status_checks.go000066400000000000000000000170471475761473200261640ustar00rootroot00000000000000package gitlab

import (
	"fmt"
	"net/http"
	"time"
)

// ExternalStatusChecksService handles communication with the external
// status check related methods of the GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/status_checks.html
type ExternalStatusChecksService struct {
	client *Client
}

type MergeStatusCheck struct {
	ID          int    `json:"id"`
	Name        string `json:"name"`
	ExternalURL string `json:"external_url"`
	Status      string `json:"status"`
}

type ProjectStatusCheck struct {
	ID                int                          `json:"id"`
	Name              string                       `json:"name"`
	ProjectID         int                          `json:"project_id"`
	ExternalURL       string                       `json:"external_url"`
	ProtectedBranches []StatusCheckProtectedBranch `json:"protected_branches"`
}

type StatusCheckProtectedBranch struct {
	ID                        int        `json:"id"`
	ProjectID                 int        `json:"project_id"`
	Name                      string     `json:"name"`
	CreatedAt                 *time.Time `json:"created_at"`
	UpdatedAt                 *time.Time `json:"updated_at"`
	CodeOwnerApprovalRequired bool       `json:"code_owner_approval_required"`
}

// ListMergeStatusChecks lists the external status checks that apply to it
// and their status for a single merge request.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/status_checks.html#list-status-checks-for-a-merge-request
func (s *ExternalStatusChecksService) ListMergeStatusChecks(pid interface{}, mr int, opt *ListOptions, options ...RequestOptionFunc) ([]*MergeStatusCheck, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/merge_requests/%d/status_checks", PathEscape(project), mr)

	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var mscs []*MergeStatusCheck
	resp, err := s.client.Do(req, &mscs)
	if err != nil {
		return nil, resp, err
	}

	return mscs, resp, nil
}

// SetExternalStatusCheckStatusOptions represents the available
// SetExternalStatusCheckStatus() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/status_checks.html#set-status-of-an-external-status-check
type SetExternalStatusCheckStatusOptions struct {
	SHA                   *string `url:"sha,omitempty" json:"sha,omitempty"`
	ExternalStatusCheckID *int    `url:"external_status_check_id,omitempty" json:"external_status_check_id,omitempty"`
	Status                *string `url:"status,omitempty" json:"status,omitempty"`
}

// SetExternalStatusCheckStatus sets the status of an external status check.
//
// Gitlab API docs:
// https://docs.gitlab.com/ee/api/status_checks.html#set-status-of-an-external-status-check
func (s *ExternalStatusChecksService) SetExternalStatusCheckStatus(pid interface{}, mergeRequest int, opt *SetExternalStatusCheckStatusOptions, options ...RequestOptionFunc) (*Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("projects/%s/merge_requests/%d/status_check_responses", PathEscape(project), mergeRequest)

	req, err := s.client.NewRequest(http.MethodPost, u, opt, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}

// ListProjectStatusChecks lists the project external status checks.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/status_checks.html#get-project-external-status-checks
func (s *ExternalStatusChecksService) ListProjectStatusChecks(pid interface{}, opt *ListOptions, options ...RequestOptionFunc) ([]*ProjectStatusCheck, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/external_status_checks", PathEscape(project))

	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var pscs []*ProjectStatusCheck
	resp, err := s.client.Do(req, &pscs)
	if err != nil {
		return nil, resp, err
	}

	return pscs, resp, nil
}

// CreateExternalStatusCheckOptions represents the available
// CreateExternalStatusCheck() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/status_checks.html#create-external-status-check
type CreateExternalStatusCheckOptions struct {
	Name               *string `url:"name,omitempty" json:"name,omitempty"`
	ExternalURL        *string `url:"external_url,omitempty" json:"external_url,omitempty"`
	ProtectedBranchIDs *[]int  `url:"protected_branch_ids,omitempty" json:"protected_branch_ids,omitempty"`
}

// CreateExternalStatusCheck creates an external status check.
//
// Gitlab API docs:
// https://docs.gitlab.com/ee/api/status_checks.html#create-external-status-check
func (s *ExternalStatusChecksService) CreateExternalStatusCheck(pid interface{}, opt *CreateExternalStatusCheckOptions, options ...RequestOptionFunc) (*Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("projects/%s/external_status_checks", PathEscape(project))

	req, err := s.client.NewRequest(http.MethodPost, u, opt, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}

// DeleteExternalStatusCheck deletes an external status check.
//
// Gitlab API docs:
// https://docs.gitlab.com/ee/api/status_checks.html#delete-external-status-check
func (s *ExternalStatusChecksService) DeleteExternalStatusCheck(pid interface{}, check int, options ...RequestOptionFunc) (*Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("projects/%s/external_status_checks/%d", PathEscape(project), check)

	req, err := s.client.NewRequest(http.MethodDelete, u, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}

// UpdateExternalStatusCheckOptions represents the available
// UpdateExternalStatusCheck() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/status_checks.html#update-external-status-check
type UpdateExternalStatusCheckOptions struct {
	Name               *string `url:"name,omitempty" json:"name,omitempty"`
	ExternalURL        *string `url:"external_url,omitempty" json:"external_url,omitempty"`
	ProtectedBranchIDs *[]int  `url:"protected_branch_ids,omitempty" json:"protected_branch_ids,omitempty"`
}

// UpdateExternalStatusCheck updates an external status check.
//
// Gitlab API docs:
// https://docs.gitlab.com/ee/api/status_checks.html#update-external-status-check
func (s *ExternalStatusChecksService) UpdateExternalStatusCheck(pid interface{}, check int, opt *UpdateExternalStatusCheckOptions, options ...RequestOptionFunc) (*Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("projects/%s/external_status_checks/%d", PathEscape(project), check)

	req, err := s.client.NewRequest(http.MethodPut, u, opt, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}

// UpdateExternalStatusCheck updates an external status check.
//
// Gitlab API docs:
// https://docs.gitlab.com/ee/api/status_checks.html#retry-failed-status-check-for-a-merge-request
func (s *ExternalStatusChecksService) RetryFailedStatusCheckForAMergeRequest(pid interface{}, mergeRequest int, externalStatusCheck int, options ...RequestOptionFunc) (*Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("projects/%s/merge_requests/%d/status_checks/%d/retry", PathEscape(project), mergeRequest, externalStatusCheck)

	req, err := s.client.NewRequest(http.MethodPost, u, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/external_status_checks_test.go000066400000000000000000000051751475761473200272220ustar00rootroot00000000000000package gitlab

import (
	"fmt"
	"net/http"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
)

func TestListMergeStatusChecks(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/merge_requests/1/status_checks", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprint(w, exampleStatusChecks)
	})

	statusChecks, _, err := client.ExternalStatusChecks.ListMergeStatusChecks(1, 1, nil)
	if err != nil {
		t.Fatalf("ExternalStatusChecks.ListMergeStatusChecks returns an error: %v", err)
	}

	expectedStatusChecks := []*MergeStatusCheck{
		{
			ID:          2,
			Name:        "Rule 1",
			ExternalURL: "https://gitlab.com/test-endpoint",
			Status:      "approved",
		},
		{
			ID:          1,
			Name:        "Rule 2",
			ExternalURL: "https://gitlab.com/test-endpoint-2",
			Status:      "pending",
		},
	}

	assert.Equal(t, expectedStatusChecks, statusChecks)
}

func TestListProjectStatusChecks(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/external_status_checks", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprint(w, exampleProjectStatusChecks)
	})

	projectStatusChecks, _, err := client.ExternalStatusChecks.ListProjectStatusChecks(1, nil)
	if err != nil {
		t.Fatalf("ExternalStatusChecks.ListProjectStatusChecks returns an error: %v", err)
	}

	time1, err := time.Parse(time.RFC3339, "2020-10-12T14:04:50.787Z")
	if err != nil {
		t.Errorf("ExternalStatusChecks.ListProjectStatusChecks returns an error: %v", err)
	}

	expectedProjectStatusChecks := []*ProjectStatusCheck{
		{
			ID:          1,
			Name:        "Compliance Check",
			ProjectID:   6,
			ExternalURL: "https://gitlab.com/example/test.json",
			ProtectedBranches: []StatusCheckProtectedBranch{
				{
					ID:                        14,
					ProjectID:                 6,
					Name:                      "master",
					CreatedAt:                 &time1,
					UpdatedAt:                 &time1,
					CodeOwnerApprovalRequired: false,
				},
			},
		},
	}

	assert.Equal(t, expectedProjectStatusChecks, projectStatusChecks)
}

func TestRetryFailedStatusCheckForAMergeRequest(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/merge_requests/2/status_checks/3/retry", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		fmt.Fprint(w, `{"message": "202 Accepted"}`)
	})

	resp, err := client.ExternalStatusChecks.RetryFailedStatusCheckForAMergeRequest(1, 2, 3)
	if err != nil {
		t.Fatalf("ExternalStatusChecks.RetryFailedStatusCheckForAMergeRequest returns an error: %v", err)
	}

	assert.NotNil(t, resp)
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/feature_flags.go000066400000000000000000000047041475761473200242220ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"fmt"
	"net/http"
	"net/url"
)

// FeaturesService handles the communication with the application FeaturesService
// related methods of the GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/features.html
type FeaturesService struct {
	client *Client
}

// Feature represents a GitLab feature flag.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/features.html
type Feature struct {
	Name  string `json:"name"`
	State string `json:"state"`
	Gates []Gate
}

// Gate represents a gate of a GitLab feature flag.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/features.html
type Gate struct {
	Key   string      `json:"key"`
	Value interface{} `json:"value"`
}

func (f Feature) String() string {
	return Stringify(f)
}

// ListFeatures gets a list of feature flags
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/features.html#list-all-features
func (s *FeaturesService) ListFeatures(options ...RequestOptionFunc) ([]*Feature, *Response, error) {
	req, err := s.client.NewRequest(http.MethodGet, "features", nil, options)
	if err != nil {
		return nil, nil, err
	}

	var f []*Feature
	resp, err := s.client.Do(req, &f)
	if err != nil {
		return nil, resp, err
	}
	return f, resp, nil
}

// SetFeatureFlag sets or creates a feature flag gate
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/features.html#set-or-create-a-feature
func (s *FeaturesService) SetFeatureFlag(name string, value interface{}, options ...RequestOptionFunc) (*Feature, *Response, error) {
	u := fmt.Sprintf("features/%s", url.PathEscape(name))

	opt := struct {
		Value interface{} `url:"value" json:"value"`
	}{
		value,
	}

	req, err := s.client.NewRequest(http.MethodPost, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	f := &Feature{}
	resp, err := s.client.Do(req, f)
	if err != nil {
		return nil, resp, err
	}
	return f, resp, nil
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/feature_flags_test.go000066400000000000000000000045341475761473200252620ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"fmt"
	"net/http"
	"reflect"
	"testing"
)

func TestListFeatureFlags(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/features", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprint(w, `
		[
			{
			  "name": "experimental_feature",
			  "state": "off",
			  "gates": [
				{
				  "key": "boolean",
				  "value": false
				}
			  ]
			},
			{
			  "name": "new_library",
			  "state": "on"
			}
		  ]
	`)
	})

	features, _, err := client.Features.ListFeatures()
	if err != nil {
		t.Errorf("Features.ListFeatures returned error: %v", err)
	}

	want := []*Feature{
		{Name: "experimental_feature", State: "off", Gates: []Gate{
			{Key: "boolean", Value: false},
		}},
		{Name: "new_library", State: "on"},
	}
	if !reflect.DeepEqual(want, features) {
		t.Errorf("Features.ListFeatures returned %+v, want %+v", features, want)
	}
}

func TestSetFeatureFlag(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/features/new_library", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		fmt.Fprint(w, `
		{
			"name": "new_library",
			"state": "conditional",
			"gates": [
			  {
				"key": "boolean",
				"value": false
			  },
			  {
				"key": "percentage_of_time",
				"value": 30
			  }
			]
		  }
		`)
	})

	feature, _, err := client.Features.SetFeatureFlag("new_library", "30")
	if err != nil {
		t.Errorf("Features.SetFeatureFlag returned error: %v", err)
	}

	want := &Feature{
		Name:  "new_library",
		State: "conditional",
		Gates: []Gate{
			{Key: "boolean", Value: false},
			{Key: "percentage_of_time", Value: 30.0},
		},
	}
	if !reflect.DeepEqual(want, feature) {
		t.Errorf("Features.SetFeatureFlag returned %+v, want %+v", feature, want)
	}
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/fixtures_test.go000066400000000000000000000304521475761473200243220ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

const (
	// exampleChangelogResponse provides fixture for Changelog tests.
	exampleChangelogResponse = `{
    "notes": "## 1.0.0 (2021-11-17)\n\n### feature (2 changes)\n\n- [Title 2](namespace13/project13@ad608eb642124f5b3944ac0ac772fecaf570a6bf) ([merge request](namespace13/project13!2))\n- [Title 1](namespace13/project13@3c6b80ff7034fa0d585314e1571cc780596ce3c8) ([merge request](namespace13/project13!1))\n"
  }`

	// exampleCommitMessage provides fixture for a commit message.
	exampleCommitMessage = "Merge branch 'some-feature' into 'master'\n\nRelease v1.0.0\n\nSee merge request jsmith/example!1"

	// exampleCommitTitle provides fixture for a commit title.
	exampleCommitTitle = "Merge branch 'some-feature' into 'master'"

	// exampleDetailResponse provides fixture for Runners tests.
	exampleDetailResponse = `{
		"active": true,
		"architecture": null,
		"description": "test-1-20150125-test",
		"run_untagged": true,
		"id": 6,
		"is_shared": false,
		"runner_type": "project_type",
		"contacted_at": "2016-01-25T16:39:48.166Z",
		"name": null,
		"online": true,
		"status": "online",
		"platform": null,
		"projects": [
			{
				"id": 1,
				"name": "GitLab Community Edition",
				"name_with_namespace": "GitLab.org / GitLab Community Edition",
				"path": "gitlab-ce",
				"path_with_namespace": "gitlab-org/gitlab-ce"
			}
		],
		"token": "205086a8e3b9a2b818ffac9b89d102",
		"revision": null,
		"tag_list": [
			"ruby",
			"mysql"
		],
		"version": null,
		"access_level": "ref_protected",
		"maximum_timeout": 3600,
		"locked": false
	}`

	// exampleEventUserName provides a fixture for a event user's name.
	exampleEventUserName = "John Smith"

	// exampleEventUserUsername provides a ficture for the event username.
	exampleEventUserUsername = "jsmith"

	// exampleRunnerJob provides fixture for the list runner jobs test.
	exampleListRunnerJobs = `
  [
    {
      "id": 1,
      "status": "failed",
      "stage": "test",
      "name": "run_tests",
      "ref": "master",
      "tag": false,
      "coverage": null,
      "allow_failure": false,
      "created_at": "2021-10-22T11:59:25.201Z",
      "started_at": "2021-10-22T11:59:33.660Z",
      "finished_at": "2021-10-22T15:59:25.201Z",
      "duration": 171.540594,
      "queued_duration": 2.535766,
      "user": {
        "id": 368,
        "name": "John SMITH",
        "username": "john.smith",
        "state": "blocked",
        "avatar_url": "https://gitlab.example.com/uploads/-/system/user/avatar/368/avatar.png",
        "web_url": "https://gitlab.example.com/john.smith",
        "bio": "",
        "location": "",
        "public_email": "john.smith@example.com",
        "skype": "",
        "linkedin": "",
        "twitter": "",
        "website_url": "",
        "organization": "",
        "job_title": "",
        "pronouns": null,
        "bot": false,
        "work_information": null,
        "bio_html": ""
      },
      "commit": {
        "id": "6c016b801a88f4bd31f927fc045b5c746a6f823e",
        "short_id": "6c016b80",
        "created_at": "2018-03-21T14:41:00.000Z",
        "parent_ids": [
          "6008b4902d40799ab11688e502d9f1f27f6d2e18"
        ],
        "title": "Update env for specific runner",
        "message": "Update env for specific runner\n",
        "author_name": "John SMITH",
        "author_email": "john.smith@example.com",
        "authored_date": "2018-03-21T14:41:00.000Z",
        "committer_name": "John SMITH",
        "committer_email": "john.smith@example.com",
        "committed_date": "2018-03-21T14:41:00.000Z",
        "web_url": "https://gitlab.example.com/awesome/packages/common/-/commit/6c016b801a88f4bd31f927fc045b5c746a6f823e"
      },
      "pipeline": {
        "id": 8777,
        "project_id": 3252,
        "sha": "6c016b801a88f4bd31f927fc045b5c746a6f823e",
        "ref": "master",
        "status": "failed",
        "source": "push",
        "created_at": "2018-03-21T13:41:15.356Z",
        "updated_at": "2018-03-21T15:12:52.021Z",
        "web_url": "https://gitlab.example.com/awesome/packages/common/-/pipelines/8777"
      },
      "web_url": "https://gitlab.example.com/awesome/packages/common/-/jobs/14606",
      "project": {
        "id": 3252,
        "description": "Common nodejs paquet for producer",
        "name": "common",
        "name_with_namespace": "awesome",
        "path": "common",
        "path_with_namespace": "awesome",
        "created_at": "2018-02-13T09:21:48.107Z"
      }
    }
  ]`

	// exampleProjectName provides a fixture for a project name.
	exampleProjectName = "example-project"

	// exampleProjectStatusChecks provides a fixture for a project status checks.
	exampleProjectStatusChecks = `[
		{
			"id": 1,
			"name": "Compliance Check",
			"project_id": 6,
			"external_url": "https://gitlab.com/example/test.json",
			"protected_branches": [
				{
					"id": 14,
					"project_id": 6,
					"name": "master",
					"created_at": "2020-10-12T14:04:50.787Z",
					"updated_at": "2020-10-12T14:04:50.787Z",
					"code_owner_approval_required": false
				}
			]
		}
	]`

	// exampleRegisterNewRunner provides fixture for Runners tests.
	exampleRegisterNewRunner = `{
		"id": 12345,
		"token": "6337ff461c94fd3fa32ba3b1ff4125",
		"token_expires_at": "2016-01-25T16:39:48.166Z"
	}`

	// exampleReleaseLink provides fixture for Release Links tests.
	exampleReleaseLink = `{
		"id":1,
		"name":"awesome-v0.2.dmg",
		"url":"http://192.168.10.15:3000",
		"direct_asset_url": "http://192.168.10.15:3000/namespace/example/-/releases/v0.1/downloads/awesome-v0.2.dmg",
		"external":true,
		"link_type": "other"
	}`

	// exampleReleaseLinkList provides fixture for Release Links tests.
	exampleReleaseLinkList = `[
		{
			"id": 2,
			"name": "awesome-v0.2.msi",
			"url": "http://192.168.10.15:3000/msi",
			"external": true
		},
		{
			"id": 1,
			"name": "awesome-v0.2.dmg",
			"url": "http://192.168.10.15:3000",
			"direct_asset_url": "http://192.168.10.15:3000/namespace/example/-/releases/v0.1/downloads/awesome-v0.2.dmg",
			"external": false,
			"link_type": "other"
		}
	]`

	// exampleReleaseListResponse provides fixture for Releases tests.
	exampleReleaseListResponse = `[
		{
			"tag_name": "v0.2",
			"description": "description",
			"name": "Awesome app v0.2 beta",
			"description_html": "html",
			"created_at": "2019-01-03T01:56:19.539Z",
			"author": {
			"id": 1,
			"name": "Administrator",
			"username": "root",
			"state": "active",
			"avatar_url": "https://www.gravatar.com/avatar",
			"web_url": "http://localhost:3000/root"
			},
			"commit": {
			"id": "079e90101242458910cccd35eab0e211dfc359c0",
			"short_id": "079e9010",
			"title": "Update README.md",
			"created_at": "2019-01-03T01:55:38.000Z",
			"parent_ids": [
				"f8d3d94cbd347e924aa7b715845e439d00e80ca4"
			],
			"message": "Update README.md",
			"author_name": "Administrator",
			"author_email": "admin@example.com",
			"authored_date": "2019-01-03T01:55:38.000Z",
			"committer_name": "Administrator",
			"committer_email": "admin@example.com",
			"committed_date": "2019-01-03T01:55:38.000Z"
			},
			"assets": {
			"count": 4,
			"sources": [
				{
				"format": "zip",
				"url": "http://localhost:3000/archive/v0.2/awesome-app-v0.2.zip"
				},
				{
				"format": "tar.gz",
				"url": "http://localhost:3000/archive/v0.2/awesome-app-v0.2.tar.gz"
				}
			],
			"links": [
				{
				"id": 2,
				"name": "awesome-v0.2.msi",
				"url": "http://192.168.10.15:3000/msi",
				"external": true
				},
				{
				"id": 1,
				"name": "awesome-v0.2.dmg",
				"url": "http://192.168.10.15:3000",
				"external": true
				}
			]
			}
		},
		{
			"tag_name": "v0.1",
			"description": "description",
			"name": "Awesome app v0.1 alpha",
			"description_html": "description_html",
			"created_at": "2019-01-03T01:55:18.203Z",
			"author": {
			"id": 1,
			"name": "Administrator",
			"username": "root",
			"state": "active",
			"avatar_url": "https://www.gravatar.com/avatar",
			"web_url": "http://localhost:3000/root"
			},
			"commit": {
			"id": "f8d3d94cbd347e924aa7b715845e439d00e80ca4",
			"short_id": "f8d3d94c",
			"title": "Initial commit",
			"created_at": "2019-01-03T01:53:28.000Z",
			"parent_ids": [],
			"message": "Initial commit",
			"author_name": "Administrator",
			"author_email": "admin@example.com",
			"authored_date": "2019-01-03T01:53:28.000Z",
			"committer_name": "Administrator",
			"committer_email": "admin@example.com",
			"committed_date": "2019-01-03T01:53:28.000Z"
			},
			"assets": {
			"count": 2,
			"sources": [
				{
				"format": "zip",
				"url": "http://localhost:3000/archive/v0.1/awesome-app-v0.1.zip"
				},
				{
				"format": "tar.gz",
				"url": "http://localhost:3000/archive/v0.1/awesome-app-v0.1.tar.gz"
				}
			],
			"links": []
			}
		}
	]`

	// exampleReleaseName provides a fixture for a release name.
	exampleReleaseName = "awesome-v0.2.dmg"

	// exampleReleaseResponse provides fixture for Releases tests.
	exampleReleaseResponse = `{
		"tag_name": "v0.1",
		"description": "description",
		"name": "Awesome app v0.1 alpha",
		"description_html": "description_html",
		"created_at": "2019-01-03T01:55:18.203Z",
		"author": {
			"id": 1,
			"name": "Administrator",
			"username": "root",
			"state": "active",
			"avatar_url": "https://www.gravatar.com/avatar/",
			"web_url": "http://localhost:3000/root"
		},
		"commit": {
			"id": "f8d3d94cbd347e924aa7b715845e439d00e80ca4",
			"short_id": "f8d3d94c",
			"title": "Initial commit",
			"created_at": "2019-01-03T01:53:28.000Z",
			"parent_ids": [],
			"message": "Initial commit",
			"author_name": "Administrator",
			"author_email": "admin@example.com",
			"authored_date": "2019-01-03T01:53:28.000Z",
			"committer_name": "Administrator",
			"committer_email": "admin@example.com",
			"committed_date": "2019-01-03T01:53:28.000Z"
		},
		"assets": {
			"count": 2,
			"sources": [
			{
				"format": "zip",
				"url": "http://localhost:3000/archive/v0.1/awesome-app-v0.1.zip"
			},
			{
				"format": "tar.gz",
				"url": "http://localhost:3000/archive/v0.1/awesome-app-v0.1.tar.gz"
			}
			],
			"links": []
		}
	}`

	// exampleReleaseResponse provides fixture for Releases tests.
	exampleReleaseWithMetadataResponse = `{
		"tag_name": "v0.1.2+example-metadata",
		"description": "description",
		"name": "Awesome app v0.1 alpha",
		"description_html": "description_html",
		"created_at": "2019-01-03T01:55:18.203Z",
		"author": {
			"id": 1,
			"name": "Administrator",
			"username": "root",
			"state": "active",
			"avatar_url": "https://www.gravatar.com/avatar/",
			"web_url": "http://localhost:3000/root"
		},
		"commit": {
			"id": "f8d3d94cbd347e924aa7b715845e439d00e80ca4",
			"short_id": "f8d3d94c",
			"title": "Initial commit",
			"created_at": "2019-01-03T01:53:28.000Z",
			"parent_ids": [],
			"message": "Initial commit",
			"author_name": "Administrator",
			"author_email": "admin@example.com",
			"authored_date": "2019-01-03T01:53:28.000Z",
			"committer_name": "Administrator",
			"committer_email": "admin@example.com",
			"committed_date": "2019-01-03T01:53:28.000Z"
		},
		"assets": {
			"count": 2,
			"sources": [
			{
				"format": "zip",
				"url": "http://localhost:3000/archive/v0.1/awesome-app-v0.1.zip"
			},
			{
				"format": "tar.gz",
				"url": "http://localhost:3000/archive/v0.1/awesome-app-v0.1.tar.gz"
			}
			],
			"links": []
		}
	}`

	// exampleStatusChecks provides a fixture for status checks for a merge request.
	exampleStatusChecks = `[
    {
        "id": 2,
        "name": "Rule 1",
        "external_url": "https://gitlab.com/test-endpoint",
        "status": "approved"
    },
    {
        "id": 1,
        "name": "Rule 2",
        "external_url": "https://gitlab.com/test-endpoint-2",
        "status": "pending"
    }
	]`

	// exampleTagName provides a fixture for a tag name.
	exampleTagName = "v0.1"

	// exampleTagName provides a fixture for a tag name.
	exampleTagNameWithMetadata = "v0.1.2+example-metadata"
)
golang-gitlab-gitlab-org-api-client-go-0.123.0/freeze_periods.go000066400000000000000000000140161475761473200244150ustar00rootroot00000000000000//
// Copyright 2021 Paul Cioanca
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"fmt"
	"net/http"
	"time"
)

// FreezePeriodsService handles the communication with the freeze periods
// related methods of the GitLab API.
//
// https://docs.gitlab.com/ee/api/freeze_periods.html
type FreezePeriodsService struct {
	client *Client
}

// FreezePeriod represents a freeze period object.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/freeze_periods.html#list-freeze-periods
type FreezePeriod struct {
	ID           int        `json:"id"`
	FreezeStart  string     `json:"freeze_start"`
	FreezeEnd    string     `json:"freeze_end"`
	CronTimezone string     `json:"cron_timezone"`
	CreatedAt    *time.Time `json:"created_at"`
	UpdatedAt    *time.Time `json:"updated_at"`
}

// ListFreezePeriodsOptions represents the available ListFreezePeriodsOptions()
// options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/freeze_periods.html#list-freeze-periods
type ListFreezePeriodsOptions ListOptions

// ListFreezePeriods gets a list of project project freeze periods.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/freeze_periods.html#list-freeze-periods
func (s *FreezePeriodsService) ListFreezePeriods(pid interface{}, opt *ListFreezePeriodsOptions, options ...RequestOptionFunc) ([]*FreezePeriod, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/freeze_periods", PathEscape(project))

	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var fp []*FreezePeriod
	resp, err := s.client.Do(req, &fp)
	if err != nil {
		return nil, resp, err
	}

	return fp, resp, nil
}

// GetFreezePeriod gets a specific freeze period for a project.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/freeze_periods.html#get-a-freeze-period-by-a-freeze_period_id
func (s *FreezePeriodsService) GetFreezePeriod(pid interface{}, freezePeriod int, options ...RequestOptionFunc) (*FreezePeriod, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/freeze_periods/%d", PathEscape(project), freezePeriod)

	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	fp := new(FreezePeriod)
	resp, err := s.client.Do(req, fp)
	if err != nil {
		return nil, resp, err
	}

	return fp, resp, nil
}

// CreateFreezePeriodOptions represents the available CreateFreezePeriodOptions()
// options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/freeze_periods.html#create-a-freeze-period
type CreateFreezePeriodOptions struct {
	FreezeStart  *string `url:"freeze_start,omitempty" json:"freeze_start,omitempty"`
	FreezeEnd    *string `url:"freeze_end,omitempty" json:"freeze_end,omitempty"`
	CronTimezone *string `url:"cron_timezone,omitempty" json:"cron_timezone,omitempty"`
}

// CreateFreezePeriodOptions adds a freeze period to a specified project.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/freeze_periods.html#create-a-freeze-period
func (s *FreezePeriodsService) CreateFreezePeriodOptions(pid interface{}, opt *CreateFreezePeriodOptions, options ...RequestOptionFunc) (*FreezePeriod, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/freeze_periods", PathEscape(project))

	req, err := s.client.NewRequest(http.MethodPost, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	fp := new(FreezePeriod)
	resp, err := s.client.Do(req, fp)
	if err != nil {
		return nil, resp, err
	}

	return fp, resp, nil
}

// UpdateFreezePeriodOptions represents the available UpdateFreezePeriodOptions()
// options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/freeze_periods.html#update-a-freeze-period
type UpdateFreezePeriodOptions struct {
	FreezeStart  *string `url:"freeze_start,omitempty" json:"freeze_start,omitempty"`
	FreezeEnd    *string `url:"freeze_end,omitempty" json:"freeze_end,omitempty"`
	CronTimezone *string `url:"cron_timezone,omitempty" json:"cron_timezone,omitempty"`
}

// UpdateFreezePeriodOptions edits a freeze period for a specified project.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/freeze_periods.html#update-a-freeze-period
func (s *FreezePeriodsService) UpdateFreezePeriodOptions(pid interface{}, freezePeriod int, opt *UpdateFreezePeriodOptions, options ...RequestOptionFunc) (*FreezePeriod, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/freeze_periods/%d", PathEscape(project), freezePeriod)

	req, err := s.client.NewRequest(http.MethodPut, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	fp := new(FreezePeriod)
	resp, err := s.client.Do(req, fp)
	if err != nil {
		return nil, resp, err
	}

	return fp, resp, nil
}

// DeleteFreezePeriod removes a freeze period from a project. This is an
// idempotent method and can be called multiple times. Either the hook is
// available or not.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/freeze_periods.html#delete-a-freeze-period
func (s *FreezePeriodsService) DeleteFreezePeriod(pid interface{}, freezePeriod int, options ...RequestOptionFunc) (*Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("projects/%s/freeze_periods/%d", PathEscape(project), freezePeriod)

	req, err := s.client.NewRequest(http.MethodDelete, u, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/freeze_periods_test.go000066400000000000000000000135351475761473200254610ustar00rootroot00000000000000package gitlab

import (
	"fmt"
	"net/http"
	"testing"

	"github.com/stretchr/testify/require"
)

func TestFreezePeriodsService_ListFreezePeriods(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/19/freeze_periods", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
			[
			   {
				  "id":1,
				  "freeze_start":"0 23 * * 5",
				  "freeze_end":"0 8 * * 1",
				  "cron_timezone":"UTC"
			   }
			]
		`)
	})

	want := []*FreezePeriod{
		{
			ID:           1,
			FreezeStart:  "0 23 * * 5",
			FreezeEnd:    "0 8 * * 1",
			CronTimezone: "UTC",
		},
	}

	fps, resp, err := client.FreezePeriods.ListFreezePeriods(19, nil, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, fps)

	fps, resp, err = client.FreezePeriods.ListFreezePeriods(19.01, nil, nil)
	require.EqualError(t, err, "invalid ID type 19.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, fps)

	fps, resp, err = client.FreezePeriods.ListFreezePeriods(19, nil, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, fps)

	fps, resp, err = client.FreezePeriods.ListFreezePeriods(3, nil, nil)
	require.Error(t, err)
	require.Nil(t, fps)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestFreezePeriodsService_GetFreezePeriod(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/19/freeze_periods/1", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
		   {
			  "id":1,
			  "freeze_start":"0 23 * * 5",
			  "freeze_end":"0 8 * * 1",
			  "cron_timezone":"UTC"
		   }
		`)
	})

	want := &FreezePeriod{
		ID:           1,
		FreezeStart:  "0 23 * * 5",
		FreezeEnd:    "0 8 * * 1",
		CronTimezone: "UTC",
	}

	fp, resp, err := client.FreezePeriods.GetFreezePeriod(19, 1, nil, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, fp)

	fp, resp, err = client.FreezePeriods.GetFreezePeriod(19.01, 1, nil, nil)
	require.EqualError(t, err, "invalid ID type 19.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, fp)

	fp, resp, err = client.FreezePeriods.GetFreezePeriod(19, 1, nil, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, fp)

	fp, resp, err = client.FreezePeriods.GetFreezePeriod(3, 1, nil, nil)
	require.Error(t, err)
	require.Nil(t, fp)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestFreezePeriodsService_CreateFreezePeriodOptions(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/19/freeze_periods", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		fmt.Fprintf(w, `
		   {
			  "id":1,
			  "freeze_start":"0 23 * * 5",
			  "freeze_end":"0 8 * * 1",
			  "cron_timezone":"UTC"
		   }
		`)
	})

	want := &FreezePeriod{
		ID:           1,
		FreezeStart:  "0 23 * * 5",
		FreezeEnd:    "0 8 * * 1",
		CronTimezone: "UTC",
	}

	fp, resp, err := client.FreezePeriods.CreateFreezePeriodOptions(19, nil, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, fp)

	fp, resp, err = client.FreezePeriods.CreateFreezePeriodOptions(19.01, nil, nil)
	require.EqualError(t, err, "invalid ID type 19.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, fp)

	fp, resp, err = client.FreezePeriods.CreateFreezePeriodOptions(19, nil, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, fp)

	fp, resp, err = client.FreezePeriods.CreateFreezePeriodOptions(3, nil, nil)
	require.Error(t, err)
	require.Nil(t, fp)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestFreezePeriodsService_UpdateFreezePeriodOptions(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/19/freeze_periods/1", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPut)
		fmt.Fprintf(w, `
		   {
			  "id":1,
			  "freeze_start":"0 23 * * 5",
			  "freeze_end":"0 8 * * 1",
			  "cron_timezone":"UTC"
		   }
		`)
	})

	want := &FreezePeriod{
		ID:           1,
		FreezeStart:  "0 23 * * 5",
		FreezeEnd:    "0 8 * * 1",
		CronTimezone: "UTC",
	}

	fp, resp, err := client.FreezePeriods.UpdateFreezePeriodOptions(19, 1, nil, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, fp)

	fp, resp, err = client.FreezePeriods.UpdateFreezePeriodOptions(19.01, 1, nil, nil)
	require.EqualError(t, err, "invalid ID type 19.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, fp)

	fp, resp, err = client.FreezePeriods.UpdateFreezePeriodOptions(19, 1, nil, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, fp)

	fp, resp, err = client.FreezePeriods.UpdateFreezePeriodOptions(3, 1, nil, nil)
	require.Error(t, err)
	require.Nil(t, fp)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestFreezePeriodsService_DeleteFreezePeriod(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/19/freeze_periods/1", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodDelete)
	})

	resp, err := client.FreezePeriods.DeleteFreezePeriod(19, 1, nil, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)

	resp, err = client.FreezePeriods.DeleteFreezePeriod(19.01, 1, nil, nil)
	require.EqualError(t, err, "invalid ID type 19.01, the ID must be an int or a string")
	require.Nil(t, resp)

	resp, err = client.FreezePeriods.DeleteFreezePeriod(19, 1, nil, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)

	resp, err = client.FreezePeriods.DeleteFreezePeriod(3, 1, nil, nil)
	require.Error(t, err)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/generic_packages.go000066400000000000000000000120341475761473200246600ustar00rootroot00000000000000//
// Copyright 2021, Sune Keller
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"bytes"
	"fmt"
	"io"
	"net/http"
	"time"
)

// GenericPackagesService handles communication with the packages related
// methods of the GitLab API.
//
// GitLab docs:
// https://docs.gitlab.com/ee/user/packages/generic_packages/index.html
type GenericPackagesService struct {
	client *Client
}

// GenericPackagesFile represents a GitLab generic package file.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/user/packages/generic_packages/index.html#publish-a-package-file
type GenericPackagesFile struct {
	ID        int        `json:"id"`
	PackageID int        `json:"package_id"`
	CreatedAt *time.Time `json:"created_at"`
	UpdatedAt *time.Time `json:"updated_at"`
	Size      int        `json:"size"`
	FileStore int        `json:"file_store"`
	FileMD5   string     `json:"file_md5"`
	FileSHA1  string     `json:"file_sha1"`
	FileName  string     `json:"file_name"`
	File      struct {
		URL string `json:"url"`
	} `json:"file"`
	FileSHA256             string     `json:"file_sha256"`
	VerificationRetryAt    *time.Time `json:"verification_retry_at"`
	VerifiedAt             *time.Time `json:"verified_at"`
	VerificationFailure    bool       `json:"verification_failure"`
	VerificationRetryCount int        `json:"verification_retry_count"`
	VerificationChecksum   string     `json:"verification_checksum"`
	VerificationState      int        `json:"verification_state"`
	VerificationStartedAt  *time.Time `json:"verification_started_at"`
	NewFilePath            string     `json:"new_file_path"`
}

// FormatPackageURL returns the GitLab Package Registry URL for the given artifact metadata, without the BaseURL.
// This does not make a GitLab API request, but rather computes it based on their documentation.
func (s *GenericPackagesService) FormatPackageURL(pid interface{}, packageName, packageVersion, fileName string) (string, error) {
	project, err := parseID(pid)
	if err != nil {
		return "", err
	}
	u := fmt.Sprintf(
		"projects/%s/packages/generic/%s/%s/%s",
		PathEscape(project),
		PathEscape(packageName),
		PathEscape(packageVersion),
		PathEscape(fileName),
	)
	return u, nil
}

// PublishPackageFileOptions represents the available PublishPackageFile()
// options.
//
// GitLab docs:
// https://docs.gitlab.com/ee/user/packages/generic_packages/index.html#publish-a-package-file
type PublishPackageFileOptions struct {
	Status *GenericPackageStatusValue `url:"status,omitempty" json:"status,omitempty"`
	Select *GenericPackageSelectValue `url:"select,omitempty" json:"select,omitempty"`
}

// PublishPackageFile uploads a file to a project's package registry.
//
// GitLab docs:
// https://docs.gitlab.com/ee/user/packages/generic_packages/index.html#publish-a-package-file
func (s *GenericPackagesService) PublishPackageFile(pid interface{}, packageName, packageVersion, fileName string, content io.Reader, opt *PublishPackageFileOptions, options ...RequestOptionFunc) (*GenericPackagesFile, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf(
		"projects/%s/packages/generic/%s/%s/%s",
		PathEscape(project),
		PathEscape(packageName),
		PathEscape(packageVersion),
		PathEscape(fileName),
	)

	// We need to create the request as a GET request to make sure the options
	// are set correctly. After the request is created we will overwrite both
	// the method and the body.
	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	// Overwrite the method and body.
	req.Method = http.MethodPut
	req.SetBody(content)

	f := new(GenericPackagesFile)
	resp, err := s.client.Do(req, f)
	if err != nil {
		return nil, resp, err
	}

	return f, resp, nil
}

// DownloadPackageFile allows you to download the package file.
//
// GitLab docs:
// https://docs.gitlab.com/ee/user/packages/generic_packages/index.html#download-package-file
func (s *GenericPackagesService) DownloadPackageFile(pid interface{}, packageName, packageVersion, fileName string, options ...RequestOptionFunc) ([]byte, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf(
		"projects/%s/packages/generic/%s/%s/%s",
		PathEscape(project),
		PathEscape(packageName),
		PathEscape(packageVersion),
		PathEscape(fileName),
	)

	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	var f bytes.Buffer
	resp, err := s.client.Do(req, &f)
	if err != nil {
		return nil, resp, err
	}

	return f.Bytes(), resp, err
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/generic_packages_test.go000066400000000000000000000035351475761473200257250ustar00rootroot00000000000000//
// Copyright 2021, Sune Keller
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"fmt"
	"net/http"
	"reflect"
	"strings"
	"testing"
)

func TestPublishPackageFile(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1234/packages/generic/foo/0.1.2/bar-baz.txt", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPut)
		fmt.Fprint(w, `
		{
			"message": "201 Created"
		}
	`)
	})

	_, _, err := client.GenericPackages.PublishPackageFile(1234, "foo", "0.1.2", "bar-baz.txt", strings.NewReader("bar = baz"), &PublishPackageFileOptions{})
	if err != nil {
		t.Errorf("GenericPackages.PublishPackageFile returned error: %v", err)
	}
}

func TestDownloadPackageFile(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1234/packages/generic/foo/0.1.2/bar-baz.txt", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprint(w, strings.TrimSpace(`
		bar = baz
	`))
	})

	packageBytes, _, err := client.GenericPackages.DownloadPackageFile(1234, "foo", "0.1.2", "bar-baz.txt")
	if err != nil {
		t.Errorf("GenericPackages.DownloadPackageFile returned error: %v", err)
	}

	want := []byte("bar = baz")
	if !reflect.DeepEqual(want, packageBytes) {
		t.Errorf("GenericPackages.DownloadPackageFile returned %+v, want %+v", packageBytes, want)
	}
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/geo_nodes.go000066400000000000000000000630131475761473200233530ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"fmt"
	"net/http"
)

// GeoNode represents a GitLab Geo Node.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/geo_nodes.html
type GeoNode struct {
	ID                               int          `json:"id"`
	Name                             string       `json:"name"`
	URL                              string       `json:"url"`
	InternalURL                      string       `json:"internal_url"`
	Primary                          bool         `json:"primary"`
	Enabled                          bool         `json:"enabled"`
	Current                          bool         `json:"current"`
	FilesMaxCapacity                 int          `json:"files_max_capacity"`
	ReposMaxCapacity                 int          `json:"repos_max_capacity"`
	VerificationMaxCapacity          int          `json:"verification_max_capacity"`
	SelectiveSyncType                string       `json:"selective_sync_type"`
	SelectiveSyncShards              []string     `json:"selective_sync_shards"`
	SelectiveSyncNamespaceIds        []int        `json:"selective_sync_namespace_ids"`
	MinimumReverificationInterval    int          `json:"minimum_reverification_interval"`
	ContainerRepositoriesMaxCapacity int          `json:"container_repositories_max_capacity"`
	SyncObjectStorage                bool         `json:"sync_object_storage"`
	CloneProtocol                    string       `json:"clone_protocol"`
	WebEditURL                       string       `json:"web_edit_url"`
	WebGeoProjectsURL                string       `json:"web_geo_projects_url"`
	Links                            GeoNodeLinks `json:"_links"`
}

// GeoNodeLinks represents links for GitLab GeoNode.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/geo_nodes.html
type GeoNodeLinks struct {
	Self   string `json:"self"`
	Status string `json:"status"`
	Repair string `json:"repair"`
}

// GeoNodesService handles communication with Geo Nodes related methods
// of GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/geo_nodes.html
type GeoNodesService struct {
	client *Client
}

// CreateGeoNodesOptions represents the available CreateGeoNode() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/geo_nodes.html#create-a-new-geo-node
type CreateGeoNodesOptions struct {
	Primary                          *bool     `url:"primary,omitempty" json:"primary,omitempty"`
	Enabled                          *bool     `url:"enabled,omitempty" json:"enabled,omitempty"`
	Name                             *string   `url:"name,omitempty" json:"name,omitempty"`
	URL                              *string   `url:"url,omitempty" json:"url,omitempty"`
	InternalURL                      *string   `url:"internal_url,omitempty" json:"internal_url,omitempty"`
	FilesMaxCapacity                 *int      `url:"files_max_capacity,omitempty" json:"files_max_capacity,omitempty"`
	ReposMaxCapacity                 *int      `url:"repos_max_capacity,omitempty" json:"repos_max_capacity,omitempty"`
	VerificationMaxCapacity          *int      `url:"verification_max_capacity,omitempty" json:"verification_max_capacity,omitempty"`
	ContainerRepositoriesMaxCapacity *int      `url:"container_repositories_max_capacity,omitempty" json:"container_repositories_max_capacity,omitempty"`
	SyncObjectStorage                *bool     `url:"sync_object_storage,omitempty" json:"sync_object_storage,omitempty"`
	SelectiveSyncType                *string   `url:"selective_sync_type,omitempty" json:"selective_sync_type,omitempty"`
	SelectiveSyncShards              *[]string `url:"selective_sync_shards,omitempty" json:"selective_sync_shards,omitempty"`
	SelectiveSyncNamespaceIds        *[]int    `url:"selective_sync_namespace_ids,omitempty" json:"selective_sync_namespace_ids,omitempty"`
	MinimumReverificationInterval    *int      `url:"minimum_reverification_interval,omitempty" json:"minimum_reverification_interval,omitempty"`
}

// CreateGeoNode creates a new Geo Node.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/geo_nodes.html#create-a-new-geo-node
func (s *GeoNodesService) CreateGeoNode(opt *CreateGeoNodesOptions, options ...RequestOptionFunc) (*GeoNode, *Response, error) {
	req, err := s.client.NewRequest(http.MethodPost, "geo_nodes", opt, options)
	if err != nil {
		return nil, nil, err
	}

	g := new(GeoNode)
	resp, err := s.client.Do(req, g)
	if err != nil {
		return nil, resp, err
	}

	return g, resp, nil
}

// ListGeoNodesOptions represents the available ListGeoNodes() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/geo_nodes.html#retrieve-configuration-about-all-geo-nodes
type ListGeoNodesOptions ListOptions

// ListGeoNodes gets a list of geo nodes.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/geo_nodes.html#retrieve-configuration-about-all-geo-nodes
func (s *GeoNodesService) ListGeoNodes(opt *ListGeoNodesOptions, options ...RequestOptionFunc) ([]*GeoNode, *Response, error) {
	req, err := s.client.NewRequest(http.MethodGet, "geo_nodes", opt, options)
	if err != nil {
		return nil, nil, err
	}

	var gs []*GeoNode
	resp, err := s.client.Do(req, &gs)
	if err != nil {
		return nil, resp, err
	}

	return gs, resp, nil
}

// GetGeoNode gets a specific geo node.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/geo_nodes.html#retrieve-configuration-about-a-specific-geo-node
func (s *GeoNodesService) GetGeoNode(id int, options ...RequestOptionFunc) (*GeoNode, *Response, error) {
	u := fmt.Sprintf("geo_nodes/%d", id)

	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	g := new(GeoNode)
	resp, err := s.client.Do(req, g)
	if err != nil {
		return nil, resp, err
	}

	return g, resp, nil
}

// UpdateGeoNodesOptions represents the available EditGeoNode() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/geo_nodes.html#edit-a-geo-node
type UpdateGeoNodesOptions struct {
	ID                               *int      `url:"primary,omitempty" json:"primary,omitempty"`
	Enabled                          *bool     `url:"enabled,omitempty" json:"enabled,omitempty"`
	Name                             *string   `url:"name,omitempty" json:"name,omitempty"`
	URL                              *string   `url:"url,omitempty" json:"url,omitempty"`
	InternalURL                      *string   `url:"internal_url,omitempty" json:"internal_url,omitempty"`
	FilesMaxCapacity                 *int      `url:"files_max_capacity,omitempty" json:"files_max_capacity,omitempty"`
	ReposMaxCapacity                 *int      `url:"repos_max_capacity,omitempty" json:"repos_max_capacity,omitempty"`
	VerificationMaxCapacity          *int      `url:"verification_max_capacity,omitempty" json:"verification_max_capacity,omitempty"`
	ContainerRepositoriesMaxCapacity *int      `url:"container_repositories_max_capacity,omitempty" json:"container_repositories_max_capacity,omitempty"`
	SyncObjectStorage                *bool     `url:"sync_object_storage,omitempty" json:"sync_object_storage,omitempty"`
	SelectiveSyncType                *string   `url:"selective_sync_type,omitempty" json:"selective_sync_type,omitempty"`
	SelectiveSyncShards              *[]string `url:"selective_sync_shards,omitempty" json:"selective_sync_shards,omitempty"`
	SelectiveSyncNamespaceIds        *[]int    `url:"selective_sync_namespace_ids,omitempty" json:"selective_sync_namespace_ids,omitempty"`
	MinimumReverificationInterval    *int      `url:"minimum_reverification_interval,omitempty" json:"minimum_reverification_interval,omitempty"`
}

// EditGeoNode updates settings of an existing Geo node.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/geo_nodes.html#edit-a-geo-node
func (s *GeoNodesService) EditGeoNode(id int, opt *UpdateGeoNodesOptions, options ...RequestOptionFunc) (*GeoNode, *Response, error) {
	u := fmt.Sprintf("geo_nodes/%d", id)

	req, err := s.client.NewRequest(http.MethodPut, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	g := new(GeoNode)
	resp, err := s.client.Do(req, g)
	if err != nil {
		return nil, resp, err
	}

	return g, resp, nil
}

// DeleteGeoNode removes the Geo node.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/geo_nodes.html#delete-a-geo-node
func (s *GeoNodesService) DeleteGeoNode(id int, options ...RequestOptionFunc) (*Response, error) {
	u := fmt.Sprintf("geo_nodes/%d", id)

	req, err := s.client.NewRequest(http.MethodDelete, u, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}

// RepairGeoNode to repair the OAuth authentication of a Geo node.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/geo_nodes.html#repair-a-geo-node
func (s *GeoNodesService) RepairGeoNode(id int, options ...RequestOptionFunc) (*GeoNode, *Response, error) {
	u := fmt.Sprintf("geo_nodes/%d/repair", id)

	req, err := s.client.NewRequest(http.MethodPost, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	g := new(GeoNode)
	resp, err := s.client.Do(req, g)
	if err != nil {
		return nil, resp, err
	}

	return g, resp, nil
}

// GeoNodeStatus represents the status of Geo Node.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/geo_nodes.html#retrieve-status-about-all-geo-nodes
type GeoNodeStatus struct {
	GeoNodeID                                     int    `json:"geo_node_id"`
	Healthy                                       bool   `json:"healthy"`
	Health                                        string `json:"health"`
	HealthStatus                                  string `json:"health_status"`
	MissingOauthApplication                       bool   `json:"missing_oauth_application"`
	AttachmentsCount                              int    `json:"attachments_count"`
	AttachmentsSyncedCount                        int    `json:"attachments_synced_count"`
	AttachmentsFailedCount                        int    `json:"attachments_failed_count"`
	AttachmentsSyncedMissingOnPrimaryCount        int    `json:"attachments_synced_missing_on_primary_count"`
	AttachmentsSyncedInPercentage                 string `json:"attachments_synced_in_percentage"`
	DbReplicationLagSeconds                       int    `json:"db_replication_lag_seconds"`
	LfsObjectsCount                               int    `json:"lfs_objects_count"`
	LfsObjectsSyncedCount                         int    `json:"lfs_objects_synced_count"`
	LfsObjectsFailedCount                         int    `json:"lfs_objects_failed_count"`
	LfsObjectsSyncedMissingOnPrimaryCount         int    `json:"lfs_objects_synced_missing_on_primary_count"`
	LfsObjectsSyncedInPercentage                  string `json:"lfs_objects_synced_in_percentage"`
	JobArtifactsCount                             int    `json:"job_artifacts_count"`
	JobArtifactsSyncedCount                       int    `json:"job_artifacts_synced_count"`
	JobArtifactsFailedCount                       int    `json:"job_artifacts_failed_count"`
	JobArtifactsSyncedMissingOnPrimaryCount       int    `json:"job_artifacts_synced_missing_on_primary_count"`
	JobArtifactsSyncedInPercentage                string `json:"job_artifacts_synced_in_percentage"`
	ContainerRepositoriesCount                    int    `json:"container_repositories_count"`
	ContainerRepositoriesSyncedCount              int    `json:"container_repositories_synced_count"`
	ContainerRepositoriesFailedCount              int    `json:"container_repositories_failed_count"`
	ContainerRepositoriesSyncedInPercentage       string `json:"container_repositories_synced_in_percentage"`
	DesignRepositoriesCount                       int    `json:"design_repositories_count"`
	DesignRepositoriesSyncedCount                 int    `json:"design_repositories_synced_count"`
	DesignRepositoriesFailedCount                 int    `json:"design_repositories_failed_count"`
	DesignRepositoriesSyncedInPercentage          string `json:"design_repositories_synced_in_percentage"`
	ProjectsCount                                 int    `json:"projects_count"`
	RepositoriesCount                             int    `json:"repositories_count"`
	RepositoriesFailedCount                       int    `json:"repositories_failed_count"`
	RepositoriesSyncedCount                       int    `json:"repositories_synced_count"`
	RepositoriesSyncedInPercentage                string `json:"repositories_synced_in_percentage"`
	WikisCount                                    int    `json:"wikis_count"`
	WikisFailedCount                              int    `json:"wikis_failed_count"`
	WikisSyncedCount                              int    `json:"wikis_synced_count"`
	WikisSyncedInPercentage                       string `json:"wikis_synced_in_percentage"`
	ReplicationSlotsCount                         int    `json:"replication_slots_count"`
	ReplicationSlotsUsedCount                     int    `json:"replication_slots_used_count"`
	ReplicationSlotsUsedInPercentage              string `json:"replication_slots_used_in_percentage"`
	ReplicationSlotsMaxRetainedWalBytes           int    `json:"replication_slots_max_retained_wal_bytes"`
	RepositoriesCheckedCount                      int    `json:"repositories_checked_count"`
	RepositoriesCheckedFailedCount                int    `json:"repositories_checked_failed_count"`
	RepositoriesCheckedInPercentage               string `json:"repositories_checked_in_percentage"`
	RepositoriesChecksummedCount                  int    `json:"repositories_checksummed_count"`
	RepositoriesChecksumFailedCount               int    `json:"repositories_checksum_failed_count"`
	RepositoriesChecksummedInPercentage           string `json:"repositories_checksummed_in_percentage"`
	WikisChecksummedCount                         int    `json:"wikis_checksummed_count"`
	WikisChecksumFailedCount                      int    `json:"wikis_checksum_failed_count"`
	WikisChecksummedInPercentage                  string `json:"wikis_checksummed_in_percentage"`
	RepositoriesVerifiedCount                     int    `json:"repositories_verified_count"`
	RepositoriesVerificationFailedCount           int    `json:"repositories_verification_failed_count"`
	RepositoriesVerifiedInPercentage              string `json:"repositories_verified_in_percentage"`
	RepositoriesChecksumMismatchCount             int    `json:"repositories_checksum_mismatch_count"`
	WikisVerifiedCount                            int    `json:"wikis_verified_count"`
	WikisVerificationFailedCount                  int    `json:"wikis_verification_failed_count"`
	WikisVerifiedInPercentage                     string `json:"wikis_verified_in_percentage"`
	WikisChecksumMismatchCount                    int    `json:"wikis_checksum_mismatch_count"`
	RepositoriesRetryingVerificationCount         int    `json:"repositories_retrying_verification_count"`
	WikisRetryingVerificationCount                int    `json:"wikis_retrying_verification_count"`
	LastEventID                                   int    `json:"last_event_id"`
	LastEventTimestamp                            int    `json:"last_event_timestamp"`
	CursorLastEventID                             int    `json:"cursor_last_event_id"`
	CursorLastEventTimestamp                      int    `json:"cursor_last_event_timestamp"`
	LastSuccessfulStatusCheckTimestamp            int    `json:"last_successful_status_check_timestamp"`
	Version                                       string `json:"version"`
	Revision                                      string `json:"revision"`
	MergeRequestDiffsCount                        int    `json:"merge_request_diffs_count"`
	MergeRequestDiffsChecksumTotalCount           int    `json:"merge_request_diffs_checksum_total_count"`
	MergeRequestDiffsChecksummedCount             int    `json:"merge_request_diffs_checksummed_count"`
	MergeRequestDiffsChecksumFailedCount          int    `json:"merge_request_diffs_checksum_failed_count"`
	MergeRequestDiffsSyncedCount                  int    `json:"merge_request_diffs_synced_count"`
	MergeRequestDiffsFailedCount                  int    `json:"merge_request_diffs_failed_count"`
	MergeRequestDiffsRegistryCount                int    `json:"merge_request_diffs_registry_count"`
	MergeRequestDiffsVerificationTotalCount       int    `json:"merge_request_diffs_verification_total_count"`
	MergeRequestDiffsVerifiedCount                int    `json:"merge_request_diffs_verified_count"`
	MergeRequestDiffsVerificationFailedCount      int    `json:"merge_request_diffs_verification_failed_count"`
	MergeRequestDiffsSyncedInPercentage           string `json:"merge_request_diffs_synced_in_percentage"`
	MergeRequestDiffsVerifiedInPercentage         string `json:"merge_request_diffs_verified_in_percentage"`
	PackageFilesCount                             int    `json:"package_files_count"`
	PackageFilesChecksumTotalCount                int    `json:"package_files_checksum_total_count"`
	PackageFilesChecksummedCount                  int    `json:"package_files_checksummed_count"`
	PackageFilesChecksumFailedCount               int    `json:"package_files_checksum_failed_count"`
	PackageFilesSyncedCount                       int    `json:"package_files_synced_count"`
	PackageFilesFailedCount                       int    `json:"package_files_failed_count"`
	PackageFilesRegistryCount                     int    `json:"package_files_registry_count"`
	PackageFilesVerificationTotalCount            int    `json:"package_files_verification_total_count"`
	PackageFilesVerifiedCount                     int    `json:"package_files_verified_count"`
	PackageFilesVerificationFailedCount           int    `json:"package_files_verification_failed_count"`
	PackageFilesSyncedInPercentage                string `json:"package_files_synced_in_percentage"`
	PackageFilesVerifiedInPercentage              string `json:"package_files_verified_in_percentage"`
	PagesDeploymentsCount                         int    `json:"pages_deployments_count"`
	PagesDeploymentsChecksumTotalCount            int    `json:"pages_deployments_checksum_total_count"`
	PagesDeploymentsChecksummedCount              int    `json:"pages_deployments_checksummed_count"`
	PagesDeploymentsChecksumFailedCount           int    `json:"pages_deployments_checksum_failed_count"`
	PagesDeploymentsSyncedCount                   int    `json:"pages_deployments_synced_count"`
	PagesDeploymentsFailedCount                   int    `json:"pages_deployments_failed_count"`
	PagesDeploymentsRegistryCount                 int    `json:"pages_deployments_registry_count"`
	PagesDeploymentsVerificationTotalCount        int    `json:"pages_deployments_verification_total_count"`
	PagesDeploymentsVerifiedCount                 int    `json:"pages_deployments_verified_count"`
	PagesDeploymentsVerificationFailedCount       int    `json:"pages_deployments_verification_failed_count"`
	PagesDeploymentsSyncedInPercentage            string `json:"pages_deployments_synced_in_percentage"`
	PagesDeploymentsVerifiedInPercentage          string `json:"pages_deployments_verified_in_percentage"`
	TerraformStateVersionsCount                   int    `json:"terraform_state_versions_count"`
	TerraformStateVersionsChecksumTotalCount      int    `json:"terraform_state_versions_checksum_total_count"`
	TerraformStateVersionsChecksummedCount        int    `json:"terraform_state_versions_checksummed_count"`
	TerraformStateVersionsChecksumFailedCount     int    `json:"terraform_state_versions_checksum_failed_count"`
	TerraformStateVersionsSyncedCount             int    `json:"terraform_state_versions_synced_count"`
	TerraformStateVersionsFailedCount             int    `json:"terraform_state_versions_failed_count"`
	TerraformStateVersionsRegistryCount           int    `json:"terraform_state_versions_registry_count"`
	TerraformStateVersionsVerificationTotalCount  int    `json:"terraform_state_versions_verification_total_count"`
	TerraformStateVersionsVerifiedCount           int    `json:"terraform_state_versions_verified_count"`
	TerraformStateVersionsVerificationFailedCount int    `json:"terraform_state_versions_verification_failed_count"`
	TerraformStateVersionsSyncedInPercentage      string `json:"terraform_state_versions_synced_in_percentage"`
	TerraformStateVersionsVerifiedInPercentage    string `json:"terraform_state_versions_verified_in_percentage"`
	SnippetRepositoriesCount                      int    `json:"snippet_repositories_count"`
	SnippetRepositoriesChecksumTotalCount         int    `json:"snippet_repositories_checksum_total_count"`
	SnippetRepositoriesChecksummedCount           int    `json:"snippet_repositories_checksummed_count"`
	SnippetRepositoriesChecksumFailedCount        int    `json:"snippet_repositories_checksum_failed_count"`
	SnippetRepositoriesSyncedCount                int    `json:"snippet_repositories_synced_count"`
	SnippetRepositoriesFailedCount                int    `json:"snippet_repositories_failed_count"`
	SnippetRepositoriesRegistryCount              int    `json:"snippet_repositories_registry_count"`
	SnippetRepositoriesVerificationTotalCount     int    `json:"snippet_repositories_verification_total_count"`
	SnippetRepositoriesVerifiedCount              int    `json:"snippet_repositories_verified_count"`
	SnippetRepositoriesVerificationFailedCount    int    `json:"snippet_repositories_verification_failed_count"`
	SnippetRepositoriesSyncedInPercentage         string `json:"snippet_repositories_synced_in_percentage"`
	SnippetRepositoriesVerifiedInPercentage       string `json:"snippet_repositories_verified_in_percentage"`
	GroupWikiRepositoriesCount                    int    `json:"group_wiki_repositories_count"`
	GroupWikiRepositoriesChecksumTotalCount       int    `json:"group_wiki_repositories_checksum_total_count"`
	GroupWikiRepositoriesChecksummedCount         int    `json:"group_wiki_repositories_checksummed_count"`
	GroupWikiRepositoriesChecksumFailedCount      int    `json:"group_wiki_repositories_checksum_failed_count"`
	GroupWikiRepositoriesSyncedCount              int    `json:"group_wiki_repositories_synced_count"`
	GroupWikiRepositoriesFailedCount              int    `json:"group_wiki_repositories_failed_count"`
	GroupWikiRepositoriesRegistryCount            int    `json:"group_wiki_repositories_registry_count"`
	GroupWikiRepositoriesVerificationTotalCount   int    `json:"group_wiki_repositories_verification_total_count"`
	GroupWikiRepositoriesVerifiedCount            int    `json:"group_wiki_repositories_verified_count"`
	GroupWikiRepositoriesVerificationFailedCount  int    `json:"group_wiki_repositories_verification_failed_count"`
	GroupWikiRepositoriesSyncedInPercentage       string `json:"group_wiki_repositories_synced_in_percentage"`
	GroupWikiRepositoriesVerifiedInPercentage     string `json:"group_wiki_repositories_verified_in_percentage"`
	PipelineArtifactsCount                        int    `json:"pipeline_artifacts_count"`
	PipelineArtifactsChecksumTotalCount           int    `json:"pipeline_artifacts_checksum_total_count"`
	PipelineArtifactsChecksummedCount             int    `json:"pipeline_artifacts_checksummed_count"`
	PipelineArtifactsChecksumFailedCount          int    `json:"pipeline_artifacts_checksum_failed_count"`
	PipelineArtifactsSyncedCount                  int    `json:"pipeline_artifacts_synced_count"`
	PipelineArtifactsFailedCount                  int    `json:"pipeline_artifacts_failed_count"`
	PipelineArtifactsRegistryCount                int    `json:"pipeline_artifacts_registry_count"`
	PipelineArtifactsVerificationTotalCount       int    `json:"pipeline_artifacts_verification_total_count"`
	PipelineArtifactsVerifiedCount                int    `json:"pipeline_artifacts_verified_count"`
	PipelineArtifactsVerificationFailedCount      int    `json:"pipeline_artifacts_verification_failed_count"`
	PipelineArtifactsSyncedInPercentage           string `json:"pipeline_artifacts_synced_in_percentage"`
	PipelineArtifactsVerifiedInPercentage         string `json:"pipeline_artifacts_verified_in_percentage"`
	UploadsCount                                  int    `json:"uploads_count"`
	UploadsSyncedCount                            int    `json:"uploads_synced_count"`
	UploadsFailedCount                            int    `json:"uploads_failed_count"`
	UploadsRegistryCount                          int    `json:"uploads_registry_count"`
	UploadsSyncedInPercentage                     string `json:"uploads_synced_in_percentage"`
}

// RetrieveStatusOfAllGeoNodes get the list of status of all Geo Nodes.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/geo_nodes.html#retrieve-status-about-all-geo-nodes
func (s *GeoNodesService) RetrieveStatusOfAllGeoNodes(options ...RequestOptionFunc) ([]*GeoNodeStatus, *Response, error) {
	req, err := s.client.NewRequest(http.MethodGet, "geo_nodes/status", nil, options)
	if err != nil {
		return nil, nil, err
	}

	var gnss []*GeoNodeStatus
	resp, err := s.client.Do(req, &gnss)
	if err != nil {
		return nil, resp, err
	}

	return gnss, resp, nil
}

// RetrieveStatusOfGeoNode get the of status of a specific Geo Nodes.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/geo_nodes.html#retrieve-status-about-a-specific-geo-node
func (s *GeoNodesService) RetrieveStatusOfGeoNode(id int, options ...RequestOptionFunc) (*GeoNodeStatus, *Response, error) {
	u := fmt.Sprintf("geo_nodes/%d/status", id)

	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	gns := new(GeoNodeStatus)
	resp, err := s.client.Do(req, gns)
	if err != nil {
		return nil, resp, err
	}

	return gns, resp, nil
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/geo_nodes_test.go000066400000000000000000000633261475761473200244210ustar00rootroot00000000000000package gitlab

import (
	"fmt"
	"net/http"
	"testing"

	"github.com/stretchr/testify/require"
)

func TestGeoNodesService_CreateGeoNode(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/geo_nodes", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		fmt.Fprintf(w, `
			{
			  "id": 3,
			  "name": "Test Node 1",
			  "url": "https://secondary.example.com/",
			  "internal_url": "https://secondary.example.com/",
			  "primary": false,
			  "enabled": true,
			  "current": false,
			  "files_max_capacity": 10,
			  "repos_max_capacity": 25,
			  "verification_max_capacity": 100,
			  "selective_sync_type": "namespaces",
			  "selective_sync_shards": null,
			  "selective_sync_namespace_ids": [1, 25],
			  "minimum_reverification_interval": 7,
			  "container_repositories_max_capacity": 10,
			  "sync_object_storage": false,
			  "clone_protocol": "http",
			  "web_edit_url": "https://primary.example.com/admin/geo/nodes/3/edit",
			  "web_geo_projects_url": "http://secondary.example.com/admin/geo/projects",
			  "_links": {
				 "self": "https://primary.example.com/api/v4/geo_nodes/3",
				 "status": "https://primary.example.com/api/v4/geo_nodes/3/status",
				 "repair": "https://primary.example.com/api/v4/geo_nodes/3/repair"
			  }
			}
		`)
	})

	want := &GeoNode{
		ID:                               3,
		Name:                             "Test Node 1",
		URL:                              "https://secondary.example.com/",
		InternalURL:                      "https://secondary.example.com/",
		Primary:                          false,
		Enabled:                          true,
		Current:                          false,
		FilesMaxCapacity:                 10,
		ReposMaxCapacity:                 25,
		VerificationMaxCapacity:          100,
		SelectiveSyncType:                "namespaces",
		SelectiveSyncShards:              nil,
		SelectiveSyncNamespaceIds:        []int{1, 25},
		MinimumReverificationInterval:    7,
		ContainerRepositoriesMaxCapacity: 10,
		SyncObjectStorage:                false,
		CloneProtocol:                    "http",
		WebEditURL:                       "https://primary.example.com/admin/geo/nodes/3/edit",
		WebGeoProjectsURL:                "http://secondary.example.com/admin/geo/projects",
		Links: GeoNodeLinks{
			Self:   "https://primary.example.com/api/v4/geo_nodes/3",
			Status: "https://primary.example.com/api/v4/geo_nodes/3/status",
			Repair: "https://primary.example.com/api/v4/geo_nodes/3/repair",
		},
	}

	g, resp, err := client.GeoNodes.CreateGeoNode(nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, g)

	g, resp, err = client.GeoNodes.CreateGeoNode(nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, g)
}

func TestGeoNodesService_CreateGeoNode_StatusNotFound(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/geo_nodes", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		w.WriteHeader(http.StatusNotFound)
	})

	g, resp, err := client.GeoNodes.CreateGeoNode(nil)
	require.Error(t, err)
	require.Nil(t, g)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestGeoNodesService_ListGeoNodes(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/geo_nodes", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
			[
				{
				  "id": 3,
				  "name": "in-node",
				  "url": "https://secondary.example.com/",
				  "internal_url": "https://secondary.example.com/",
				  "primary": false,
				  "enabled": true,
				  "current": false,
				  "files_max_capacity": 10,
				  "repos_max_capacity": 25,
				  "verification_max_capacity": 100,
				  "selective_sync_type": "namespaces",
				  "selective_sync_shards": null,
				  "selective_sync_namespace_ids": [1, 25],
				  "minimum_reverification_interval": 7,
				  "container_repositories_max_capacity": 10,
				  "sync_object_storage": false,
				  "clone_protocol": "http",
				  "web_edit_url": "https://primary.example.com/admin/geo/nodes/3/edit",
				  "web_geo_projects_url": "http://secondary.example.com/admin/geo/projects",
				  "_links": {
					 "self": "https://primary.example.com/api/v4/geo_nodes/3",
					 "status": "https://primary.example.com/api/v4/geo_nodes/3/status",
					 "repair": "https://primary.example.com/api/v4/geo_nodes/3/repair"
				  }
				}
			]
		`)
	})

	want := []*GeoNode{
		{
			ID:                               3,
			Name:                             "in-node",
			URL:                              "https://secondary.example.com/",
			InternalURL:                      "https://secondary.example.com/",
			Primary:                          false,
			Enabled:                          true,
			Current:                          false,
			FilesMaxCapacity:                 10,
			ReposMaxCapacity:                 25,
			VerificationMaxCapacity:          100,
			SelectiveSyncType:                "namespaces",
			SelectiveSyncShards:              nil,
			SelectiveSyncNamespaceIds:        []int{1, 25},
			MinimumReverificationInterval:    7,
			ContainerRepositoriesMaxCapacity: 10,
			SyncObjectStorage:                false,
			CloneProtocol:                    "http",
			WebEditURL:                       "https://primary.example.com/admin/geo/nodes/3/edit",
			WebGeoProjectsURL:                "http://secondary.example.com/admin/geo/projects",
			Links: GeoNodeLinks{
				Self:   "https://primary.example.com/api/v4/geo_nodes/3",
				Status: "https://primary.example.com/api/v4/geo_nodes/3/status",
				Repair: "https://primary.example.com/api/v4/geo_nodes/3/repair",
			},
		},
	}

	gs, resp, err := client.GeoNodes.ListGeoNodes(nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, gs)

	gs, resp, err = client.GeoNodes.ListGeoNodes(nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, gs)
}

func TestGeoNodesService_ListGeoNodes_StatusNotFound(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/geo_nodes", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		w.WriteHeader(http.StatusNotFound)
	})

	gs, resp, err := client.GeoNodes.ListGeoNodes(nil)
	require.Error(t, err)
	require.Nil(t, gs)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestGeoNodesService_GetGeoNode(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/geo_nodes/3", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
			{
			  "id": 3,
			  "name": "in-node",
			  "url": "https://secondary.example.com/",
			  "internal_url": "https://secondary.example.com/",
			  "primary": false,
			  "enabled": true,
			  "current": false,
			  "files_max_capacity": 10,
			  "repos_max_capacity": 25,
			  "verification_max_capacity": 100,
			  "selective_sync_type": "namespaces",
			  "selective_sync_shards": null,
			  "selective_sync_namespace_ids": [1, 25],
			  "minimum_reverification_interval": 7,
			  "container_repositories_max_capacity": 10,
			  "sync_object_storage": false,
			  "clone_protocol": "http",
			  "web_edit_url": "https://primary.example.com/admin/geo/nodes/3/edit",
			  "web_geo_projects_url": "http://secondary.example.com/admin/geo/projects",
			  "_links": {
				 "self": "https://primary.example.com/api/v4/geo_nodes/3",
				 "status": "https://primary.example.com/api/v4/geo_nodes/3/status",
				 "repair": "https://primary.example.com/api/v4/geo_nodes/3/repair"
			  }
			}
		`)
	})

	want := &GeoNode{
		ID:                               3,
		Name:                             "in-node",
		URL:                              "https://secondary.example.com/",
		InternalURL:                      "https://secondary.example.com/",
		Primary:                          false,
		Enabled:                          true,
		Current:                          false,
		FilesMaxCapacity:                 10,
		ReposMaxCapacity:                 25,
		VerificationMaxCapacity:          100,
		SelectiveSyncType:                "namespaces",
		SelectiveSyncShards:              nil,
		SelectiveSyncNamespaceIds:        []int{1, 25},
		MinimumReverificationInterval:    7,
		ContainerRepositoriesMaxCapacity: 10,
		SyncObjectStorage:                false,
		CloneProtocol:                    "http",
		WebEditURL:                       "https://primary.example.com/admin/geo/nodes/3/edit",
		WebGeoProjectsURL:                "http://secondary.example.com/admin/geo/projects",
		Links: GeoNodeLinks{
			Self:   "https://primary.example.com/api/v4/geo_nodes/3",
			Status: "https://primary.example.com/api/v4/geo_nodes/3/status",
			Repair: "https://primary.example.com/api/v4/geo_nodes/3/repair",
		},
	}

	g, resp, err := client.GeoNodes.GetGeoNode(3, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, g)

	g, resp, err = client.GeoNodes.GetGeoNode(3, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, g)

	g, resp, err = client.GeoNodes.GetGeoNode(5, nil)
	require.Error(t, err)
	require.Nil(t, g)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestGeoNodesService_EditGeoNode(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/geo_nodes/3", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPut)
		fmt.Fprintf(w, `
			{
			  "id": 3,
			  "name": "in-node",
			  "url": "https://secondary.example.com/",
			  "internal_url": "https://secondary.example.com/",
			  "primary": false,
			  "enabled": true,
			  "current": false,
			  "files_max_capacity": 10,
			  "repos_max_capacity": 25,
			  "verification_max_capacity": 100,
			  "selective_sync_type": "namespaces",
			  "selective_sync_shards": null,
			  "selective_sync_namespace_ids": [1, 25],
			  "minimum_reverification_interval": 7,
			  "container_repositories_max_capacity": 10,
			  "sync_object_storage": false,
			  "clone_protocol": "http",
			  "web_edit_url": "https://primary.example.com/admin/geo/nodes/3/edit",
			  "web_geo_projects_url": "http://secondary.example.com/admin/geo/projects",
			  "_links": {
				 "self": "https://primary.example.com/api/v4/geo_nodes/3",
				 "status": "https://primary.example.com/api/v4/geo_nodes/3/status",
				 "repair": "https://primary.example.com/api/v4/geo_nodes/3/repair"
			  }
			}
		`)
	})

	want := &GeoNode{
		ID:                               3,
		Name:                             "in-node",
		URL:                              "https://secondary.example.com/",
		InternalURL:                      "https://secondary.example.com/",
		Primary:                          false,
		Enabled:                          true,
		Current:                          false,
		FilesMaxCapacity:                 10,
		ReposMaxCapacity:                 25,
		VerificationMaxCapacity:          100,
		SelectiveSyncType:                "namespaces",
		SelectiveSyncShards:              nil,
		SelectiveSyncNamespaceIds:        []int{1, 25},
		MinimumReverificationInterval:    7,
		ContainerRepositoriesMaxCapacity: 10,
		SyncObjectStorage:                false,
		CloneProtocol:                    "http",
		WebEditURL:                       "https://primary.example.com/admin/geo/nodes/3/edit",
		WebGeoProjectsURL:                "http://secondary.example.com/admin/geo/projects",
		Links: GeoNodeLinks{
			Self:   "https://primary.example.com/api/v4/geo_nodes/3",
			Status: "https://primary.example.com/api/v4/geo_nodes/3/status",
			Repair: "https://primary.example.com/api/v4/geo_nodes/3/repair",
		},
	}

	g, resp, err := client.GeoNodes.EditGeoNode(3, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, g)

	g, resp, err = client.GeoNodes.EditGeoNode(3, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, g)

	g, resp, err = client.GeoNodes.EditGeoNode(5, nil)
	require.Error(t, err)
	require.Nil(t, g)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestGeoNodesService_DeleteGeoNode(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/geo_nodes/3", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodDelete)
	})

	resp, err := client.GeoNodes.DeleteGeoNode(3, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)

	resp, err = client.GeoNodes.DeleteGeoNode(3, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)

	resp, err = client.GeoNodes.DeleteGeoNode(5, nil)
	require.Error(t, err)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestGeoNodesService_RepairGeoNode(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/geo_nodes/3/repair", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		fmt.Fprintf(w, `
			{
			  "id": 3,
			  "name": "in-node",
			  "url": "https://secondary.example.com/",
			  "internal_url": "https://secondary.example.com/",
			  "primary": false,
			  "enabled": true,
			  "current": false,
			  "files_max_capacity": 10,
			  "repos_max_capacity": 25,
			  "verification_max_capacity": 100,
			  "selective_sync_type": "namespaces",
			  "selective_sync_shards": null,
			  "selective_sync_namespace_ids": [1, 25],
			  "minimum_reverification_interval": 7,
			  "container_repositories_max_capacity": 10,
			  "sync_object_storage": false,
			  "clone_protocol": "http",
			  "web_edit_url": "https://primary.example.com/admin/geo/nodes/3/edit",
			  "web_geo_projects_url": "http://secondary.example.com/admin/geo/projects",
			  "_links": {
				 "self": "https://primary.example.com/api/v4/geo_nodes/3",
				 "status": "https://primary.example.com/api/v4/geo_nodes/3/status",
				 "repair": "https://primary.example.com/api/v4/geo_nodes/3/repair"
			  }
			}
		`)
	})

	want := &GeoNode{
		ID:                               3,
		Name:                             "in-node",
		URL:                              "https://secondary.example.com/",
		InternalURL:                      "https://secondary.example.com/",
		Primary:                          false,
		Enabled:                          true,
		Current:                          false,
		FilesMaxCapacity:                 10,
		ReposMaxCapacity:                 25,
		VerificationMaxCapacity:          100,
		SelectiveSyncType:                "namespaces",
		SelectiveSyncShards:              nil,
		SelectiveSyncNamespaceIds:        []int{1, 25},
		MinimumReverificationInterval:    7,
		ContainerRepositoriesMaxCapacity: 10,
		SyncObjectStorage:                false,
		CloneProtocol:                    "http",
		WebEditURL:                       "https://primary.example.com/admin/geo/nodes/3/edit",
		WebGeoProjectsURL:                "http://secondary.example.com/admin/geo/projects",
		Links: GeoNodeLinks{
			Self:   "https://primary.example.com/api/v4/geo_nodes/3",
			Status: "https://primary.example.com/api/v4/geo_nodes/3/status",
			Repair: "https://primary.example.com/api/v4/geo_nodes/3/repair",
		},
	}

	g, resp, err := client.GeoNodes.RepairGeoNode(3, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, g)

	g, resp, err = client.GeoNodes.RepairGeoNode(3, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, g)

	g, resp, err = client.GeoNodes.RepairGeoNode(5, nil)
	require.Error(t, err)
	require.Nil(t, g)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestGeoNodesService_RetrieveStatusOfAllGeoNodes(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/geo_nodes/status", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		mustWriteHTTPResponse(t, w, "testdata/list_geo_nodes_status.json")
	})

	want := []*GeoNodeStatus{
		{
			GeoNodeID:                                  1,
			Healthy:                                    true,
			Health:                                     "Healthy",
			HealthStatus:                               "Healthy",
			MissingOauthApplication:                    false,
			AttachmentsCount:                           1,
			AttachmentsSyncedInPercentage:              "0.00%",
			LfsObjectsSyncedInPercentage:               "0.00%",
			JobArtifactsCount:                          2,
			JobArtifactsSyncedInPercentage:             "0.00%",
			ContainerRepositoriesCount:                 3,
			ContainerRepositoriesSyncedInPercentage:    "0.00%",
			DesignRepositoriesCount:                    3,
			DesignRepositoriesSyncedInPercentage:       "0.00%",
			ProjectsCount:                              41,
			RepositoriesCount:                          41,
			RepositoriesSyncedInPercentage:             "0.00%",
			WikisCount:                                 41,
			WikisSyncedInPercentage:                    "0.00%",
			ReplicationSlotsCount:                      1,
			ReplicationSlotsUsedCount:                  1,
			ReplicationSlotsUsedInPercentage:           "100.00%",
			RepositoriesCheckedCount:                   20,
			RepositoriesCheckedFailedCount:             20,
			RepositoriesCheckedInPercentage:            "100.00%",
			RepositoriesChecksummedCount:               20,
			RepositoriesChecksumFailedCount:            5,
			RepositoriesChecksummedInPercentage:        "48.78%",
			WikisChecksummedCount:                      10,
			WikisChecksumFailedCount:                   3,
			WikisChecksummedInPercentage:               "24.39%",
			RepositoriesVerifiedCount:                  20,
			RepositoriesVerificationFailedCount:        5,
			RepositoriesVerifiedInPercentage:           "48.78%",
			RepositoriesChecksumMismatchCount:          3,
			WikisVerifiedCount:                         10,
			WikisVerificationFailedCount:               3,
			WikisVerifiedInPercentage:                  "24.39%",
			WikisChecksumMismatchCount:                 1,
			RepositoriesRetryingVerificationCount:      1,
			WikisRetryingVerificationCount:             3,
			LastEventID:                                23,
			LastEventTimestamp:                         1509681166,
			LastSuccessfulStatusCheckTimestamp:         1510125024,
			Version:                                    "10.3.0",
			Revision:                                   "33d33a096a",
			MergeRequestDiffsCount:                     5,
			MergeRequestDiffsChecksumTotalCount:        5,
			MergeRequestDiffsChecksummedCount:          5,
			MergeRequestDiffsSyncedInPercentage:        "0.00%",
			MergeRequestDiffsVerifiedInPercentage:      "0.00%",
			PackageFilesCount:                          5,
			PackageFilesChecksumTotalCount:             5,
			PackageFilesChecksummedCount:               5,
			PackageFilesSyncedInPercentage:             "0.00%",
			PackageFilesVerifiedInPercentage:           "0.00%",
			PagesDeploymentsCount:                      5,
			PagesDeploymentsChecksumTotalCount:         5,
			PagesDeploymentsChecksummedCount:           5,
			PagesDeploymentsSyncedInPercentage:         "0.00%",
			PagesDeploymentsVerifiedInPercentage:       "0.00%",
			TerraformStateVersionsCount:                5,
			TerraformStateVersionsChecksumTotalCount:   5,
			TerraformStateVersionsChecksummedCount:     5,
			TerraformStateVersionsSyncedInPercentage:   "0.00%",
			TerraformStateVersionsVerifiedInPercentage: "0.00%",
			SnippetRepositoriesCount:                   5,
			SnippetRepositoriesChecksumTotalCount:      5,
			SnippetRepositoriesChecksummedCount:        5,
			SnippetRepositoriesSyncedInPercentage:      "0.00%",
			SnippetRepositoriesVerifiedInPercentage:    "0.00%",
			GroupWikiRepositoriesCount:                 5,
			GroupWikiRepositoriesChecksumTotalCount:    5,
			GroupWikiRepositoriesChecksummedCount:      5,
			GroupWikiRepositoriesSyncedInPercentage:    "0.00%",
			GroupWikiRepositoriesVerifiedInPercentage:  "0.00%",
			PipelineArtifactsCount:                     5,
			PipelineArtifactsChecksumTotalCount:        5,
			PipelineArtifactsChecksummedCount:          5,
			PipelineArtifactsSyncedInPercentage:        "0.00%",
			PipelineArtifactsVerifiedInPercentage:      "0.00%",
			UploadsCount:                               5,
			UploadsSyncedInPercentage:                  "0.00%",
		},
	}

	gnss, resp, err := client.GeoNodes.RetrieveStatusOfAllGeoNodes(nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, gnss)

	gnss, resp, err = client.GeoNodes.RetrieveStatusOfAllGeoNodes(errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, gnss)
}

func TestGeoNodesService_RetrieveStatusOfAllGeoNodes_StatusNotFound(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/geo_nodes/status", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		w.WriteHeader(http.StatusNotFound)
	})

	gnss, resp, err := client.GeoNodes.RetrieveStatusOfAllGeoNodes(nil)
	require.Error(t, err)
	require.Nil(t, gnss)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestGeoNodesService_RetrieveStatusOfGeoNode(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/geo_nodes/1/status", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		mustWriteHTTPResponse(t, w, "testdata/get_geo_node_status.json")
	})

	want := &GeoNodeStatus{
		GeoNodeID:                                  1,
		Healthy:                                    true,
		Health:                                     "Healthy",
		HealthStatus:                               "Healthy",
		MissingOauthApplication:                    false,
		AttachmentsCount:                           1,
		AttachmentsSyncedInPercentage:              "0.00%",
		LfsObjectsSyncedInPercentage:               "0.00%",
		JobArtifactsCount:                          2,
		JobArtifactsSyncedInPercentage:             "0.00%",
		ContainerRepositoriesCount:                 3,
		ContainerRepositoriesSyncedInPercentage:    "0.00%",
		DesignRepositoriesCount:                    3,
		DesignRepositoriesSyncedInPercentage:       "0.00%",
		ProjectsCount:                              41,
		RepositoriesCount:                          41,
		RepositoriesSyncedInPercentage:             "0.00%",
		WikisCount:                                 41,
		WikisSyncedInPercentage:                    "0.00%",
		ReplicationSlotsCount:                      1,
		ReplicationSlotsUsedCount:                  1,
		ReplicationSlotsUsedInPercentage:           "100.00%",
		RepositoriesCheckedCount:                   20,
		RepositoriesCheckedFailedCount:             20,
		RepositoriesCheckedInPercentage:            "100.00%",
		RepositoriesChecksummedCount:               20,
		RepositoriesChecksumFailedCount:            5,
		RepositoriesChecksummedInPercentage:        "48.78%",
		WikisChecksummedCount:                      10,
		WikisChecksumFailedCount:                   3,
		WikisChecksummedInPercentage:               "24.39%",
		RepositoriesVerifiedCount:                  20,
		RepositoriesVerificationFailedCount:        5,
		RepositoriesVerifiedInPercentage:           "48.78%",
		RepositoriesChecksumMismatchCount:          3,
		WikisVerifiedCount:                         10,
		WikisVerificationFailedCount:               3,
		WikisVerifiedInPercentage:                  "24.39%",
		WikisChecksumMismatchCount:                 1,
		RepositoriesRetryingVerificationCount:      1,
		WikisRetryingVerificationCount:             3,
		LastEventID:                                23,
		LastEventTimestamp:                         1509681166,
		LastSuccessfulStatusCheckTimestamp:         1510125024,
		Version:                                    "10.3.0",
		Revision:                                   "33d33a096a",
		MergeRequestDiffsCount:                     5,
		MergeRequestDiffsChecksumTotalCount:        5,
		MergeRequestDiffsChecksummedCount:          5,
		MergeRequestDiffsSyncedInPercentage:        "0.00%",
		MergeRequestDiffsVerifiedInPercentage:      "0.00%",
		PackageFilesCount:                          5,
		PackageFilesChecksumTotalCount:             5,
		PackageFilesChecksummedCount:               5,
		PackageFilesSyncedInPercentage:             "0.00%",
		PackageFilesVerifiedInPercentage:           "0.00%",
		PagesDeploymentsCount:                      5,
		PagesDeploymentsChecksumTotalCount:         5,
		PagesDeploymentsChecksummedCount:           5,
		PagesDeploymentsSyncedInPercentage:         "0.00%",
		PagesDeploymentsVerifiedInPercentage:       "0.00%",
		TerraformStateVersionsCount:                5,
		TerraformStateVersionsChecksumTotalCount:   5,
		TerraformStateVersionsChecksummedCount:     5,
		TerraformStateVersionsSyncedInPercentage:   "0.00%",
		TerraformStateVersionsVerifiedInPercentage: "0.00%",
		SnippetRepositoriesCount:                   5,
		SnippetRepositoriesChecksumTotalCount:      5,
		SnippetRepositoriesChecksummedCount:        5,
		SnippetRepositoriesSyncedInPercentage:      "0.00%",
		SnippetRepositoriesVerifiedInPercentage:    "0.00%",
		GroupWikiRepositoriesCount:                 5,
		GroupWikiRepositoriesChecksumTotalCount:    5,
		GroupWikiRepositoriesChecksummedCount:      5,
		GroupWikiRepositoriesSyncedInPercentage:    "0.00%",
		GroupWikiRepositoriesVerifiedInPercentage:  "0.00%",
		PipelineArtifactsCount:                     5,
		PipelineArtifactsChecksumTotalCount:        5,
		PipelineArtifactsChecksummedCount:          5,
		PipelineArtifactsSyncedInPercentage:        "0.00%",
		PipelineArtifactsVerifiedInPercentage:      "0.00%",
		UploadsCount:                               5,
		UploadsSyncedInPercentage:                  "0.00%",
	}

	gns, resp, err := client.GeoNodes.RetrieveStatusOfGeoNode(1, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, gns)

	gns, resp, err = client.GeoNodes.RetrieveStatusOfGeoNode(1, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, gns)

	gns, resp, err = client.GeoNodes.RetrieveStatusOfGeoNode(3, nil)
	require.Error(t, err)
	require.Nil(t, gns)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/gitignore_templates.go000066400000000000000000000054341475761473200254610ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"fmt"
	"net/http"
	"net/url"
)

// GitIgnoreTemplatesService handles communication with the gitignore
// templates related methods of the GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/templates/gitignores.html
type GitIgnoreTemplatesService struct {
	client *Client
}

// GitIgnoreTemplate represents a GitLab gitignore template.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/templates/gitignores.html
type GitIgnoreTemplate struct {
	Name    string `json:"name"`
	Content string `json:"content"`
}

// GitIgnoreTemplateListItem represents a GitLab gitignore template from the list.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/templates/gitignores.html
type GitIgnoreTemplateListItem struct {
	Key  string `json:"key"`
	Name string `json:"name"`
}

// ListTemplatesOptions represents the available ListAllTemplates() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/templates/gitignores.html#get-all-gitignore-templates
type ListTemplatesOptions ListOptions

// ListTemplates get a list of available git ignore templates
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/templates/gitignores.html#get-all-gitignore-templates
func (s *GitIgnoreTemplatesService) ListTemplates(opt *ListTemplatesOptions, options ...RequestOptionFunc) ([]*GitIgnoreTemplateListItem, *Response, error) {
	req, err := s.client.NewRequest(http.MethodGet, "templates/gitignores", opt, options)
	if err != nil {
		return nil, nil, err
	}

	var gs []*GitIgnoreTemplateListItem
	resp, err := s.client.Do(req, &gs)
	if err != nil {
		return nil, resp, err
	}

	return gs, resp, nil
}

// GetTemplate get a git ignore template
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/templates/gitignores.html#get-a-single-gitignore-template
func (s *GitIgnoreTemplatesService) GetTemplate(key string, options ...RequestOptionFunc) (*GitIgnoreTemplate, *Response, error) {
	u := fmt.Sprintf("templates/gitignores/%s", url.PathEscape(key))

	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	g := new(GitIgnoreTemplate)
	resp, err := s.client.Do(req, g)
	if err != nil {
		return nil, resp, err
	}

	return g, resp, nil
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/gitignore_templates_test.go000066400000000000000000000102341475761473200265120ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"fmt"
	"net/http"
	"reflect"
	"testing"
)

func TestListTemplates(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/templates/gitignores", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `[
			{
			  "key": "Actionscript",
			  "name": "Actionscript"
			},
			{
			  "key": "Ada",
			  "name": "Ada"
			},
			{
			  "key": "Agda",
			  "name": "Agda"
			},
			{
			  "key": "Android",
			  "name": "Android"
			},
			{
			  "key": "AppEngine",
			  "name": "AppEngine"
			},
			{
			  "key": "AppceleratorTitanium",
			  "name": "AppceleratorTitanium"
			},
			{
			  "key": "ArchLinuxPackages",
			  "name": "ArchLinuxPackages"
			},
			{
			  "key": "Autotools",
			  "name": "Autotools"
			},
			{
			  "key": "C",
			  "name": "C"
			},
			{
			  "key": "C++",
			  "name": "C++"
			},
			{
			  "key": "CFWheels",
			  "name": "CFWheels"
			},
			{
			  "key": "CMake",
			  "name": "CMake"
			},
			{
			  "key": "CUDA",
			  "name": "CUDA"
			},
			{
			  "key": "CakePHP",
			  "name": "CakePHP"
			},
			{
			  "key": "ChefCookbook",
			  "name": "ChefCookbook"
			},
			{
			  "key": "Clojure",
			  "name": "Clojure"
			},
			{
			  "key": "CodeIgniter",
			  "name": "CodeIgniter"
			},
			{
			  "key": "CommonLisp",
			  "name": "CommonLisp"
			},
			{
			  "key": "Composer",
			  "name": "Composer"
			},
			{
			  "key": "Concrete5",
			  "name": "Concrete5"
			}
		  ]`)
	})

	templates, _, err := client.GitIgnoreTemplates.ListTemplates(&ListTemplatesOptions{})
	if err != nil {
		t.Errorf("GitIgnoreTemplates.ListTemplates returned error: %v", err)
	}

	want := []*GitIgnoreTemplateListItem{
		{
			Key:  "Actionscript",
			Name: "Actionscript",
		},
		{
			Key:  "Ada",
			Name: "Ada",
		},
		{
			Key:  "Agda",
			Name: "Agda",
		},
		{
			Key:  "Android",
			Name: "Android",
		},
		{
			Key:  "AppEngine",
			Name: "AppEngine",
		},
		{
			Key:  "AppceleratorTitanium",
			Name: "AppceleratorTitanium",
		},
		{
			Key:  "ArchLinuxPackages",
			Name: "ArchLinuxPackages",
		},
		{
			Key:  "Autotools",
			Name: "Autotools",
		},
		{
			Key:  "C",
			Name: "C",
		},
		{
			Key:  "C++",
			Name: "C++",
		},
		{
			Key:  "CFWheels",
			Name: "CFWheels",
		},
		{
			Key:  "CMake",
			Name: "CMake",
		},
		{
			Key:  "CUDA",
			Name: "CUDA",
		},
		{
			Key:  "CakePHP",
			Name: "CakePHP",
		},
		{
			Key:  "ChefCookbook",
			Name: "ChefCookbook",
		},
		{
			Key:  "Clojure",
			Name: "Clojure",
		},
		{
			Key:  "CodeIgniter",
			Name: "CodeIgniter",
		},
		{
			Key:  "CommonLisp",
			Name: "CommonLisp",
		},
		{
			Key:  "Composer",
			Name: "Composer",
		},
		{
			Key:  "Concrete5",
			Name: "Concrete5",
		},
	}
	if !reflect.DeepEqual(want, templates) {
		t.Errorf("GitIgnoreTemplates.ListTemplates returned %+v, want %+v", templates, want)
	}
}

func TestGetTemplates(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/templates/gitignores/Ruby", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `{
			"name": "Ruby",
			"content": "*.gem\n*.rbc\n/.config\n/coverage/\n/InstalledFiles\n/pkg/\n/spec/reports/"
		  }`)
	})

	template, _, err := client.GitIgnoreTemplates.GetTemplate("Ruby")
	if err != nil {
		t.Errorf("GitIgnoreTempaltes.GetTemplate returned an error: %v", err)
	}

	want := &GitIgnoreTemplate{
		Name:    "Ruby",
		Content: "*.gem\n*.rbc\n/.config\n/coverage/\n/InstalledFiles\n/pkg/\n/spec/reports/",
	}
	if !reflect.DeepEqual(want, template) {
		t.Errorf("GitIgnoreTemplates.GetTemplate returned %+v, want %+v", template, want)
	}
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/gitlab.go000066400000000000000000001070761475761473200226630ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

// Package gitlab implements a GitLab API client.
package gitlab

import (
	"bytes"
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"math"
	"math/rand"
	"mime/multipart"
	"net/http"
	"net/url"
	"sort"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/hashicorp/go-cleanhttp"

	"github.com/google/go-querystring/query"
	retryablehttp "github.com/hashicorp/go-retryablehttp"
	"golang.org/x/oauth2"
	"golang.org/x/time/rate"
)

const (
	defaultBaseURL = "https://gitlab.com/"
	apiVersionPath = "api/v4/"
	userAgent      = "go-gitlab"

	headerRateLimit = "RateLimit-Limit"
	headerRateReset = "RateLimit-Reset"
)

// AuthType represents an authentication type within GitLab.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/
type AuthType int

// List of available authentication types.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/
const (
	BasicAuth AuthType = iota
	JobToken
	OAuthToken
	PrivateToken
)

var ErrNotFound = errors.New("404 Not Found")

// A Client manages communication with the GitLab API.
type Client struct {
	// HTTP client used to communicate with the API.
	client *retryablehttp.Client

	// Base URL for API requests. Defaults to the public GitLab API, but can be
	// set to a domain endpoint to use with a self hosted GitLab server. baseURL
	// should always be specified with a trailing slash.
	baseURL *url.URL

	// disableRetries is used to disable the default retry logic.
	disableRetries bool

	// configureLimiterOnce is used to make sure the limiter is configured exactly
	// once and block all other calls until the initial (one) call is done.
	configureLimiterOnce sync.Once

	// Limiter is used to limit API calls and prevent 429 responses.
	limiter RateLimiter

	// Token type used to make authenticated API calls.
	authType AuthType

	// Username and password used for basic authentication.
	username, password string

	// Token used to make authenticated API calls.
	token string

	// Protects the token field from concurrent read/write accesses.
	tokenLock sync.RWMutex

	// Default request options applied to every request.
	defaultRequestOptions []RequestOptionFunc

	// User agent used when communicating with the GitLab API.
	UserAgent string

	// Services used for talking to different parts of the GitLab API.
	AccessRequests               *AccessRequestsService
	Appearance                   *AppearanceService
	Applications                 *ApplicationsService
	AuditEvents                  *AuditEventsService
	Avatar                       *AvatarRequestsService
	AwardEmoji                   *AwardEmojiService
	Boards                       *IssueBoardsService
	Branches                     *BranchesService
	BroadcastMessage             *BroadcastMessagesService
	BulkImports                  *BulkImportsService
	CIYMLTemplate                *CIYMLTemplatesService
	ClusterAgents                *ClusterAgentsService
	Commits                      *CommitsService
	ContainerRegistry            *ContainerRegistryService
	CustomAttribute              *CustomAttributesService
	DependencyListExport         *DependencyListExportService
	DeployKeys                   *DeployKeysService
	DeployTokens                 *DeployTokensService
	DeploymentMergeRequests      *DeploymentMergeRequestsService
	Deployments                  *DeploymentsService
	Discussions                  *DiscussionsService
	DockerfileTemplate           *DockerfileTemplatesService
	DORAMetrics                  *DORAMetricsService
	DraftNotes                   *DraftNotesService
	Environments                 *EnvironmentsService
	EpicIssues                   *EpicIssuesService
	Epics                        *EpicsService
	ErrorTracking                *ErrorTrackingService
	Events                       *EventsService
	ExternalStatusChecks         *ExternalStatusChecksService
	Features                     *FeaturesService
	FreezePeriods                *FreezePeriodsService
	GenericPackages              *GenericPackagesService
	GeoNodes                     *GeoNodesService
	GitIgnoreTemplates           *GitIgnoreTemplatesService
	GroupAccessTokens            *GroupAccessTokensService
	GroupBadges                  *GroupBadgesService
	GroupCluster                 *GroupClustersService
	GroupEpicBoards              *GroupEpicBoardsService
	GroupImportExport            *GroupImportExportService
	GroupIssueBoards             *GroupIssueBoardsService
	GroupIterations              *GroupIterationsService
	GroupLabels                  *GroupLabelsService
	GroupMembers                 *GroupMembersService
	GroupMilestones              *GroupMilestonesService
	GroupProtectedEnvironments   *GroupProtectedEnvironmentsService
	GroupRepositoryStorageMove   *GroupRepositoryStorageMoveService
	GroupSecuritySettings        *GroupSecuritySettingsService
	GroupSSHCertificates         *GroupSSHCertificatesService
	GroupVariables               *GroupVariablesService
	GroupWikis                   *GroupWikisService
	Groups                       *GroupsService
	Import                       *ImportService
	InstanceCluster              *InstanceClustersService
	InstanceVariables            *InstanceVariablesService
	Invites                      *InvitesService
	IssueLinks                   *IssueLinksService
	Issues                       *IssuesService
	IssuesStatistics             *IssuesStatisticsService
	Jobs                         *JobsService
	JobTokenScope                *JobTokenScopeService
	Keys                         *KeysService
	Labels                       *LabelsService
	License                      *LicenseService
	LicenseTemplates             *LicenseTemplatesService
	ManagedLicenses              *ManagedLicensesService
	Markdown                     *MarkdownService
	MemberRolesService           *MemberRolesService
	MergeRequestApprovals        *MergeRequestApprovalsService
	MergeRequests                *MergeRequestsService
	MergeTrains                  *MergeTrainsService
	Metadata                     *MetadataService
	Milestones                   *MilestonesService
	Namespaces                   *NamespacesService
	Notes                        *NotesService
	NotificationSettings         *NotificationSettingsService
	Packages                     *PackagesService
	Pages                        *PagesService
	PagesDomains                 *PagesDomainsService
	PersonalAccessTokens         *PersonalAccessTokensService
	PipelineSchedules            *PipelineSchedulesService
	PipelineTriggers             *PipelineTriggersService
	Pipelines                    *PipelinesService
	PlanLimits                   *PlanLimitsService
	ProjectAccessTokens          *ProjectAccessTokensService
	ProjectBadges                *ProjectBadgesService
	ProjectCluster               *ProjectClustersService
	ProjectFeatureFlags          *ProjectFeatureFlagService
	ProjectImportExport          *ProjectImportExportService
	ProjectIterations            *ProjectIterationsService
	ProjectMarkdownUploads       *ProjectMarkdownUploadsService
	ProjectMembers               *ProjectMembersService
	ProjectMirrors               *ProjectMirrorService
	ProjectRepositoryStorageMove *ProjectRepositoryStorageMoveService
	ProjectSnippets              *ProjectSnippetsService
	ProjectTemplates             *ProjectTemplatesService
	ProjectVariables             *ProjectVariablesService
	ProjectVulnerabilities       *ProjectVulnerabilitiesService
	Projects                     *ProjectsService
	ProtectedBranches            *ProtectedBranchesService
	ProtectedEnvironments        *ProtectedEnvironmentsService
	ProtectedTags                *ProtectedTagsService
	ReleaseLinks                 *ReleaseLinksService
	Releases                     *ReleasesService
	Repositories                 *RepositoriesService
	RepositoryFiles              *RepositoryFilesService
	RepositorySubmodules         *RepositorySubmodulesService
	ResourceGroup                *ResourceGroupService
	ResourceIterationEvents      *ResourceIterationEventsService
	ResourceLabelEvents          *ResourceLabelEventsService
	ResourceMilestoneEvents      *ResourceMilestoneEventsService
	ResourceStateEvents          *ResourceStateEventsService
	ResourceWeightEvents         *ResourceWeightEventsService
	Runners                      *RunnersService
	Search                       *SearchService
	Services                     *ServicesService
	Settings                     *SettingsService
	Sidekiq                      *SidekiqService
	SnippetRepositoryStorageMove *SnippetRepositoryStorageMoveService
	Snippets                     *SnippetsService
	SystemHooks                  *SystemHooksService
	Tags                         *TagsService
	Todos                        *TodosService
	Topics                       *TopicsService
	Users                        *UsersService
	Validate                     *ValidateService
	Version                      *VersionService
	Wikis                        *WikisService
}

// ListOptions specifies the optional parameters to various List methods that
// support pagination.
type ListOptions struct {
	// For keyset-based paginated result sets, the value must be `"keyset"`
	Pagination string `url:"pagination,omitempty" json:"pagination,omitempty"`
	// For offset-based and keyset-based paginated result sets, the number of results to include per page.
	PerPage int `url:"per_page,omitempty" json:"per_page,omitempty"`
	// For offset-based paginated result sets, page of results to retrieve.
	Page int `url:"page,omitempty" json:"page,omitempty"`
	// For keyset-based paginated result sets, tree record ID at which to fetch the next page.
	PageToken string `url:"page_token,omitempty" json:"page_token,omitempty"`
	// For keyset-based paginated result sets, name of the column by which to order
	OrderBy string `url:"order_by,omitempty" json:"order_by,omitempty"`
	// For keyset-based paginated result sets, sort order (`"asc"`` or `"desc"`)
	Sort string `url:"sort,omitempty" json:"sort,omitempty"`
}

// RateLimiter describes the interface that all (custom) rate limiters must implement.
type RateLimiter interface {
	Wait(context.Context) error
}

// NewClient returns a new GitLab API client. To use API methods which require
// authentication, provide a valid private or personal token.
func NewClient(token string, options ...ClientOptionFunc) (*Client, error) {
	client, err := newClient(options...)
	if err != nil {
		return nil, err
	}
	client.authType = PrivateToken
	client.token = token
	return client, nil
}

// NewBasicAuthClient returns a new GitLab API client. To use API methods which
// require authentication, provide a valid username and password.
func NewBasicAuthClient(username, password string, options ...ClientOptionFunc) (*Client, error) {
	client, err := newClient(options...)
	if err != nil {
		return nil, err
	}

	client.authType = BasicAuth
	client.username = username
	client.password = password

	return client, nil
}

// NewJobClient returns a new GitLab API client. To use API methods which require
// authentication, provide a valid job token.
func NewJobClient(token string, options ...ClientOptionFunc) (*Client, error) {
	client, err := newClient(options...)
	if err != nil {
		return nil, err
	}
	client.authType = JobToken
	client.token = token
	return client, nil
}

// NewOAuthClient returns a new GitLab API client. To use API methods which
// require authentication, provide a valid oauth token.
func NewOAuthClient(token string, options ...ClientOptionFunc) (*Client, error) {
	client, err := newClient(options...)
	if err != nil {
		return nil, err
	}
	client.authType = OAuthToken
	client.token = token
	return client, nil
}

func newClient(options ...ClientOptionFunc) (*Client, error) {
	c := &Client{UserAgent: userAgent}

	// Configure the HTTP client.
	c.client = &retryablehttp.Client{
		Backoff:      c.retryHTTPBackoff,
		CheckRetry:   c.retryHTTPCheck,
		ErrorHandler: retryablehttp.PassthroughErrorHandler,
		HTTPClient:   cleanhttp.DefaultPooledClient(),
		RetryWaitMin: 100 * time.Millisecond,
		RetryWaitMax: 400 * time.Millisecond,
		RetryMax:     5,
	}

	// Set the default base URL.
	c.setBaseURL(defaultBaseURL)

	// Apply any given client options.
	for _, fn := range options {
		if fn == nil {
			continue
		}
		if err := fn(c); err != nil {
			return nil, err
		}
	}

	// If no custom limiter was set using a client option, configure
	// the default rate limiter with values that implicitly disable
	// rate limiting until an initial HTTP call is done and we can
	// use the headers to try and properly configure the limiter.
	if c.limiter == nil {
		c.limiter = rate.NewLimiter(rate.Inf, 0)
	}

	// Create the internal timeStats service.
	timeStats := &timeStatsService{client: c}

	// Create all the public services.
	c.AccessRequests = &AccessRequestsService{client: c}
	c.Appearance = &AppearanceService{client: c}
	c.Applications = &ApplicationsService{client: c}
	c.AuditEvents = &AuditEventsService{client: c}
	c.Avatar = &AvatarRequestsService{client: c}
	c.AwardEmoji = &AwardEmojiService{client: c}
	c.Boards = &IssueBoardsService{client: c}
	c.Branches = &BranchesService{client: c}
	c.BroadcastMessage = &BroadcastMessagesService{client: c}
	c.BulkImports = &BulkImportsService{client: c}
	c.CIYMLTemplate = &CIYMLTemplatesService{client: c}
	c.ClusterAgents = &ClusterAgentsService{client: c}
	c.Commits = &CommitsService{client: c}
	c.ContainerRegistry = &ContainerRegistryService{client: c}
	c.CustomAttribute = &CustomAttributesService{client: c}
	c.DependencyListExport = &DependencyListExportService{client: c}
	c.DeployKeys = &DeployKeysService{client: c}
	c.DeployTokens = &DeployTokensService{client: c}
	c.DeploymentMergeRequests = &DeploymentMergeRequestsService{client: c}
	c.Deployments = &DeploymentsService{client: c}
	c.Discussions = &DiscussionsService{client: c}
	c.DockerfileTemplate = &DockerfileTemplatesService{client: c}
	c.DORAMetrics = &DORAMetricsService{client: c}
	c.DraftNotes = &DraftNotesService{client: c}
	c.Environments = &EnvironmentsService{client: c}
	c.EpicIssues = &EpicIssuesService{client: c}
	c.Epics = &EpicsService{client: c}
	c.ErrorTracking = &ErrorTrackingService{client: c}
	c.Events = &EventsService{client: c}
	c.ExternalStatusChecks = &ExternalStatusChecksService{client: c}
	c.Features = &FeaturesService{client: c}
	c.FreezePeriods = &FreezePeriodsService{client: c}
	c.GenericPackages = &GenericPackagesService{client: c}
	c.GeoNodes = &GeoNodesService{client: c}
	c.GitIgnoreTemplates = &GitIgnoreTemplatesService{client: c}
	c.GroupAccessTokens = &GroupAccessTokensService{client: c}
	c.GroupBadges = &GroupBadgesService{client: c}
	c.GroupCluster = &GroupClustersService{client: c}
	c.GroupEpicBoards = &GroupEpicBoardsService{client: c}
	c.GroupImportExport = &GroupImportExportService{client: c}
	c.GroupIssueBoards = &GroupIssueBoardsService{client: c}
	c.GroupIterations = &GroupIterationsService{client: c}
	c.GroupLabels = &GroupLabelsService{client: c}
	c.GroupMembers = &GroupMembersService{client: c}
	c.GroupMilestones = &GroupMilestonesService{client: c}
	c.GroupProtectedEnvironments = &GroupProtectedEnvironmentsService{client: c}
	c.GroupRepositoryStorageMove = &GroupRepositoryStorageMoveService{client: c}
	c.GroupSecuritySettings = &GroupSecuritySettingsService{client: c}
	c.GroupSSHCertificates = &GroupSSHCertificatesService{client: c}
	c.GroupVariables = &GroupVariablesService{client: c}
	c.GroupWikis = &GroupWikisService{client: c}
	c.Groups = &GroupsService{client: c}
	c.Import = &ImportService{client: c}
	c.InstanceCluster = &InstanceClustersService{client: c}
	c.InstanceVariables = &InstanceVariablesService{client: c}
	c.Invites = &InvitesService{client: c}
	c.IssueLinks = &IssueLinksService{client: c}
	c.Issues = &IssuesService{client: c, timeStats: timeStats}
	c.IssuesStatistics = &IssuesStatisticsService{client: c}
	c.Jobs = &JobsService{client: c}
	c.JobTokenScope = &JobTokenScopeService{client: c}
	c.Keys = &KeysService{client: c}
	c.Labels = &LabelsService{client: c}
	c.License = &LicenseService{client: c}
	c.LicenseTemplates = &LicenseTemplatesService{client: c}
	c.ManagedLicenses = &ManagedLicensesService{client: c}
	c.Markdown = &MarkdownService{client: c}
	c.MemberRolesService = &MemberRolesService{client: c}
	c.MergeRequestApprovals = &MergeRequestApprovalsService{client: c}
	c.MergeRequests = &MergeRequestsService{client: c, timeStats: timeStats}
	c.MergeTrains = &MergeTrainsService{client: c}
	c.Metadata = &MetadataService{client: c}
	c.Milestones = &MilestonesService{client: c}
	c.Namespaces = &NamespacesService{client: c}
	c.Notes = &NotesService{client: c}
	c.NotificationSettings = &NotificationSettingsService{client: c}
	c.Packages = &PackagesService{client: c}
	c.Pages = &PagesService{client: c}
	c.PagesDomains = &PagesDomainsService{client: c}
	c.PersonalAccessTokens = &PersonalAccessTokensService{client: c}
	c.PipelineSchedules = &PipelineSchedulesService{client: c}
	c.PipelineTriggers = &PipelineTriggersService{client: c}
	c.Pipelines = &PipelinesService{client: c}
	c.PlanLimits = &PlanLimitsService{client: c}
	c.ProjectAccessTokens = &ProjectAccessTokensService{client: c}
	c.ProjectBadges = &ProjectBadgesService{client: c}
	c.ProjectCluster = &ProjectClustersService{client: c}
	c.ProjectFeatureFlags = &ProjectFeatureFlagService{client: c}
	c.ProjectImportExport = &ProjectImportExportService{client: c}
	c.ProjectIterations = &ProjectIterationsService{client: c}
	c.ProjectMarkdownUploads = &ProjectMarkdownUploadsService{client: c}
	c.ProjectMembers = &ProjectMembersService{client: c}
	c.ProjectMirrors = &ProjectMirrorService{client: c}
	c.ProjectRepositoryStorageMove = &ProjectRepositoryStorageMoveService{client: c}
	c.ProjectSnippets = &ProjectSnippetsService{client: c}
	c.ProjectTemplates = &ProjectTemplatesService{client: c}
	c.ProjectVariables = &ProjectVariablesService{client: c}
	c.ProjectVulnerabilities = &ProjectVulnerabilitiesService{client: c}
	c.Projects = &ProjectsService{client: c}
	c.ProtectedBranches = &ProtectedBranchesService{client: c}
	c.ProtectedEnvironments = &ProtectedEnvironmentsService{client: c}
	c.ProtectedTags = &ProtectedTagsService{client: c}
	c.ReleaseLinks = &ReleaseLinksService{client: c}
	c.Releases = &ReleasesService{client: c}
	c.Repositories = &RepositoriesService{client: c}
	c.RepositoryFiles = &RepositoryFilesService{client: c}
	c.RepositorySubmodules = &RepositorySubmodulesService{client: c}
	c.ResourceGroup = &ResourceGroupService{client: c}
	c.ResourceIterationEvents = &ResourceIterationEventsService{client: c}
	c.ResourceLabelEvents = &ResourceLabelEventsService{client: c}
	c.ResourceMilestoneEvents = &ResourceMilestoneEventsService{client: c}
	c.ResourceStateEvents = &ResourceStateEventsService{client: c}
	c.ResourceWeightEvents = &ResourceWeightEventsService{client: c}
	c.Runners = &RunnersService{client: c}
	c.Search = &SearchService{client: c}
	c.Services = &ServicesService{client: c}
	c.Settings = &SettingsService{client: c}
	c.Sidekiq = &SidekiqService{client: c}
	c.Snippets = &SnippetsService{client: c}
	c.SnippetRepositoryStorageMove = &SnippetRepositoryStorageMoveService{client: c}
	c.SystemHooks = &SystemHooksService{client: c}
	c.Tags = &TagsService{client: c}
	c.Todos = &TodosService{client: c}
	c.Topics = &TopicsService{client: c}
	c.Users = &UsersService{client: c}
	c.Validate = &ValidateService{client: c}
	c.Version = &VersionService{client: c}
	c.Wikis = &WikisService{client: c}

	return c, nil
}

// retryHTTPCheck provides a callback for Client.CheckRetry which
// will retry both rate limit (429) and server (>= 500) errors.
func (c *Client) retryHTTPCheck(ctx context.Context, resp *http.Response, err error) (bool, error) {
	if ctx.Err() != nil {
		return false, ctx.Err()
	}
	if err != nil {
		return false, err
	}
	if !c.disableRetries && (resp.StatusCode == 429 || resp.StatusCode >= 500) {
		return true, nil
	}
	return false, nil
}

// retryHTTPBackoff provides a generic callback for Client.Backoff which
// will pass through all calls based on the status code of the response.
func (c *Client) retryHTTPBackoff(min, max time.Duration, attemptNum int, resp *http.Response) time.Duration {
	// Use the rate limit backoff function when we are rate limited.
	if resp != nil && resp.StatusCode == 429 {
		return rateLimitBackoff(min, max, attemptNum, resp)
	}

	// Set custom duration's when we experience a service interruption.
	min = 700 * time.Millisecond
	max = 900 * time.Millisecond

	return retryablehttp.LinearJitterBackoff(min, max, attemptNum, resp)
}

// rateLimitBackoff provides a callback for Client.Backoff which will use the
// RateLimit-Reset header to determine the time to wait. We add some jitter
// to prevent a thundering herd.
//
// min and max are mainly used for bounding the jitter that will be added to
// the reset time retrieved from the headers. But if the final wait time is
// less then min, min will be used instead.
func rateLimitBackoff(min, max time.Duration, attemptNum int, resp *http.Response) time.Duration {
	// rnd is used to generate pseudo-random numbers.
	rnd := rand.New(rand.NewSource(time.Now().UnixNano()))

	// First create some jitter bounded by the min and max durations.
	jitter := time.Duration(rnd.Float64() * float64(max-min))

	if resp != nil {
		if v := resp.Header.Get(headerRateReset); v != "" {
			if reset, _ := strconv.ParseInt(v, 10, 64); reset > 0 {
				// Only update min if the given time to wait is longer.
				if wait := time.Until(time.Unix(reset, 0)); wait > min {
					min = wait
				}
			}
		} else {
			// In case the RateLimit-Reset header is not set, back off an additional
			// 100% exponentially. With the default milliseconds being set to 100 for
			// `min`, this makes the 5th retry wait 3.2 seconds (3,200 ms) by default.
			min = time.Duration(float64(min) * math.Pow(2, float64(attemptNum)))
		}
	}

	return min + jitter
}

// configureLimiter configures the rate limiter.
func (c *Client) configureLimiter(ctx context.Context, headers http.Header) {
	if v := headers.Get(headerRateLimit); v != "" {
		if rateLimit, _ := strconv.ParseFloat(v, 64); rateLimit > 0 {
			// The rate limit is based on requests per minute, so for our limiter to
			// work correctly we divide the limit by 60 to get the limit per second.
			rateLimit /= 60

			// Configure the limit and burst using a split of 2/3 for the limit and
			// 1/3 for the burst. This enables clients to burst 1/3 of the allowed
			// calls before the limiter kicks in. The remaining calls will then be
			// spread out evenly using intervals of time.Second / limit which should
			// prevent hitting the rate limit.
			limit := rate.Limit(rateLimit * 0.66)
			burst := int(rateLimit * 0.33)

			// Need at least one allowed to burst or x/time will throw an error
			if burst == 0 {
				burst = 1
			}

			// Create a new limiter using the calculated values.
			c.limiter = rate.NewLimiter(limit, burst)

			// Call the limiter once as we have already made a request
			// to get the headers and the limiter is not aware of this.
			c.limiter.Wait(ctx)
		}
	}
}

// BaseURL return a copy of the baseURL.
func (c *Client) BaseURL() *url.URL {
	u := *c.baseURL
	return &u
}

// setBaseURL sets the base URL for API requests to a custom endpoint.
func (c *Client) setBaseURL(urlStr string) error {
	// Make sure the given URL end with a slash
	if !strings.HasSuffix(urlStr, "/") {
		urlStr += "/"
	}

	baseURL, err := url.Parse(urlStr)
	if err != nil {
		return err
	}

	if !strings.HasSuffix(baseURL.Path, apiVersionPath) {
		baseURL.Path += apiVersionPath
	}

	// Update the base URL of the client.
	c.baseURL = baseURL

	return nil
}

// NewRequest creates a new API request. The method expects a relative URL
// path that will be resolved relative to the base URL of the Client.
// Relative URL paths 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, path string, opt interface{}, options []RequestOptionFunc) (*retryablehttp.Request, error) {
	u := *c.baseURL
	unescaped, err := url.PathUnescape(path)
	if err != nil {
		return nil, err
	}

	// Set the encoded path data
	u.RawPath = c.baseURL.Path + path
	u.Path = c.baseURL.Path + unescaped

	// Create a request specific headers map.
	reqHeaders := make(http.Header)
	reqHeaders.Set("Accept", "application/json")

	if c.UserAgent != "" {
		reqHeaders.Set("User-Agent", c.UserAgent)
	}

	var body interface{}
	switch {
	case method == http.MethodPatch || method == http.MethodPost || method == http.MethodPut:
		reqHeaders.Set("Content-Type", "application/json")

		if opt != nil {
			body, err = json.Marshal(opt)
			if err != nil {
				return nil, err
			}
		}
	case opt != nil:
		q, err := query.Values(opt)
		if err != nil {
			return nil, err
		}
		u.RawQuery = q.Encode()
	}

	req, err := retryablehttp.NewRequest(method, u.String(), body)
	if err != nil {
		return nil, err
	}

	for _, fn := range append(c.defaultRequestOptions, options...) {
		if fn == nil {
			continue
		}
		if err := fn(req); err != nil {
			return nil, err
		}
	}

	// Set the request specific headers.
	for k, v := range reqHeaders {
		req.Header[k] = v
	}

	return req, nil
}

// UploadRequest creates an API request for uploading a file. The method
// expects a relative URL path that will be resolved relative to the base
// URL of the Client. Relative URL paths 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) UploadRequest(method, path string, content io.Reader, filename string, uploadType UploadType, opt interface{}, options []RequestOptionFunc) (*retryablehttp.Request, error) {
	u := *c.baseURL
	unescaped, err := url.PathUnescape(path)
	if err != nil {
		return nil, err
	}

	// Set the encoded path data
	u.RawPath = c.baseURL.Path + path
	u.Path = c.baseURL.Path + unescaped

	// Create a request specific headers map.
	reqHeaders := make(http.Header)
	reqHeaders.Set("Accept", "application/json")

	if c.UserAgent != "" {
		reqHeaders.Set("User-Agent", c.UserAgent)
	}

	b := new(bytes.Buffer)
	w := multipart.NewWriter(b)

	fw, err := w.CreateFormFile(string(uploadType), filename)
	if err != nil {
		return nil, err
	}

	if _, err := io.Copy(fw, content); err != nil {
		return nil, err
	}

	if opt != nil {
		fields, err := query.Values(opt)
		if err != nil {
			return nil, err
		}
		for name := range fields {
			if err = w.WriteField(name, fmt.Sprintf("%v", fields.Get(name))); err != nil {
				return nil, err
			}
		}
	}

	if err = w.Close(); err != nil {
		return nil, err
	}

	reqHeaders.Set("Content-Type", w.FormDataContentType())

	req, err := retryablehttp.NewRequest(method, u.String(), b)
	if err != nil {
		return nil, err
	}

	for _, fn := range append(c.defaultRequestOptions, options...) {
		if fn == nil {
			continue
		}
		if err := fn(req); err != nil {
			return nil, err
		}
	}

	// Set the request specific headers.
	for k, v := range reqHeaders {
		req.Header[k] = v
	}

	return req, nil
}

// Response is a GitLab API response. This wraps the standard http.Response
// returned from GitLab and provides convenient access to things like
// pagination links.
type Response struct {
	*http.Response

	// Fields used for offset-based pagination.
	TotalItems   int
	TotalPages   int
	ItemsPerPage int
	CurrentPage  int
	NextPage     int
	PreviousPage int

	// Fields used for keyset-based pagination.
	PreviousLink string
	NextLink     string
	FirstLink    string
	LastLink     string
}

// newResponse creates a new Response for the provided http.Response.
func newResponse(r *http.Response) *Response {
	response := &Response{Response: r}
	response.populatePageValues()
	response.populateLinkValues()
	return response
}

const (
	// Headers used for offset-based pagination.
	xTotal      = "X-Total"
	xTotalPages = "X-Total-Pages"
	xPerPage    = "X-Per-Page"
	xPage       = "X-Page"
	xNextPage   = "X-Next-Page"
	xPrevPage   = "X-Prev-Page"

	// Headers used for keyset-based pagination.
	linkPrev  = "prev"
	linkNext  = "next"
	linkFirst = "first"
	linkLast  = "last"
)

// populatePageValues parses the HTTP Link response headers and populates the
// various pagination link values in the Response.
func (r *Response) populatePageValues() {
	if totalItems := r.Header.Get(xTotal); totalItems != "" {
		r.TotalItems, _ = strconv.Atoi(totalItems)
	}
	if totalPages := r.Header.Get(xTotalPages); totalPages != "" {
		r.TotalPages, _ = strconv.Atoi(totalPages)
	}
	if itemsPerPage := r.Header.Get(xPerPage); itemsPerPage != "" {
		r.ItemsPerPage, _ = strconv.Atoi(itemsPerPage)
	}
	if currentPage := r.Header.Get(xPage); currentPage != "" {
		r.CurrentPage, _ = strconv.Atoi(currentPage)
	}
	if nextPage := r.Header.Get(xNextPage); nextPage != "" {
		r.NextPage, _ = strconv.Atoi(nextPage)
	}
	if previousPage := r.Header.Get(xPrevPage); previousPage != "" {
		r.PreviousPage, _ = strconv.Atoi(previousPage)
	}
}

func (r *Response) populateLinkValues() {
	if link := r.Header.Get("Link"); link != "" {
		for _, link := range strings.Split(link, ",") {
			parts := strings.Split(link, ";")
			if len(parts) < 2 {
				continue
			}

			linkType := strings.Trim(strings.Split(parts[1], "=")[1], "\"")
			linkValue := strings.Trim(parts[0], "< >")

			switch linkType {
			case linkPrev:
				r.PreviousLink = linkValue
			case linkNext:
				r.NextLink = linkValue
			case linkFirst:
				r.FirstLink = linkValue
			case linkLast:
				r.LastLink = linkValue
			}
		}
	}
}

// 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.
func (c *Client) Do(req *retryablehttp.Request, v interface{}) (*Response, error) {
	// Wait will block until the limiter can obtain a new token.
	err := c.limiter.Wait(req.Context())
	if err != nil {
		return nil, err
	}

	// Set the correct authentication header. If using basic auth, then check
	// if we already have a token and if not first authenticate and get one.
	var basicAuthToken string
	switch c.authType {
	case BasicAuth:
		c.tokenLock.RLock()
		basicAuthToken = c.token
		c.tokenLock.RUnlock()
		if basicAuthToken == "" {
			// If we don't have a token yet, we first need to request one.
			basicAuthToken, err = c.requestOAuthToken(req.Context(), basicAuthToken)
			if err != nil {
				return nil, err
			}
		}
		req.Header.Set("Authorization", "Bearer "+basicAuthToken)
	case JobToken:
		if values := req.Header.Values("JOB-TOKEN"); len(values) == 0 {
			req.Header.Set("JOB-TOKEN", c.token)
		}
	case OAuthToken:
		if values := req.Header.Values("Authorization"); len(values) == 0 {
			req.Header.Set("Authorization", "Bearer "+c.token)
		}
	case PrivateToken:
		if values := req.Header.Values("PRIVATE-TOKEN"); len(values) == 0 {
			req.Header.Set("PRIVATE-TOKEN", c.token)
		}
	}

	resp, err := c.client.Do(req)
	if err != nil {
		return nil, err
	}

	if resp.StatusCode == http.StatusUnauthorized && c.authType == BasicAuth {
		resp.Body.Close()
		// The token most likely expired, so we need to request a new one and try again.
		if _, err := c.requestOAuthToken(req.Context(), basicAuthToken); err != nil {
			return nil, err
		}
		return c.Do(req, v)
	}
	defer resp.Body.Close()
	defer io.Copy(io.Discard, resp.Body)

	// If not yet configured, try to configure the rate limiter
	// using the response headers we just received. Fail silently
	// so the limiter will remain disabled in case of an error.
	c.configureLimiterOnce.Do(func() { c.configureLimiter(req.Context(), resp.Header) })

	response := newResponse(resp)

	err = CheckResponse(resp)
	if err != nil {
		// Even though there was an error, we still return the response
		// in case the caller wants to inspect it further.
		return response, err
	}

	if v != nil {
		if w, ok := v.(io.Writer); ok {
			_, err = io.Copy(w, resp.Body)
		} else {
			err = json.NewDecoder(resp.Body).Decode(v)
		}
	}

	return response, err
}

func (c *Client) requestOAuthToken(ctx context.Context, token string) (string, error) {
	c.tokenLock.Lock()
	defer c.tokenLock.Unlock()

	// Return early if the token was updated while waiting for the lock.
	if c.token != token {
		return c.token, nil
	}

	config := &oauth2.Config{
		Endpoint: oauth2.Endpoint{
			AuthURL:  strings.TrimSuffix(c.baseURL.String(), apiVersionPath) + "oauth/authorize",
			TokenURL: strings.TrimSuffix(c.baseURL.String(), apiVersionPath) + "oauth/token",
		},
	}

	ctx = context.WithValue(ctx, oauth2.HTTPClient, c.client.HTTPClient)
	t, err := config.PasswordCredentialsToken(ctx, c.username, c.password)
	if err != nil {
		return "", err
	}
	c.token = t.AccessToken

	return c.token, nil
}

// Helper function to accept and format both the project ID or name as project
// identifier for all API calls.
func parseID(id interface{}) (string, error) {
	switch v := id.(type) {
	case int:
		return strconv.Itoa(v), nil
	case string:
		return v, nil
	default:
		return "", fmt.Errorf("invalid ID type %#v, the ID must be an int or a string", id)
	}
}

// Helper function to escape a project identifier.
func PathEscape(s string) string {
	return strings.ReplaceAll(url.PathEscape(s), ".", "%2E")
}

// An ErrorResponse reports one or more errors caused by an API request.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/index.html#data-validation-and-error-reporting
type ErrorResponse struct {
	Body     []byte
	Response *http.Response
	Message  string
}

func (e *ErrorResponse) Error() string {
	path, _ := url.QueryUnescape(e.Response.Request.URL.Path)
	url := fmt.Sprintf("%s://%s%s", e.Response.Request.URL.Scheme, e.Response.Request.URL.Host, path)

	if e.Message == "" {
		return fmt.Sprintf("%s %s: %d", e.Response.Request.Method, url, e.Response.StatusCode)
	} else {
		return fmt.Sprintf("%s %s: %d %s", e.Response.Request.Method, url, e.Response.StatusCode, e.Message)
	}
}

// CheckResponse checks the API response for errors, and returns them if present.
func CheckResponse(r *http.Response) error {
	switch r.StatusCode {
	case 200, 201, 202, 204, 304:
		return nil
	case 404:
		return ErrNotFound
	}

	errorResponse := &ErrorResponse{Response: r}

	data, err := io.ReadAll(r.Body)
	if err == nil && strings.TrimSpace(string(data)) != "" {
		errorResponse.Body = data

		var raw interface{}
		if err := json.Unmarshal(data, &raw); err != nil {
			errorResponse.Message = fmt.Sprintf("failed to parse unknown error format: %s", data)
		} else {
			errorResponse.Message = parseError(raw)
		}
	}

	return errorResponse
}

// Format:
//
//	{
//	    "message": {
//	        "": [
//	            "",
//	            "",
//	            ...
//	        ],
//	        "": {
//	            "": [
//	                "",
//	                "",
//	                ...
//	            ],
//	        }
//	    },
//	    "error": ""
//	}
func parseError(raw interface{}) string {
	switch raw := raw.(type) {
	case string:
		return raw

	case []interface{}:
		var errs []string
		for _, v := range raw {
			errs = append(errs, parseError(v))
		}
		return fmt.Sprintf("[%s]", strings.Join(errs, ", "))

	case map[string]interface{}:
		var errs []string
		for k, v := range raw {
			errs = append(errs, fmt.Sprintf("{%s: %s}", k, parseError(v)))
		}
		sort.Strings(errs)
		return strings.Join(errs, ", ")

	default:
		return fmt.Sprintf("failed to parse unexpected error type: %T", raw)
	}
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/gitlab_test.go000066400000000000000000000263021475761473200237120ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"bytes"
	"context"
	"errors"
	"fmt"
	"io"
	"net/http"
	"net/http/httptest"
	"os"
	"strings"
	"testing"
	"time"

	retryablehttp "github.com/hashicorp/go-retryablehttp"
)

var timeLayout = "2006-01-02T15:04:05Z07:00"

// setup sets up a test HTTP server along with a gitlab.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(t *testing.T) (*http.ServeMux, *Client) {
	// mux is the HTTP request multiplexer used with the test server.
	mux := http.NewServeMux()

	// server is a test HTTP server used to provide mock API responses.
	server := httptest.NewServer(mux)
	t.Cleanup(server.Close)

	// client is the Gitlab client being tested.
	client, err := NewClient("",
		WithBaseURL(server.URL),
		// Disable backoff to speed up tests that expect errors.
		WithCustomBackoff(func(_, _ time.Duration, _ int, _ *http.Response) time.Duration {
			return 0
		}),
	)
	if err != nil {
		t.Fatalf("Failed to create client: %v", err)
	}

	return mux, client
}

func testURL(t *testing.T, r *http.Request, want string) {
	if got := r.RequestURI; got != want {
		t.Errorf("Request url: %+v, want %s", got, want)
	}
}

func testMethod(t *testing.T, r *http.Request, want string) {
	if got := r.Method; got != want {
		t.Errorf("Request method: %s, want %s", got, want)
	}
}

func testBody(t *testing.T, r *http.Request, want string) {
	buffer := new(bytes.Buffer)
	_, err := buffer.ReadFrom(r.Body)
	if err != nil {
		t.Fatalf("Failed to Read Body: %v", err)
	}

	if got := buffer.String(); got != want {
		t.Errorf("Request body: %s, want %s", got, want)
	}
}

func testParams(t *testing.T, r *http.Request, want string) {
	if got := r.URL.RawQuery; got != want {
		t.Errorf("Request query: %s, want %s", got, want)
	}
}

func mustWriteHTTPResponse(t *testing.T, w io.Writer, fixturePath string) {
	f, err := os.Open(fixturePath)
	if err != nil {
		t.Fatalf("error opening fixture file: %v", err)
	}

	if _, err = io.Copy(w, f); err != nil {
		t.Fatalf("error writing response: %v", err)
	}
}

func errorOption(*retryablehttp.Request) error {
	return errors.New("RequestOptionFunc returns an error")
}

func TestNewClient(t *testing.T) {
	c, err := NewClient("")
	if err != nil {
		t.Fatalf("Failed to create client: %v", err)
	}

	expectedBaseURL := defaultBaseURL + apiVersionPath

	if c.BaseURL().String() != expectedBaseURL {
		t.Errorf("NewClient BaseURL is %s, want %s", c.BaseURL().String(), expectedBaseURL)
	}
	if c.UserAgent != userAgent {
		t.Errorf("NewClient UserAgent is %s, want %s", c.UserAgent, userAgent)
	}
}

func TestCheckResponse(t *testing.T) {
	c, err := NewClient("")
	if err != nil {
		t.Fatalf("Failed to create client: %v", err)
	}

	req, err := c.NewRequest(http.MethodGet, "test", nil, nil)
	if err != nil {
		t.Fatalf("Failed to create request: %v", err)
	}

	resp := &http.Response{
		Request:    req.Request,
		StatusCode: http.StatusBadRequest,
		Body: io.NopCloser(strings.NewReader(`
		{
			"message": {
				"prop1": [
					"message 1",
					"message 2"
				],
				"prop2":[
					"message 3"
				],
				"embed1": {
					"prop3": [
						"msg 1",
						"msg2"
					]
				},
				"embed2": {
					"prop4": [
						"some msg"
					]
				}
			},
			"error": "message 1"
		}`)),
	}

	errResp := CheckResponse(resp)
	if errResp == nil {
		t.Fatal("Expected error response.")
	}

	want := "GET https://gitlab.com/api/v4/test: 400 {error: message 1}, {message: {embed1: {prop3: [msg 1, msg2]}}, {embed2: {prop4: [some msg]}}, {prop1: [message 1, message 2]}, {prop2: [message 3]}}"

	if errResp.Error() != want {
		t.Errorf("Expected error: %s, got %s", want, errResp.Error())
	}
}

func TestCheckResponseOnUnknownErrorFormat(t *testing.T) {
	c, err := NewClient("")
	if err != nil {
		t.Fatalf("Failed to create client: %v", err)
	}

	req, err := c.NewRequest(http.MethodGet, "test", nil, nil)
	if err != nil {
		t.Fatalf("Failed to create request: %v", err)
	}

	resp := &http.Response{
		Request:    req.Request,
		StatusCode: http.StatusBadRequest,
		Body:       io.NopCloser(strings.NewReader("some error message but not JSON")),
	}

	errResp := CheckResponse(resp)
	if errResp == nil {
		t.Fatal("Expected error response.")
	}

	want := "GET https://gitlab.com/api/v4/test: 400 failed to parse unknown error format: some error message but not JSON"

	if errResp.Error() != want {
		t.Errorf("Expected error: %s, got %s", want, errResp.Error())
	}
}

func TestCheckResponseOnHeadRequestError(t *testing.T) {
	c, err := NewClient("")
	if err != nil {
		t.Fatalf("Failed to create client: %v", err)
	}

	req, err := c.NewRequest(http.MethodHead, "test", nil, nil)
	if err != nil {
		t.Fatalf("Failed to create request: %v", err)
	}

	resp := &http.Response{
		Request:    req.Request,
		StatusCode: http.StatusNotFound,
		Body:       nil,
	}

	errResp := CheckResponse(resp)
	if errResp == nil {
		t.Fatal("Expected error response.")
	}

	want := "404 Not Found"

	if errResp.Error() != want {
		t.Errorf("Expected error: %s, got %s", want, errResp.Error())
	}
}

func TestRequestWithContext(t *testing.T) {
	c, err := NewClient("")
	if err != nil {
		t.Fatalf("Failed to create client: %v", err)
	}

	ctx, cancel := context.WithCancel(context.Background())
	req, err := c.NewRequest(http.MethodGet, "test", nil, []RequestOptionFunc{WithContext(ctx)})
	if err != nil {
		t.Fatalf("Failed to create request: %v", err)
	}
	defer cancel()

	if req.Context() != ctx {
		t.Fatal("Context was not set correctly")
	}
}

func loadFixture(t *testing.T, filePath string) []byte {
	t.Helper()
	content, err := os.ReadFile(filePath)
	if err != nil {
		t.Fatal(err)
	}

	return content
}

func TestPathEscape(t *testing.T) {
	want := "diaspora%2Fdiaspora"
	got := PathEscape("diaspora/diaspora")
	if want != got {
		t.Errorf("Expected: %s, got %s", want, got)
	}
}

func TestPaginationPopulatePageValuesEmpty(t *testing.T) {
	wantPageHeaders := map[string]int{
		xTotal:      0,
		xTotalPages: 0,
		xPerPage:    0,
		xPage:       0,
		xNextPage:   0,
		xPrevPage:   0,
	}
	wantLinkHeaders := map[string]string{
		linkPrev:  "",
		linkNext:  "",
		linkFirst: "",
		linkLast:  "",
	}

	r := newResponse(&http.Response{
		Header: http.Header{},
	})

	gotPageHeaders := map[string]int{
		xTotal:      r.TotalItems,
		xTotalPages: r.TotalPages,
		xPerPage:    r.ItemsPerPage,
		xPage:       r.CurrentPage,
		xNextPage:   r.NextPage,
		xPrevPage:   r.PreviousPage,
	}
	for k, v := range wantPageHeaders {
		if v != gotPageHeaders[k] {
			t.Errorf("For %s, expected %d, got %d", k, v, gotPageHeaders[k])
		}
	}

	gotLinkHeaders := map[string]string{
		linkPrev:  r.PreviousLink,
		linkNext:  r.NextLink,
		linkFirst: r.FirstLink,
		linkLast:  r.LastLink,
	}
	for k, v := range wantLinkHeaders {
		if v != gotLinkHeaders[k] {
			t.Errorf("For %s, expected %s, got %s", k, v, gotLinkHeaders[k])
		}
	}
}

func TestPaginationPopulatePageValuesOffset(t *testing.T) {
	wantPageHeaders := map[string]int{
		xTotal:      100,
		xTotalPages: 5,
		xPerPage:    20,
		xPage:       2,
		xNextPage:   3,
		xPrevPage:   1,
	}
	wantLinkHeaders := map[string]string{
		linkPrev:  "https://gitlab.example.com/api/v4/projects/8/issues/8/notes?page=1&per_page=3",
		linkNext:  "https://gitlab.example.com/api/v4/projects/8/issues/8/notes?page=3&per_page=3",
		linkFirst: "https://gitlab.example.com/api/v4/projects/8/issues/8/notes?page=1&per_page=3",
		linkLast:  "https://gitlab.example.com/api/v4/projects/8/issues/8/notes?page=3&per_page=3",
	}

	h := http.Header{}
	for k, v := range wantPageHeaders {
		h.Add(k, fmt.Sprint(v))
	}
	var linkHeaderComponents []string
	for k, v := range wantLinkHeaders {
		if v != "" {
			linkHeaderComponents = append(linkHeaderComponents, fmt.Sprintf("<%s>; rel=\"%s\"", v, k))
		}
	}
	h.Add("Link", strings.Join(linkHeaderComponents, ", "))

	r := newResponse(&http.Response{
		Header: h,
	})

	gotPageHeaders := map[string]int{
		xTotal:      r.TotalItems,
		xTotalPages: r.TotalPages,
		xPerPage:    r.ItemsPerPage,
		xPage:       r.CurrentPage,
		xNextPage:   r.NextPage,
		xPrevPage:   r.PreviousPage,
	}
	for k, v := range wantPageHeaders {
		if v != gotPageHeaders[k] {
			t.Errorf("For %s, expected %d, got %d", k, v, gotPageHeaders[k])
		}
	}

	gotLinkHeaders := map[string]string{
		linkPrev:  r.PreviousLink,
		linkNext:  r.NextLink,
		linkFirst: r.FirstLink,
		linkLast:  r.LastLink,
	}
	for k, v := range wantLinkHeaders {
		if v != gotLinkHeaders[k] {
			t.Errorf("For %s, expected %s, got %s", k, v, gotLinkHeaders[k])
		}
	}
}

func TestPaginationPopulatePageValuesKeyset(t *testing.T) {
	wantPageHeaders := map[string]int{
		xTotal:      0,
		xTotalPages: 0,
		xPerPage:    0,
		xPage:       0,
		xNextPage:   0,
		xPrevPage:   0,
	}
	wantLinkHeaders := map[string]string{
		linkPrev:  "",
		linkFirst: "",
		linkLast:  "",
	}

	h := http.Header{}
	for k, v := range wantPageHeaders {
		h.Add(k, fmt.Sprint(v))
	}
	var linkHeaderComponents []string
	for k, v := range wantLinkHeaders {
		if v != "" {
			linkHeaderComponents = append(linkHeaderComponents, fmt.Sprintf("<%s>; rel=\"%s\"", v, k))
		}
	}
	h.Add("Link", strings.Join(linkHeaderComponents, ", "))

	r := newResponse(&http.Response{
		Header: h,
	})

	gotPageHeaders := map[string]int{
		xTotal:      r.TotalItems,
		xTotalPages: r.TotalPages,
		xPerPage:    r.ItemsPerPage,
		xPage:       r.CurrentPage,
		xNextPage:   r.NextPage,
		xPrevPage:   r.PreviousPage,
	}
	for k, v := range wantPageHeaders {
		if v != gotPageHeaders[k] {
			t.Errorf("For %s, expected %d, got %d", k, v, gotPageHeaders[k])
		}
	}
}

func TestExponentialBackoffLogic(t *testing.T) {
	// Can't use the default `setup` because it disabled the backoff
	mux := http.NewServeMux()
	server := httptest.NewServer(mux)
	t.Cleanup(server.Close)
	client, err := NewClient("",
		WithBaseURL(server.URL),
	)
	if err != nil {
		t.Fatalf("Failed to create client: %v", err)
	}

	// Create a method that returns 429
	mux.HandleFunc("/api/v4/projects/1", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		w.WriteHeader(http.StatusTooManyRequests)
	})

	// Measure the time at the start of the test
	start := time.Now()

	// Send a request (which will get a bunch of 429s)
	// None of the responses matter, so ignore them all
	_, resp, _ := client.Projects.GetProject(1, nil)
	end := time.Now()

	// The test should run for _at least_ 3,200 milliseconds
	duration := float64(end.Sub(start))
	if duration < float64(3200*time.Millisecond) {
		t.Fatal("Wait was shorter than expected. Expected a minimum of 5 retries taking 3200 milliseconds, got:", duration)
	}
	if resp.StatusCode != 429 {
		t.Fatal("Expected to get a 429 code given the server is hard-coded to return this. Received instead:", resp.StatusCode)
	}
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/go.mod000066400000000000000000000006611475761473200221700ustar00rootroot00000000000000module gitlab.com/gitlab-org/api/client-go

go 1.22

require (
	github.com/google/go-querystring v1.1.0
	github.com/hashicorp/go-cleanhttp v0.5.2
	github.com/hashicorp/go-retryablehttp v0.7.7
	github.com/stretchr/testify v1.10.0
	golang.org/x/oauth2 v0.25.0
	golang.org/x/time v0.9.0
)

require (
	github.com/davecgh/go-spew v1.1.1 // indirect
	github.com/pmezard/go-difflib v1.0.0 // indirect
	gopkg.in/yaml.v3 v3.0.1 // indirect
)
golang-gitlab-gitlab-org-api-client-go-0.123.0/go.sum000066400000000000000000000056301475761473200222160ustar00rootroot00000000000000github.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/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
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-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=
github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU=
github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
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.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70=
golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
golang-gitlab-gitlab-org-api-client-go-0.123.0/group_access_tokens.go000066400000000000000000000144711475761473200254550ustar00rootroot00000000000000//
// Copyright 2022, Masahiro Yoshida
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"fmt"
	"net/http"
	"time"
)

// GroupAccessTokensService handles communication with the
// groups access tokens related methods of the GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/group_access_tokens.html
type GroupAccessTokensService struct {
	client *Client
}

// GroupAccessToken represents a GitLab group access token.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/group_access_tokens.html
type GroupAccessToken struct {
	ID          int              `json:"id"`
	UserID      int              `json:"user_id"`
	Name        string           `json:"name"`
	Scopes      []string         `json:"scopes"`
	CreatedAt   *time.Time       `json:"created_at"`
	ExpiresAt   *ISOTime         `json:"expires_at"`
	LastUsedAt  *time.Time       `json:"last_used_at"`
	Active      bool             `json:"active"`
	Revoked     bool             `json:"revoked"`
	Token       string           `json:"token"`
	AccessLevel AccessLevelValue `json:"access_level"`
}

func (v GroupAccessToken) String() string {
	return Stringify(v)
}

// ListGroupAccessTokensOptions represents the available options for
// listing variables in a group.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_access_tokens.html#list-group-access-tokens
type ListGroupAccessTokensOptions ListOptions

// ListGroupAccessTokens gets a list of all group access tokens in a group.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_access_tokens.html#list-group-access-tokens
func (s *GroupAccessTokensService) ListGroupAccessTokens(gid interface{}, opt *ListGroupAccessTokensOptions, options ...RequestOptionFunc) ([]*GroupAccessToken, *Response, error) {
	groups, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/access_tokens", PathEscape(groups))

	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var gats []*GroupAccessToken
	resp, err := s.client.Do(req, &gats)
	if err != nil {
		return nil, resp, err
	}

	return gats, resp, nil
}

// GetGroupAccessToken gets a single group access tokens in a group.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_access_tokens.html#get-a-group-access-token
func (s *GroupAccessTokensService) GetGroupAccessToken(gid interface{}, id int, options ...RequestOptionFunc) (*GroupAccessToken, *Response, error) {
	groups, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/access_tokens/%d", PathEscape(groups), id)

	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	gat := new(GroupAccessToken)
	resp, err := s.client.Do(req, &gat)
	if err != nil {
		return nil, resp, err
	}

	return gat, resp, nil
}

// CreateGroupAccessTokenOptions represents the available CreateVariable()
// options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_access_tokens.html#create-a-group-access-token
type CreateGroupAccessTokenOptions struct {
	Name        *string           `url:"name,omitempty" json:"name,omitempty"`
	Scopes      *[]string         `url:"scopes,omitempty" json:"scopes,omitempty"`
	AccessLevel *AccessLevelValue `url:"access_level,omitempty" json:"access_level,omitempty"`
	ExpiresAt   *ISOTime          `url:"expires_at,omitempty" json:"expires_at,omitempty"`
}

// CreateGroupAccessToken creates a new group access token.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_access_tokens.html#create-a-group-access-token
func (s *GroupAccessTokensService) CreateGroupAccessToken(gid interface{}, opt *CreateGroupAccessTokenOptions, options ...RequestOptionFunc) (*GroupAccessToken, *Response, error) {
	groups, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/access_tokens", PathEscape(groups))

	req, err := s.client.NewRequest(http.MethodPost, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	pat := new(GroupAccessToken)
	resp, err := s.client.Do(req, pat)
	if err != nil {
		return nil, resp, err
	}

	return pat, resp, nil
}

// RotateGroupAccessTokenOptions represents the available RotateGroupAccessToken()
// options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_access_tokens.html#rotate-a-group-access-token
type RotateGroupAccessTokenOptions struct {
	ExpiresAt *ISOTime `url:"expires_at,omitempty" json:"expires_at,omitempty"`
}

// RotateGroupAccessToken revokes a group access token and returns a new group
// access token that expires in one week per default.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_access_tokens.html#rotate-a-group-access-token
func (s *GroupAccessTokensService) RotateGroupAccessToken(gid interface{}, id int, opt *RotateGroupAccessTokenOptions, options ...RequestOptionFunc) (*GroupAccessToken, *Response, error) {
	groups, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/access_tokens/%d/rotate", PathEscape(groups), id)
	req, err := s.client.NewRequest(http.MethodPost, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	gat := new(GroupAccessToken)
	resp, err := s.client.Do(req, gat)
	if err != nil {
		return nil, resp, err
	}

	return gat, resp, nil
}

// RevokeGroupAccessToken revokes a group access token.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_access_tokens.html#revoke-a-group-access-token
func (s *GroupAccessTokensService) RevokeGroupAccessToken(gid interface{}, id int, options ...RequestOptionFunc) (*Response, error) {
	groups, err := parseID(gid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("groups/%s/access_tokens/%d", PathEscape(groups), id)

	req, err := s.client.NewRequest(http.MethodDelete, u, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/group_access_tokens_test.go000066400000000000000000000140421475761473200265060ustar00rootroot00000000000000//
// Copyright 2022, Masahiro Yoshida
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"net/http"
	"reflect"
	"testing"
	"time"
)

func TestListGroupAccessTokens(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1/access_tokens", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		mustWriteHTTPResponse(t, w, "testdata/list_group_access_tokens.json")
	})

	groupAccessTokens, _, err := client.GroupAccessTokens.ListGroupAccessTokens(1, &ListGroupAccessTokensOptions{Page: 1, PerPage: 20})
	if err != nil {
		t.Errorf("GroupAccessTokens.ListGroupAccessTokens returned error: %v", err)
	}

	time1, err := time.Parse(time.RFC3339, "2021-03-09T21:11:47.271Z")
	if err != nil {
		t.Errorf("GroupAccessTokens.ListGroupAccessTokens returned error: %v", err)
	}
	time2, err := time.Parse(time.RFC3339, "2021-03-09T21:11:47.340Z")
	if err != nil {
		t.Errorf("GroupAccessTokens.ListGroupAccessTokens returned error: %v", err)
	}
	time3, err := time.Parse(time.RFC3339, "2021-03-10T21:11:47.271Z")
	if err != nil {
		t.Errorf("GroupAccessTokens.ListGroupAccessTokens returned error: %v", err)
	}

	want := []*GroupAccessToken{
		{
			ID:          1876,
			UserID:      2453,
			Name:        "token 10",
			Scopes:      []string{"api", "read_api", "read_repository", "write_repository"},
			CreatedAt:   &time1,
			LastUsedAt:  &time3,
			Active:      true,
			Revoked:     false,
			AccessLevel: AccessLevelValue(40),
		},
		{
			ID:          1877,
			UserID:      2456,
			Name:        "token 8",
			Scopes:      []string{"api", "read_api", "read_repository", "write_repository"},
			CreatedAt:   &time2,
			Active:      true,
			Revoked:     false,
			AccessLevel: AccessLevelValue(30),
		},
	}

	if !reflect.DeepEqual(want, groupAccessTokens) {
		t.Errorf("GroupAccessTokens.ListGroupAccessTokens returned %+v, want %+v", groupAccessTokens, want)
	}
}

func TestGetGroupAccessToken(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1/access_tokens/1", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		mustWriteHTTPResponse(t, w, "testdata/get_group_access_token.json")
	})

	groupAccessToken, _, err := client.GroupAccessTokens.GetGroupAccessToken(1, 1)
	if err != nil {
		t.Errorf("GroupAccessTokens.GetGroupAccessToken returned error: %v", err)
	}

	createdAt, err := time.Parse(time.RFC3339, "2021-03-09T21:11:47.271Z")
	if err != nil {
		t.Errorf("GroupAccessTokens.GetGroupAccessToken returned error: %v", err)
	}

	want := &GroupAccessToken{
		ID:          1,
		UserID:      2453,
		Name:        "token 10",
		Scopes:      []string{"api", "read_api", "read_repository", "write_repository"},
		CreatedAt:   &createdAt,
		Active:      true,
		Revoked:     false,
		AccessLevel: AccessLevelValue(40),
	}

	if !reflect.DeepEqual(want, groupAccessToken) {
		t.Errorf("GroupAccessTokens.GetGroupAccessToken returned %+v, want %+v", groupAccessToken, want)
	}
}

func TestCreateGroupAccessToken(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1/access_tokens", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		mustWriteHTTPResponse(t, w, "testdata/create_group_access_token.json")
	})

	groupAccessToken, _, err := client.GroupAccessTokens.CreateGroupAccessToken(1, nil)
	if err != nil {
		t.Errorf("GroupAccessTokens.CreateGroupAccessToken returned error: %v", err)
	}

	time1, err := time.Parse(time.RFC3339, "2021-03-09T21:11:47.271Z")
	if err != nil {
		t.Errorf("GroupAccessTokens.CreateGroupAccessToken returned error: %v", err)
	}
	want := &GroupAccessToken{
		ID:          1876,
		UserID:      2453,
		Name:        "token 10",
		Scopes:      []string{"api", "read_api", "read_repository", "write_repository"},
		ExpiresAt:   nil,
		CreatedAt:   &time1,
		Active:      true,
		Revoked:     false,
		Token:       "2UsevZE1x1ZdFZW4MNzH",
		AccessLevel: AccessLevelValue(40),
	}

	if !reflect.DeepEqual(want, groupAccessToken) {
		t.Errorf("GroupAccessTokens.CreateGroupAccessToken returned %+v, want %+v", groupAccessToken, want)
	}
}

func TestRotateGroupAccessToken(t *testing.T) {
	mux, client := setup(t)
	mux.HandleFunc("/api/v4/groups/1/access_tokens/42/rotate", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		mustWriteHTTPResponse(t, w, "testdata/rotate_group_access_token.json")
	})

	createdAt, _ := time.Parse(time.RFC3339, "2023-08-01T15:00:00.00Z")
	expiration := ISOTime(time.Date(2023, time.August, 15, 0, 0, 0, 0, time.UTC))
	opts := &RotateGroupAccessTokenOptions{ExpiresAt: &expiration}
	rotatedToken, _, err := client.GroupAccessTokens.RotateGroupAccessToken(1, 42, opts)
	if err != nil {
		t.Errorf("GroupAccessTokens.RotateGroupAccessToken returned error: %v", err)
	}

	want := &GroupAccessToken{
		ID:          42,
		UserID:      1337,
		Name:        "Rotated Token",
		Scopes:      []string{"api"},
		ExpiresAt:   &expiration,
		CreatedAt:   &createdAt,
		Active:      true,
		Revoked:     false,
		Token:       "s3cr3t",
		AccessLevel: AccessLevelValue(30),
	}

	if !reflect.DeepEqual(want, rotatedToken) {
		t.Errorf("GroupAccessTokens.RotateGroupAccessToken returned %+v, want %+v", rotatedToken, want)
	}
}

func TestRevokeGroupAccessToken(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1/access_tokens/1234", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodDelete)
	})

	_, err := client.GroupAccessTokens.RevokeGroupAccessToken("1", 1234)
	if err != nil {
		t.Errorf("GroupAccessTokens.RevokeGroupAccessToken returned error: %v", err)
	}
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/group_badges.go000066400000000000000000000156221475761473200240550ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"fmt"
	"net/http"
)

// GroupBadgesService handles communication with the group badges
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_badges.html
type GroupBadgesService struct {
	client *Client
}

// BadgeKind represents a GitLab Badge Kind
type BadgeKind string

// all possible values Badge Kind
const (
	ProjectBadgeKind BadgeKind = "project"
	GroupBadgeKind   BadgeKind = "group"
)

// GroupBadge represents a group badge.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_badges.html
type GroupBadge struct {
	ID               int       `json:"id"`
	Name             string    `json:"name"`
	LinkURL          string    `json:"link_url"`
	ImageURL         string    `json:"image_url"`
	RenderedLinkURL  string    `json:"rendered_link_url"`
	RenderedImageURL string    `json:"rendered_image_url"`
	Kind             BadgeKind `json:"kind"`
}

// ListGroupBadgesOptions represents the available ListGroupBadges() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_badges.html#list-all-badges-of-a-group
type ListGroupBadgesOptions struct {
	ListOptions
	Name *string `url:"name,omitempty" json:"name,omitempty"`
}

// ListGroupBadges gets a list of a group badges.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_badges.html#list-all-badges-of-a-group
func (s *GroupBadgesService) ListGroupBadges(gid interface{}, opt *ListGroupBadgesOptions, options ...RequestOptionFunc) ([]*GroupBadge, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/badges", PathEscape(group))

	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var gb []*GroupBadge
	resp, err := s.client.Do(req, &gb)
	if err != nil {
		return nil, resp, err
	}

	return gb, resp, nil
}

// GetGroupBadge gets a group badge.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_badges.html#get-a-badge-of-a-group
func (s *GroupBadgesService) GetGroupBadge(gid interface{}, badge int, options ...RequestOptionFunc) (*GroupBadge, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/badges/%d", PathEscape(group), badge)

	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	gb := new(GroupBadge)
	resp, err := s.client.Do(req, gb)
	if err != nil {
		return nil, resp, err
	}

	return gb, resp, nil
}

// AddGroupBadgeOptions represents the available AddGroupBadge() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_badges.html#add-a-badge-to-a-group
type AddGroupBadgeOptions struct {
	LinkURL  *string `url:"link_url,omitempty" json:"link_url,omitempty"`
	ImageURL *string `url:"image_url,omitempty" json:"image_url,omitempty"`
	Name     *string `url:"name,omitempty" json:"name,omitempty"`
}

// AddGroupBadge adds a badge to a group.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_badges.html#add-a-badge-to-a-group
func (s *GroupBadgesService) AddGroupBadge(gid interface{}, opt *AddGroupBadgeOptions, options ...RequestOptionFunc) (*GroupBadge, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/badges", PathEscape(group))

	req, err := s.client.NewRequest(http.MethodPost, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	gb := new(GroupBadge)
	resp, err := s.client.Do(req, gb)
	if err != nil {
		return nil, resp, err
	}

	return gb, resp, nil
}

// EditGroupBadgeOptions represents the available EditGroupBadge() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_badges.html#edit-a-badge-of-a-group
type EditGroupBadgeOptions struct {
	LinkURL  *string `url:"link_url,omitempty" json:"link_url,omitempty"`
	ImageURL *string `url:"image_url,omitempty" json:"image_url,omitempty"`
	Name     *string `url:"name,omitempty" json:"name,omitempty"`
}

// EditGroupBadge updates a badge of a group.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_badges.html#edit-a-badge-of-a-group
func (s *GroupBadgesService) EditGroupBadge(gid interface{}, badge int, opt *EditGroupBadgeOptions, options ...RequestOptionFunc) (*GroupBadge, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/badges/%d", PathEscape(group), badge)

	req, err := s.client.NewRequest(http.MethodPut, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	gb := new(GroupBadge)
	resp, err := s.client.Do(req, gb)
	if err != nil {
		return nil, resp, err
	}

	return gb, resp, nil
}

// DeleteGroupBadge removes a badge from a group.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_badges.html#remove-a-badge-from-a-group
func (s *GroupBadgesService) DeleteGroupBadge(gid interface{}, badge int, options ...RequestOptionFunc) (*Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("groups/%s/badges/%d", PathEscape(group), badge)

	req, err := s.client.NewRequest(http.MethodDelete, u, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}

// GroupBadgePreviewOptions represents the available PreviewGroupBadge() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_badges.html#preview-a-badge-from-a-group
type GroupBadgePreviewOptions struct {
	LinkURL  *string `url:"link_url,omitempty" json:"link_url,omitempty"`
	ImageURL *string `url:"image_url,omitempty" json:"image_url,omitempty"`
	Name     *string `url:"name,omitempty" json:"name,omitempty"`
}

// PreviewGroupBadge returns how the link_url and image_url final URLs would be after
// resolving the placeholder interpolation.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_badges.html#preview-a-badge-from-a-group
func (s *GroupBadgesService) PreviewGroupBadge(gid interface{}, opt *GroupBadgePreviewOptions, options ...RequestOptionFunc) (*GroupBadge, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/badges/render", PathEscape(group))

	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	gb := new(GroupBadge)
	resp, err := s.client.Do(req, &gb)
	if err != nil {
		return nil, resp, err
	}

	return gb, resp, nil
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/group_badges_test.go000066400000000000000000000076661475761473200251250ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"fmt"
	"net/http"
	"reflect"
	"testing"
)

func TestListGroupBadges(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1/badges",
		func(w http.ResponseWriter, r *http.Request) {
			testMethod(t, r, http.MethodGet)
			fmt.Fprint(w, `[{"id":1, "name":"one", "kind":"group"},{"id":2, "name":"two", "kind":"group"}]`)
		})

	badges, _, err := client.GroupBadges.ListGroupBadges(1, &ListGroupBadgesOptions{})
	if err != nil {
		t.Errorf("GroupBadges.ListGroupBadges returned error: %v", err)
	}

	want := []*GroupBadge{{ID: 1, Name: "one", Kind: GroupBadgeKind}, {ID: 2, Name: "two", Kind: GroupBadgeKind}}
	if !reflect.DeepEqual(want, badges) {
		t.Errorf("GroupBadges.ListGroupBadges returned %+v, want %+v", badges, want)
	}
}

func TestGetGroupBadge(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1/badges/2",
		func(w http.ResponseWriter, r *http.Request) {
			testMethod(t, r, http.MethodGet)
			fmt.Fprint(w, `{"id":2, "name":"two", "kind":"group"}`)
		})

	badge, _, err := client.GroupBadges.GetGroupBadge(1, 2)
	if err != nil {
		t.Errorf("GroupBadges.GetGroupBadge returned error: %v", err)
	}

	want := &GroupBadge{ID: 2, Name: "two", Kind: GroupBadgeKind}
	if !reflect.DeepEqual(want, badge) {
		t.Errorf("GroupBadges.GetGroupBadge returned %+v, want %+v", badge, want)
	}
}

func TestAddGroupBadge(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1/badges",
		func(w http.ResponseWriter, r *http.Request) {
			testMethod(t, r, http.MethodPost)
			fmt.Fprint(w, `{"id":3, "name":"three", "link_url":"LINK", "image_url":"IMAGE", "kind":"group"}`)
		})

	opt := &AddGroupBadgeOptions{ImageURL: Ptr("IMAGE"), LinkURL: Ptr("LINK")}
	badge, _, err := client.GroupBadges.AddGroupBadge(1, opt)
	if err != nil {
		t.Errorf("GroupBadges.AddGroupBadge returned error: %v", err)
	}

	want := &GroupBadge{ID: 3, Name: "three", ImageURL: "IMAGE", LinkURL: "LINK", Kind: GroupBadgeKind}
	if !reflect.DeepEqual(want, badge) {
		t.Errorf("GroupBadges.AddGroupBadge returned %+v, want %+v", badge, want)
	}
}

func TestEditGroupBadge(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1/badges/2",
		func(w http.ResponseWriter, r *http.Request) {
			testMethod(t, r, http.MethodPut)
			fmt.Fprint(w, `{"id":2, "name":"two", "link_url":"NEW_LINK", "image_url":"NEW_IMAGE", "kind":"group"}`)
		})

	opt := &EditGroupBadgeOptions{ImageURL: Ptr("NEW_IMAGE"), LinkURL: Ptr("NEW_LINK")}
	badge, _, err := client.GroupBadges.EditGroupBadge(1, 2, opt)
	if err != nil {
		t.Errorf("GroupBadges.EditGroupBadge returned error: %v", err)
	}

	want := &GroupBadge{ID: 2, Name: "two", ImageURL: "NEW_IMAGE", LinkURL: "NEW_LINK", Kind: GroupBadgeKind}
	if !reflect.DeepEqual(want, badge) {
		t.Errorf("GroupBadges.EditGroupBadge returned %+v, want %+v", badge, want)
	}
}

func TestRemoveGroupBadge(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1/badges/2",
		func(w http.ResponseWriter, r *http.Request) {
			testMethod(t, r, http.MethodDelete)
			w.WriteHeader(http.StatusAccepted)
		},
	)

	resp, err := client.GroupBadges.DeleteGroupBadge(1, 2)
	if err != nil {
		t.Errorf("GroupBadges.DeleteGroupBadge returned error: %v", err)
	}

	want := http.StatusAccepted
	got := resp.StatusCode
	if got != want {
		t.Errorf("GroupsBadges.DeleteGroupBadge returned %d, want %d", got, want)
	}
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/group_boards.go000066400000000000000000000243641475761473200241050ustar00rootroot00000000000000//
// Copyright 2021, Patrick Webster
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"fmt"
	"net/http"
)

// GroupIssueBoardsService handles communication with the group issue board
// related methods of the GitLab API.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_boards.html
type GroupIssueBoardsService struct {
	client *Client
}

// GroupIssueBoard represents a GitLab group issue board.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_boards.html
type GroupIssueBoard struct {
	ID        int           `json:"id"`
	Name      string        `json:"name"`
	Group     *Group        `json:"group"`
	Milestone *Milestone    `json:"milestone"`
	Labels    []*GroupLabel `json:"labels"`
	Lists     []*BoardList  `json:"lists"`
}

func (b GroupIssueBoard) String() string {
	return Stringify(b)
}

// ListGroupIssueBoardsOptions represents the available
// ListGroupIssueBoards() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_boards.html#list-all-group-issue-boards-in-a-group
type ListGroupIssueBoardsOptions ListOptions

// ListGroupIssueBoards gets a list of all issue boards in a group.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_boards.html#list-all-group-issue-boards-in-a-group
func (s *GroupIssueBoardsService) ListGroupIssueBoards(gid interface{}, opt *ListGroupIssueBoardsOptions, options ...RequestOptionFunc) ([]*GroupIssueBoard, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/boards", PathEscape(group))

	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var gs []*GroupIssueBoard
	resp, err := s.client.Do(req, &gs)
	if err != nil {
		return nil, resp, err
	}

	return gs, resp, nil
}

// CreateGroupIssueBoardOptions represents the available
// CreateGroupIssueBoard() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_boards.html#create-a-group-issue-board
type CreateGroupIssueBoardOptions struct {
	Name *string `url:"name" json:"name"`
}

// CreateGroupIssueBoard creates a new issue board.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_boards.html#create-a-group-issue-board
func (s *GroupIssueBoardsService) CreateGroupIssueBoard(gid interface{}, opt *CreateGroupIssueBoardOptions, options ...RequestOptionFunc) (*GroupIssueBoard, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/boards", PathEscape(group))

	req, err := s.client.NewRequest(http.MethodPost, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	gib := new(GroupIssueBoard)
	resp, err := s.client.Do(req, gib)
	if err != nil {
		return nil, resp, err
	}

	return gib, resp, nil
}

// GetGroupIssueBoard gets a single issue board of a group.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_boards.html#single-group-issue-board
func (s *GroupIssueBoardsService) GetGroupIssueBoard(gid interface{}, board int, options ...RequestOptionFunc) (*GroupIssueBoard, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/boards/%d", PathEscape(group), board)

	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	gib := new(GroupIssueBoard)
	resp, err := s.client.Do(req, gib)
	if err != nil {
		return nil, resp, err
	}

	return gib, resp, nil
}

// UpdateGroupIssueBoardOptions represents a group issue board.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_boards.html#update-a-group-issue-board
type UpdateGroupIssueBoardOptions struct {
	Name        *string       `url:"name,omitempty" json:"name,omitempty"`
	AssigneeID  *int          `url:"assignee_id,omitempty" json:"assignee_id,omitempty"`
	MilestoneID *int          `url:"milestone_id,omitempty" json:"milestone_id,omitempty"`
	Labels      *LabelOptions `url:"labels,omitempty" json:"labels,omitempty"`
	Weight      *int          `url:"weight,omitempty" json:"weight,omitempty"`
}

// UpdateIssueBoard updates a single issue board of a group.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_boards.html#update-a-group-issue-board
func (s *GroupIssueBoardsService) UpdateIssueBoard(gid interface{}, board int, opt *UpdateGroupIssueBoardOptions, options ...RequestOptionFunc) (*GroupIssueBoard, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/boards/%d", PathEscape(group), board)

	req, err := s.client.NewRequest(http.MethodPut, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	gib := new(GroupIssueBoard)
	resp, err := s.client.Do(req, gib)
	if err != nil {
		return nil, resp, err
	}

	return gib, resp, nil
}

// DeleteIssueBoard delete a single issue board of a group.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_boards.html#delete-a-group-issue-board
func (s *GroupIssueBoardsService) DeleteIssueBoard(gid interface{}, board int, options ...RequestOptionFunc) (*Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("groups/%s/boards/%d", PathEscape(group), board)

	req, err := s.client.NewRequest(http.MethodDelete, u, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}

// ListGroupIssueBoardListsOptions represents the available
// ListGroupIssueBoardLists() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_boards.html#list-group-issue-board-lists
type ListGroupIssueBoardListsOptions ListOptions

// ListGroupIssueBoardLists gets a list of the issue board's lists. Does not include
// backlog and closed lists.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/group_boards.html#list-group-issue-board-lists
func (s *GroupIssueBoardsService) ListGroupIssueBoardLists(gid interface{}, board int, opt *ListGroupIssueBoardListsOptions, options ...RequestOptionFunc) ([]*BoardList, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/boards/%d/lists", PathEscape(group), board)

	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var gbl []*BoardList
	resp, err := s.client.Do(req, &gbl)
	if err != nil {
		return nil, resp, err
	}

	return gbl, resp, nil
}

// GetGroupIssueBoardList gets a single issue board list.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_boards.html#single-group-issue-board-list
func (s *GroupIssueBoardsService) GetGroupIssueBoardList(gid interface{}, board, list int, options ...RequestOptionFunc) (*BoardList, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/boards/%d/lists/%d",
		PathEscape(group),
		board,
		list,
	)

	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	gbl := new(BoardList)
	resp, err := s.client.Do(req, gbl)
	if err != nil {
		return nil, resp, err
	}

	return gbl, resp, nil
}

// CreateGroupIssueBoardListOptions represents the available
// CreateGroupIssueBoardList() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_boards.html#new-group-issue-board-list
type CreateGroupIssueBoardListOptions struct {
	LabelID *int `url:"label_id" json:"label_id"`
}

// CreateGroupIssueBoardList creates a new issue board list.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_boards.html#new-group-issue-board-list
func (s *GroupIssueBoardsService) CreateGroupIssueBoardList(gid interface{}, board int, opt *CreateGroupIssueBoardListOptions, options ...RequestOptionFunc) (*BoardList, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/boards/%d/lists", PathEscape(group), board)

	req, err := s.client.NewRequest(http.MethodPost, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	gbl := new(BoardList)
	resp, err := s.client.Do(req, gbl)
	if err != nil {
		return nil, resp, err
	}

	return gbl, resp, nil
}

// UpdateGroupIssueBoardListOptions represents the available
// UpdateGroupIssueBoardList() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_boards.html#edit-group-issue-board-list
type UpdateGroupIssueBoardListOptions struct {
	Position *int `url:"position" json:"position"`
}

// UpdateIssueBoardList updates the position of an existing
// group issue board list.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_boards.html#edit-group-issue-board-list
func (s *GroupIssueBoardsService) UpdateIssueBoardList(gid interface{}, board, list int, opt *UpdateGroupIssueBoardListOptions, options ...RequestOptionFunc) ([]*BoardList, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/boards/%d/lists/%d",
		PathEscape(group),
		board,
		list,
	)

	req, err := s.client.NewRequest(http.MethodPut, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var gbl []*BoardList
	resp, err := s.client.Do(req, &gbl)
	if err != nil {
		return nil, resp, err
	}

	return gbl, resp, nil
}

// DeleteGroupIssueBoardList soft deletes a group issue board list.
// Only for admins and group owners.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_boards.html#delete-a-group-issue-board-list
func (s *GroupIssueBoardsService) DeleteGroupIssueBoardList(gid interface{}, board, list int, options ...RequestOptionFunc) (*Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("groups/%s/boards/%d/lists/%d",
		PathEscape(group),
		board,
		list,
	)

	req, err := s.client.NewRequest(http.MethodDelete, u, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/group_boards_test.go000066400000000000000000000437401475761473200251430ustar00rootroot00000000000000package gitlab

import (
	"fmt"
	"net/http"
	"testing"

	"github.com/stretchr/testify/require"
)

func TestGroupIssueBoardsService_ListGroupIssueBoards(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/5/boards", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
			[
			  {
				"id": 1,
				"name": "group issue board",
				"group": {
				  "id": 5,
				  "name": "Documentcloud",
				  "web_url": "http://example.com/groups/documentcloud"
				},
				"milestone":   {
				  "id": 12,
				  "title": "10.0"
				},
				"lists" : [
				  {
					"id" : 1,
					"label" : {
					  "name" : "Testing",
					  "color" : "#F0AD4E",
					  "description" : null
					},
					"position" : 1
				  },
				  {
					"id" : 2,
					"label" : {
					  "name" : "Ready",
					  "color" : "#FF0000",
					  "description" : null
					},
					"position" : 2
				  },
				  {
					"id" : 3,
					"label" : {
					  "name" : "Production",
					  "color" : "#FF5F00",
					  "description" : null
					},
					"position" : 3
				  }
				]
			  }
			]
		`)
	})

	want := []*GroupIssueBoard{{
		ID:   1,
		Name: "group issue board",
		Group: &Group{
			ID:     5,
			Name:   "Documentcloud",
			WebURL: "http://example.com/groups/documentcloud",
		},
		Milestone: &Milestone{
			ID:          12,
			IID:         0,
			ProjectID:   0,
			Title:       "10.0",
			Description: "",
		},
		Lists: []*BoardList{
			{
				ID: 1,
				Label: &Label{
					Name:        "Testing",
					Color:       "#F0AD4E",
					Description: "",
				},
				Position: 1,
			},
			{
				ID: 2,
				Label: &Label{
					Name:        "Ready",
					Color:       "#FF0000",
					Description: "",
				},
				Position: 2,
			},
			{
				ID: 3,
				Label: &Label{
					Name:        "Production",
					Color:       "#FF5F00",
					Description: "",
				},
				Position: 3,
			},
		},
	}}

	gibs, resp, err := client.GroupIssueBoards.ListGroupIssueBoards(5, nil, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, gibs)

	gibs, resp, err = client.GroupIssueBoards.ListGroupIssueBoards(5.01, nil, nil)
	require.EqualError(t, err, "invalid ID type 5.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, gibs)

	gibs, resp, err = client.GroupIssueBoards.ListGroupIssueBoards(5, nil, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, gibs)

	gibs, resp, err = client.GroupIssueBoards.ListGroupIssueBoards(3, nil, nil)
	require.Error(t, err)
	require.Nil(t, gibs)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestGroupIssueBoardsService_CreateGroupIssueBoard(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/5/boards", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		fmt.Fprintf(w, `
			  {
				"id": 1,
				"name": "newboard",
				"project": null,
				"lists" : [],
				"group": {
				  "id": 5,
				  "name": "Documentcloud",
				  "web_url": "http://example.com/groups/documentcloud"
				},
				"milestone": null,
				"assignee" : null,
				"labels" : [],
				"weight" : null
			  }
		`)
	})

	want := &GroupIssueBoard{
		ID:   1,
		Name: "newboard",
		Group: &Group{
			ID:     5,
			Name:   "Documentcloud",
			WebURL: "http://example.com/groups/documentcloud",
		},
		Milestone: nil,
		Labels:    []*GroupLabel{},
		Lists:     []*BoardList{},
	}

	gib, resp, err := client.GroupIssueBoards.CreateGroupIssueBoard(5, nil, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, gib)

	gib, resp, err = client.GroupIssueBoards.CreateGroupIssueBoard(5.01, nil, nil)
	require.EqualError(t, err, "invalid ID type 5.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, gib)

	gib, resp, err = client.GroupIssueBoards.CreateGroupIssueBoard(5, nil, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, gib)

	gib, resp, err = client.GroupIssueBoards.CreateGroupIssueBoard(3, nil, nil)
	require.Error(t, err)
	require.Nil(t, gib)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestGroupIssueBoardsService_GetGroupIssueBoard(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/5/boards/1", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
		  {
			"id": 1,
			"name": "group issue board",
			"group": {
			  "id": 5,
			  "name": "Documentcloud",
			  "web_url": "http://example.com/groups/documentcloud"
			},
			"milestone":   {
			  "id": 12,
			  "title": "10.0"
			},
			"labels": [
				{
					"id":1749,
					"name":"my-scope1",
					"description":null,
					"description_html":"",
					"text_color":"#FFFFFF",
					"color":"#6699cc"
				},
				{
					"id":1747,
					"name":"my-scope2",
					"description":null,
					"description_html":"",
					"text_color":"#FFFFFF",
					"color":"#FF0000"
				}
			],
			"lists" : [
			  {
				"id" : 1,
				"label" : {
				  "name" : "Testing",
				  "color" : "#F0AD4E",
				  "description" : null
				},
				"position" : 1
			  },
			  {
				"id" : 2,
				"label" : {
				  "name" : "Ready",
				  "color" : "#FF0000",
				  "description" : null
				},
				"position" : 2
			  },
			  {
				"id" : 3,
				"label" : {
				  "name" : "Production",
				  "color" : "#FF5F00",
				  "description" : null
				},
				"position" : 3
			  }
			]
		  }
		`)
	})

	want := &GroupIssueBoard{
		ID:   1,
		Name: "group issue board",
		Group: &Group{
			ID:     5,
			Name:   "Documentcloud",
			WebURL: "http://example.com/groups/documentcloud",
		},
		Milestone: &Milestone{
			ID:          12,
			IID:         0,
			ProjectID:   0,
			Title:       "10.0",
			Description: "",
		},
		Labels: []*GroupLabel{
			{
				ID:        1749,
				Name:      "my-scope1",
				TextColor: "#FFFFFF",
				Color:     "#6699cc",
			},
			{
				ID:        1747,
				Name:      "my-scope2",
				TextColor: "#FFFFFF",
				Color:     "#FF0000",
			},
		},
		Lists: []*BoardList{
			{
				ID: 1,
				Label: &Label{
					Name:        "Testing",
					Color:       "#F0AD4E",
					Description: "",
				},
				Position: 1,
			},
			{
				ID: 2,
				Label: &Label{
					Name:        "Ready",
					Color:       "#FF0000",
					Description: "",
				},
				Position: 2,
			},
			{
				ID: 3,
				Label: &Label{
					Name:        "Production",
					Color:       "#FF5F00",
					Description: "",
				},
				Position: 3,
			},
		},
	}

	gib, resp, err := client.GroupIssueBoards.GetGroupIssueBoard(5, 1, nil, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, gib)

	gib, resp, err = client.GroupIssueBoards.GetGroupIssueBoard(5.01, 1, nil, nil)
	require.EqualError(t, err, "invalid ID type 5.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, gib)

	gib, resp, err = client.GroupIssueBoards.GetGroupIssueBoard(5, 1, nil, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, gib)

	gib, resp, err = client.GroupIssueBoards.GetGroupIssueBoard(3, 1, nil, nil)
	require.Error(t, err)
	require.Nil(t, gib)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestGroupIssueBoardsService_UpdateIssueBoard(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/5/boards/1", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPut)
		fmt.Fprintf(w, `
			  {
				"id": 1,
				"project": null,
				"lists": [],
				"name": "new_name",
				"group": {
				  "id": 5,
				  "name": "Documentcloud",
				  "web_url": "http://example.com/groups/documentcloud"
				},
				"milestone": {
				  "id": 44,
				  "iid": 1,
				  "group_id": 5,
				  "title": "Group Milestone",
				  "description": "Group Milestone Desc",
				  "state": "active",
				  "web_url": "http://example.com/groups/documentcloud/-/milestones/1"
				},
				"assignee": {
				  "id": 1,
				  "name": "Administrator",
				  "username": "root",
				  "state": "active",
				  "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
				  "web_url": "http://example.com/root"
				},
				"labels": [{
				  "id": 11,
				  "name": "GroupLabel",
				  "color": "#428BCA",
				  "description": ""
				}],
				"weight": 4
			  }
		`)
	})

	want := &GroupIssueBoard{
		ID:   1,
		Name: "new_name",
		Group: &Group{
			ID:     5,
			Name:   "Documentcloud",
			WebURL: "http://example.com/groups/documentcloud",
		},
		Milestone: &Milestone{
			ID:          44,
			IID:         1,
			GroupID:     5,
			Title:       "Group Milestone",
			Description: "Group Milestone Desc",
			State:       "active",
			WebURL:      "http://example.com/groups/documentcloud/-/milestones/1",
		},
		Labels: []*GroupLabel{
			{
				ID:          11,
				Name:        "GroupLabel",
				Color:       "#428BCA",
				Description: "",
			},
		},
		Lists: []*BoardList{},
	}

	gib, resp, err := client.GroupIssueBoards.UpdateIssueBoard(5, 1, nil, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, gib)

	gib, resp, err = client.GroupIssueBoards.UpdateIssueBoard(5.01, 1, nil, nil)
	require.EqualError(t, err, "invalid ID type 5.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, gib)

	gib, resp, err = client.GroupIssueBoards.UpdateIssueBoard(5, 1, nil, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, gib)

	gib, resp, err = client.GroupIssueBoards.UpdateIssueBoard(3, 1, nil, nil)
	require.Error(t, err)
	require.Nil(t, gib)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestGroupIssueBoardsService_DeleteIssueBoard(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/5/boards/1", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodDelete)
	})

	resp, err := client.GroupIssueBoards.DeleteIssueBoard(5, 1, nil, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)

	resp, err = client.GroupIssueBoards.DeleteIssueBoard(5.01, 1, nil, nil)
	require.EqualError(t, err, "invalid ID type 5.01, the ID must be an int or a string")
	require.Nil(t, resp)

	resp, err = client.GroupIssueBoards.DeleteIssueBoard(5, 1, nil, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)

	resp, err = client.GroupIssueBoards.DeleteIssueBoard(3, 1, nil, nil)
	require.Error(t, err)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestGroupIssueBoardsService_ListGroupIssueBoardLists(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/5/boards/1/lists", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
			[
			  {
				"id" : 1,
				"label" : {
				  "name" : "Testing",
				  "color" : "#F0AD4E",
				  "description" : null
				},
				"position" : 1
			  },
			  {
				"id" : 2,
				"label" : {
				  "name" : "Ready",
				  "color" : "#FF0000",
				  "description" : null
				},
				"position" : 2
			  },
			  {
				"id" : 3,
				"label" : {
				  "name" : "Production",
				  "color" : "#FF5F00",
				  "description" : null
				},
				"position" : 3
			  }
			]
		`)
	})

	want := []*BoardList{
		{
			ID: 1,
			Label: &Label{
				Name:        "Testing",
				Color:       "#F0AD4E",
				Description: "",
			},
			Position: 1,
		},
		{
			ID: 2,
			Label: &Label{
				Name:        "Ready",
				Color:       "#FF0000",
				Description: "",
			},
			Position: 2,
		},
		{
			ID: 3,
			Label: &Label{
				Name:        "Production",
				Color:       "#FF5F00",
				Description: "",
			},
			Position: 3,
		},
	}

	bls, resp, err := client.GroupIssueBoards.ListGroupIssueBoardLists(5, 1, nil, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, bls)

	bls, resp, err = client.GroupIssueBoards.ListGroupIssueBoardLists(5.01, 1, nil, nil)
	require.EqualError(t, err, "invalid ID type 5.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, bls)

	bls, resp, err = client.GroupIssueBoards.ListGroupIssueBoardLists(5, 1, nil, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, bls)

	bls, resp, err = client.GroupIssueBoards.ListGroupIssueBoardLists(3, 1, nil, nil)
	require.Error(t, err)
	require.Nil(t, bls)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestGroupIssueBoardsService_GetGroupIssueBoardList(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/5/boards/1/lists/1", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
		  {
			"id" : 1,
			"label" : {
			  "name" : "Testing",
			  "color" : "#F0AD4E",
			  "description" : null
			},
			"position" : 1
		  }
		`)
	})

	want := &BoardList{
		ID: 1,
		Label: &Label{
			Name:        "Testing",
			Color:       "#F0AD4E",
			Description: "",
		},
		Position: 1,
	}

	bl, resp, err := client.GroupIssueBoards.GetGroupIssueBoardList(5, 1, 1, nil, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, bl)

	bl, resp, err = client.GroupIssueBoards.GetGroupIssueBoardList(5.01, 1, 1, nil, nil)
	require.EqualError(t, err, "invalid ID type 5.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, bl)

	bl, resp, err = client.GroupIssueBoards.GetGroupIssueBoardList(5, 1, 1, nil, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, bl)

	bl, resp, err = client.GroupIssueBoards.GetGroupIssueBoardList(3, 1, 1, nil, nil)
	require.Error(t, err)
	require.Nil(t, bl)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestGroupIssueBoardsService_CreateGroupIssueBoardList(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/5/boards/1/lists", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		fmt.Fprintf(w, `
			{
			  "id": 9,
			  "label": null,
			  "position": 0,
			  "milestone": {
				"id": 7,
				"iid": 3,
				"group_id": 12,
				"title": "Milestone with due date",
				"description": "",
				"state": "active",
				"web_url": "https://gitlab.example.com/groups/issue-reproduce/-/milestones/3"
			  }
			}
		`)
	})

	want := &BoardList{
		ID:       9,
		Label:    nil,
		Position: 0,
		Milestone: &Milestone{
			ID:          7,
			IID:         3,
			GroupID:     12,
			Title:       "Milestone with due date",
			Description: "",
			State:       "active",
			WebURL:      "https://gitlab.example.com/groups/issue-reproduce/-/milestones/3",
		},
	}

	bl, resp, err := client.GroupIssueBoards.CreateGroupIssueBoardList(5, 1, nil, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, bl)

	bl, resp, err = client.GroupIssueBoards.CreateGroupIssueBoardList(5.01, 1, nil, nil)
	require.EqualError(t, err, "invalid ID type 5.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, bl)

	bl, resp, err = client.GroupIssueBoards.CreateGroupIssueBoardList(5, 1, nil, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, bl)

	bl, resp, err = client.GroupIssueBoards.CreateGroupIssueBoardList(3, 1, nil, nil)
	require.Error(t, err)
	require.Nil(t, bl)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestGroupIssueBoardsService_UpdateIssueBoardList(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/5/boards/1/lists/1", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPut)
		fmt.Fprintf(w, `
			[
			  {
				"id" : 1,
				"label" : {
				  "name" : "Testing",
				  "color" : "#F0AD4E",
				  "description" : null
				},
				"position" : 1
			  },
			  {
				"id" : 2,
				"label" : {
				  "name" : "Ready",
				  "color" : "#FF0000",
				  "description" : null
				},
				"position" : 2
			  },
			  {
				"id" : 3,
				"label" : {
				  "name" : "Production",
				  "color" : "#FF5F00",
				  "description" : null
				},
				"position" : 3
			  }
			]
		`)
	})

	want := []*BoardList{
		{
			ID: 1,
			Label: &Label{
				Name:        "Testing",
				Color:       "#F0AD4E",
				Description: "",
			},
			Position: 1,
		},
		{
			ID: 2,
			Label: &Label{
				Name:        "Ready",
				Color:       "#FF0000",
				Description: "",
			},
			Position: 2,
		},
		{
			ID: 3,
			Label: &Label{
				Name:        "Production",
				Color:       "#FF5F00",
				Description: "",
			},
			Position: 3,
		},
	}

	bl, resp, err := client.GroupIssueBoards.UpdateIssueBoardList(5, 1, 1, nil, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, bl)

	bl, resp, err = client.GroupIssueBoards.UpdateIssueBoardList(5.01, 1, 1, nil, nil)
	require.EqualError(t, err, "invalid ID type 5.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, bl)

	bl, resp, err = client.GroupIssueBoards.UpdateIssueBoardList(5, 1, 1, nil, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, bl)

	bl, resp, err = client.GroupIssueBoards.UpdateIssueBoardList(3, 1, 1, nil, nil)
	require.Error(t, err)
	require.Nil(t, bl)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestGroupIssueBoardsService_DeleteGroupIssueBoardList(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/5/boards/1/lists/1", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodDelete)
	})

	resp, err := client.GroupIssueBoards.DeleteGroupIssueBoardList(5, 1, 1, nil, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)

	resp, err = client.GroupIssueBoards.DeleteGroupIssueBoardList(5.01, 1, 1, nil, nil)
	require.EqualError(t, err, "invalid ID type 5.01, the ID must be an int or a string")
	require.Nil(t, resp)

	resp, err = client.GroupIssueBoards.DeleteGroupIssueBoardList(5, 1, 1, nil, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)

	resp, err = client.GroupIssueBoards.DeleteGroupIssueBoardList(3, 1, 1, nil, nil)
	require.Error(t, err)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/group_clusters.go000066400000000000000000000175151475761473200244770ustar00rootroot00000000000000//
// Copyright 2021, Paul Shoemaker
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"fmt"
	"net/http"
	"time"
)

// GroupClustersService handles communication with the
// group clusters related methods of the GitLab API.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_clusters.html
type GroupClustersService struct {
	client *Client
}

// GroupCluster represents a GitLab Group Cluster.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/group_clusters.html
type GroupCluster struct {
	ID                 int                 `json:"id"`
	Name               string              `json:"name"`
	Domain             string              `json:"domain"`
	CreatedAt          *time.Time          `json:"created_at"`
	Managed            bool                `json:"managed"`
	Enabled            bool                `json:"enabled"`
	ProviderType       string              `json:"provider_type"`
	PlatformType       string              `json:"platform_type"`
	EnvironmentScope   string              `json:"environment_scope"`
	ClusterType        string              `json:"cluster_type"`
	User               *User               `json:"user"`
	PlatformKubernetes *PlatformKubernetes `json:"platform_kubernetes"`
	ManagementProject  *ManagementProject  `json:"management_project"`
	Group              *Group              `json:"group"`
}

func (v GroupCluster) String() string {
	return Stringify(v)
}

// ListClusters gets a list of all clusters in a group.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_clusters.html#list-group-clusters
func (s *GroupClustersService) ListClusters(pid interface{}, options ...RequestOptionFunc) ([]*GroupCluster, *Response, error) {
	group, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/clusters", PathEscape(group))

	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	var pcs []*GroupCluster
	resp, err := s.client.Do(req, &pcs)
	if err != nil {
		return nil, resp, err
	}

	return pcs, resp, nil
}

// GetCluster gets a cluster.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_clusters.html#get-a-single-group-cluster
func (s *GroupClustersService) GetCluster(pid interface{}, cluster int, options ...RequestOptionFunc) (*GroupCluster, *Response, error) {
	group, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/clusters/%d", PathEscape(group), cluster)

	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	gc := new(GroupCluster)
	resp, err := s.client.Do(req, &gc)
	if err != nil {
		return nil, resp, err
	}

	return gc, resp, nil
}

// AddGroupClusterOptions represents the available AddCluster() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_clusters.html#add-existing-cluster-to-group
type AddGroupClusterOptions struct {
	Name                *string                            `url:"name,omitempty" json:"name,omitempty"`
	Domain              *string                            `url:"domain,omitempty" json:"domain,omitempty"`
	ManagementProjectID *string                            `url:"management_project_id,omitempty" json:"management_project_id,omitempty"`
	Enabled             *bool                              `url:"enabled,omitempty" json:"enabled,omitempty"`
	Managed             *bool                              `url:"managed,omitempty" json:"managed,omitempty"`
	EnvironmentScope    *string                            `url:"environment_scope,omitempty" json:"environment_scope,omitempty"`
	PlatformKubernetes  *AddGroupPlatformKubernetesOptions `url:"platform_kubernetes_attributes,omitempty" json:"platform_kubernetes_attributes,omitempty"`
}

// AddGroupPlatformKubernetesOptions represents the available PlatformKubernetes options for adding.
type AddGroupPlatformKubernetesOptions struct {
	APIURL            *string `url:"api_url,omitempty" json:"api_url,omitempty"`
	Token             *string `url:"token,omitempty" json:"token,omitempty"`
	CaCert            *string `url:"ca_cert,omitempty" json:"ca_cert,omitempty"`
	Namespace         *string `url:"namespace,omitempty" json:"namespace,omitempty"`
	AuthorizationType *string `url:"authorization_type,omitempty" json:"authorization_type,omitempty"`
}

// AddCluster adds an existing cluster to the group.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_clusters.html#add-existing-cluster-to-group
func (s *GroupClustersService) AddCluster(pid interface{}, opt *AddGroupClusterOptions, options ...RequestOptionFunc) (*GroupCluster, *Response, error) {
	group, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/clusters/user", PathEscape(group))

	req, err := s.client.NewRequest(http.MethodPost, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	gc := new(GroupCluster)
	resp, err := s.client.Do(req, gc)
	if err != nil {
		return nil, resp, err
	}

	return gc, resp, nil
}

// EditGroupClusterOptions represents the available EditCluster() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_clusters.html#edit-group-cluster
type EditGroupClusterOptions struct {
	Name                *string                             `url:"name,omitempty" json:"name,omitempty"`
	Domain              *string                             `url:"domain,omitempty" json:"domain,omitempty"`
	EnvironmentScope    *string                             `url:"environment_scope,omitempty" json:"environment_scope,omitempty"`
	PlatformKubernetes  *EditGroupPlatformKubernetesOptions `url:"platform_kubernetes_attributes,omitempty" json:"platform_kubernetes_attributes,omitempty"`
	ManagementProjectID *string                             `url:"management_project_id,omitempty" json:"management_project_id,omitempty"`
}

// EditGroupPlatformKubernetesOptions represents the available PlatformKubernetes options for editing.
type EditGroupPlatformKubernetesOptions struct {
	APIURL *string `url:"api_url,omitempty" json:"api_url,omitempty"`
	Token  *string `url:"token,omitempty" json:"token,omitempty"`
	CaCert *string `url:"ca_cert,omitempty" json:"ca_cert,omitempty"`
}

// EditCluster updates an existing group cluster.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_clusters.html#edit-group-cluster
func (s *GroupClustersService) EditCluster(pid interface{}, cluster int, opt *EditGroupClusterOptions, options ...RequestOptionFunc) (*GroupCluster, *Response, error) {
	group, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/clusters/%d", PathEscape(group), cluster)

	req, err := s.client.NewRequest(http.MethodPut, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	gc := new(GroupCluster)
	resp, err := s.client.Do(req, gc)
	if err != nil {
		return nil, resp, err
	}

	return gc, resp, nil
}

// DeleteCluster deletes an existing group cluster.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_clusters.html#delete-group-cluster
func (s *GroupClustersService) DeleteCluster(pid interface{}, cluster int, options ...RequestOptionFunc) (*Response, error) {
	group, err := parseID(pid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("groups/%s/clusters/%d", PathEscape(group), cluster)

	req, err := s.client.NewRequest(http.MethodDelete, u, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/group_clusters_test.go000066400000000000000000000314361475761473200255340ustar00rootroot00000000000000//
// Copyright 2021, Paul Shoemaker
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"fmt"
	"net/http"
	"reflect"
	"testing"
	"time"
)

func TestGroupListClusters(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/26/clusters", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `[
			{
			  "id":18,
			  "name":"cluster-1",
			  "domain":"example.com",
			  "created_at":"2019-01-02T20:18:12.563Z",
			  "managed": true,
			  "enabled": true,
			  "provider_type":"user",
			  "platform_type":"kubernetes",
			  "environment_scope":"*",
			  "cluster_type":"group_type",
			  "user":
			  {
				"id":1,
				"name":"Administrator",
				"username":"root",
				"state":"active",
				"avatar_url":"https://www.gravatar.com/avatar/4249f4df72b..",
				"web_url":"https://gitlab.example.com/root"
			  },
			  "platform_kubernetes":
			  {
				"api_url":"https://104.197.68.152",
				"authorization_type":"rbac",
			    "ca_cert":"-----BEGIN CERTIFICATE-----\r\nAAAAA\r\n-----END CERTIFICATE-----"
			  },
			  "management_project":
			  {
				"id":2,
				"description": "sdhfgnbsdjfhg",
				"name":"project2",
				"name_with_namespace":"John Doe8 / project2",
				"path":"project2",
				"path_with_namespace":"namespace2/project2",
				"created_at":"2019-10-11T02:55:54.138Z"
			  }
			},
			{
			  "id":19,
			  "name":"cluster-2"
			}
		  ]`)
	})

	clusters, _, err := client.GroupCluster.ListClusters(26)
	if err != nil {
		t.Errorf("GroupCluster.ListClusters returned error: %v", err)
	}

	createdAt, err := time.Parse(timeLayout, "2019-01-02T20:18:12.563Z")
	if err != nil {
		t.Errorf("DeployKeys.ListAllDeployKeys returned an error while parsing time: %v", err)
	}

	createdAt2, err := time.Parse(timeLayout, "2019-10-11T02:55:54.138Z")
	if err != nil {
		t.Errorf("DeployKeys.ListAllDeployKeys returned an error while parsing time: %v", err)
	}

	want := []*GroupCluster{
		{
			ID:               18,
			Name:             "cluster-1",
			Domain:           "example.com",
			CreatedAt:        &createdAt,
			Managed:          true,
			Enabled:          true,
			ProviderType:     "user",
			PlatformType:     "kubernetes",
			EnvironmentScope: "*",
			ClusterType:      "group_type",
			User: &User{
				ID:        1,
				Name:      "Administrator",
				Username:  "root",
				State:     "active",
				AvatarURL: "https://www.gravatar.com/avatar/4249f4df72b..",
				WebURL:    "https://gitlab.example.com/root",
			},
			PlatformKubernetes: &PlatformKubernetes{
				APIURL:            "https://104.197.68.152",
				AuthorizationType: "rbac",
				CaCert:            "-----BEGIN CERTIFICATE-----\r\nAAAAA\r\n-----END CERTIFICATE-----",
			},
			ManagementProject: &ManagementProject{
				ID:                2,
				Description:       "sdhfgnbsdjfhg",
				Name:              "project2",
				NameWithNamespace: "John Doe8 / project2",
				Path:              "project2",
				PathWithNamespace: "namespace2/project2",
				CreatedAt:         &createdAt2,
			},
		},
		{
			ID:   19,
			Name: "cluster-2",
		},
	}
	if !reflect.DeepEqual(want, clusters) {
		t.Errorf("GroupCluster.ListClusters returned %+v, want %+v", clusters, want)
	}
}

func TestGetGroupCluster(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/26/clusters/18", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `{
			"id":18,
			"name":"cluster-1",
			"domain":"example.com",
			"created_at":"2019-01-02T20:18:12.563Z",
			"managed": true,
			"enabled": true,
			"provider_type":"user",
			"platform_type":"kubernetes",
			"environment_scope":"*",
			"cluster_type":"group_type",
			"user":
			{
			  "id":1,
			  "name":"Administrator",
			  "username":"root",
			  "state":"active",
			  "avatar_url":"https://www.gravatar.com/avatar/4249f4df72b..",
			  "web_url":"https://gitlab.example.com/root"
			},
			"platform_kubernetes":
			{
			  "api_url":"https://104.197.68.152",
			  "authorization_type":"rbac",
			  "ca_cert":"-----BEGIN CERTIFICATE-----\r\nAAAAA\r\n-----END CERTIFICATE-----"
			},
			"management_project":
			{
			  "id":2,
			  "description": "skjdfgsdfg",
			  "name":"project2",
			  "name_with_namespace":"John Doe8 / project2",
			  "path":"project2",
			  "path_with_namespace":"namespace2/project2",
			  "created_at":"2019-10-11T02:55:54.138Z"
			},
			"group":
			{
			  "id":26,
			  "name":"group-with-clusters-api",
			  "web_url":"https://gitlab.example.com/group-with-clusters-api"
			}
		  }`)
	})

	cluster, _, err := client.GroupCluster.GetCluster(26, 18)
	if err != nil {
		t.Errorf("GroupCluster.GetCluster returned error: %v", err)
	}

	createdAt, err := time.Parse(timeLayout, "2019-01-02T20:18:12.563Z")
	if err != nil {
		t.Errorf("DeployKeys.ListAllDeployKeys returned an error while parsing time: %v", err)
	}

	createdAt2, err := time.Parse(timeLayout, "2019-10-11T02:55:54.138Z")
	if err != nil {
		t.Errorf("DeployKeys.ListAllDeployKeys returned an error while parsing time: %v", err)
	}

	want := &GroupCluster{
		ID:               18,
		Name:             "cluster-1",
		Domain:           "example.com",
		CreatedAt:        &createdAt,
		Managed:          true,
		Enabled:          true,
		ProviderType:     "user",
		PlatformType:     "kubernetes",
		EnvironmentScope: "*",
		ClusterType:      "group_type",
		User: &User{
			ID:        1,
			Name:      "Administrator",
			Username:  "root",
			State:     "active",
			AvatarURL: "https://www.gravatar.com/avatar/4249f4df72b..",
			WebURL:    "https://gitlab.example.com/root",
		},
		PlatformKubernetes: &PlatformKubernetes{
			APIURL:            "https://104.197.68.152",
			AuthorizationType: "rbac",
			CaCert:            "-----BEGIN CERTIFICATE-----\r\nAAAAA\r\n-----END CERTIFICATE-----",
		},
		ManagementProject: &ManagementProject{
			ID:                2,
			Description:       "skjdfgsdfg",
			Name:              "project2",
			NameWithNamespace: "John Doe8 / project2",
			Path:              "project2",
			PathWithNamespace: "namespace2/project2",
			CreatedAt:         &createdAt2,
		},
		Group: &Group{
			ID:     26,
			Name:   "group-with-clusters-api",
			WebURL: "https://gitlab.example.com/group-with-clusters-api",
		},
	}
	if !reflect.DeepEqual(want, cluster) {
		t.Errorf("GroupCluster.GetCluster returned %+v, want %+v", cluster, want)
	}
}

func TestAddGroupCluster(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/26/clusters/user", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		fmt.Fprintf(w, `{
			"id":24,
			"name":"cluster-5",
			"created_at":"2019-01-03T21:53:40.610Z",
			"managed": true,
			"enabled": true,
			"provider_type":"user",
			"platform_type":"kubernetes",
			"environment_scope":"*",
			"cluster_type":"group_type",
			"user":
			{
			  "id":1,
			  "name":"Administrator",
			  "username":"root",
			  "state":"active",
			  "avatar_url":"https://www.gravatar.com/avatar/4249f4df72b..",
			  "web_url":"https://gitlab.example.com/root"
			},
			"platform_kubernetes":
			{
			  "api_url":"https://35.111.51.20",
			  "authorization_type":"rbac",
			  "ca_cert":"-----BEGIN CERTIFICATE-----\r\nAAAAA\r\n-----END CERTIFICATE-----"
			},
			"management_project":null,
			"group":
			{
			  "id":26,
			  "name":"group-with-clusters-api",
			  "web_url":"https://gitlab.example.com/root/group-with-clusters-api"
			}
		  }`)
	})

	cluster, _, err := client.GroupCluster.AddCluster(26, &AddGroupClusterOptions{})
	if err != nil {
		t.Errorf("GroupCluster.AddCluster returned error: %v", err)
	}

	createdAt, err := time.Parse(timeLayout, "2019-01-03T21:53:40.610Z")
	if err != nil {
		t.Errorf("DeployKeys.ListAllDeployKeys returned an error while parsing time: %v", err)
	}

	want := &GroupCluster{
		ID:               24,
		Name:             "cluster-5",
		CreatedAt:        &createdAt,
		Managed:          true,
		Enabled:          true,
		ProviderType:     "user",
		PlatformType:     "kubernetes",
		EnvironmentScope: "*",
		ClusterType:      "group_type",
		User: &User{
			ID:        1,
			Name:      "Administrator",
			Username:  "root",
			State:     "active",
			AvatarURL: "https://www.gravatar.com/avatar/4249f4df72b..",
			WebURL:    "https://gitlab.example.com/root",
		},
		PlatformKubernetes: &PlatformKubernetes{
			APIURL:            "https://35.111.51.20",
			AuthorizationType: "rbac",
			CaCert:            "-----BEGIN CERTIFICATE-----\r\nAAAAA\r\n-----END CERTIFICATE-----",
		},
		ManagementProject: nil,
		Group: &Group{
			ID:     26,
			Name:   "group-with-clusters-api",
			WebURL: "https://gitlab.example.com/root/group-with-clusters-api",
		},
	}
	if !reflect.DeepEqual(want, cluster) {
		t.Errorf("GroupCluster.AddCluster returned %+v, want %+v", cluster, want)
	}
}

func TestEditGroupCluster(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/26/clusters/24", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPut)
		fmt.Fprintf(w, `{
			"id":24,
			"name":"new-cluster-name",
			"domain":"new-domain.com",
			"created_at":"2019-01-03T21:53:40.610Z",
			"managed": true,
			"enabled": true,
			"provider_type":"user",
			"platform_type":"kubernetes",
			"environment_scope":"*",
			"cluster_type":"group_type",
			"user":
			{
			  "id":1,
			  "name":"Administrator",
			  "username":"root",
			  "state":"active",
			  "avatar_url":"https://www.gravatar.com/avatar/4249f4df72b..",
			  "web_url":"https://gitlab.example.com/root"
			},
			"platform_kubernetes":
			{
			  "api_url":"https://new-api-url.com",
			  "authorization_type":"rbac"
			},
			"management_project":
			{
			  "id":2,
			  "description":"sjdkfngjkdsfngdfgndfg",
			  "name":"project2",
			  "name_with_namespace":"John Doe8 / project2",
			  "path":"project2",
			  "path_with_namespace":"namespace2/project2",
			  "created_at":"2019-10-11T02:55:54.138Z"
			},
			"group":
			{
			  "id":26,
			  "name":"group-with-clusters-api",
			  "web_url":"https://gitlab.example.com/group-with-clusters-api"
			}
		  }`)
	})

	name := "new-cluster-name"
	domain := "new-domain.com"
	enviromentScope := "*"
	apiURL := "https://new-api-url.com"
	opt := &EditGroupClusterOptions{
		Name:             &name,
		Domain:           &domain,
		EnvironmentScope: &enviromentScope,
		PlatformKubernetes: &EditGroupPlatformKubernetesOptions{
			APIURL: &apiURL,
		},
	}
	cluster, _, err := client.GroupCluster.EditCluster(26, 24, opt)
	if err != nil {
		t.Errorf("GroupCluster.EditCluster returned error: %v", err)
	}

	createdAt, err := time.Parse(timeLayout, "2019-01-03T21:53:40.610Z")
	if err != nil {
		t.Errorf("DeployKeys.ListAllDeployKeys returned an error while parsing time: %v", err)
	}

	createdAt2, err := time.Parse(timeLayout, "2019-10-11T02:55:54.138Z")
	if err != nil {
		t.Errorf("DeployKeys.ListAllDeployKeys returned an error while parsing time: %v", err)
	}

	want := &GroupCluster{
		ID:               24,
		Name:             "new-cluster-name",
		Domain:           "new-domain.com",
		CreatedAt:        &createdAt,
		Managed:          true,
		Enabled:          true,
		ProviderType:     "user",
		PlatformType:     "kubernetes",
		EnvironmentScope: "*",
		ClusterType:      "group_type",
		User: &User{
			ID:        1,
			Name:      "Administrator",
			Username:  "root",
			State:     "active",
			AvatarURL: "https://www.gravatar.com/avatar/4249f4df72b..",
			WebURL:    "https://gitlab.example.com/root",
		},
		PlatformKubernetes: &PlatformKubernetes{
			APIURL:            "https://new-api-url.com",
			AuthorizationType: "rbac",
		},
		ManagementProject: &ManagementProject{
			ID:                2,
			Description:       "sjdkfngjkdsfngdfgndfg",
			Name:              "project2",
			NameWithNamespace: "John Doe8 / project2",
			Path:              "project2",
			PathWithNamespace: "namespace2/project2",
			CreatedAt:         &createdAt2,
		},
		Group: &Group{
			ID:     26,
			Name:   "group-with-clusters-api",
			WebURL: "https://gitlab.example.com/group-with-clusters-api",
		},
	}
	if !reflect.DeepEqual(want, cluster) {
		t.Errorf("GroupCluster.EditCluster returned %+v, want %+v", cluster, want)
	}
}

func TestDeleteGroupCluster(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/26/clusters/23", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodDelete)
	})

	_, err := client.GroupCluster.DeleteCluster(26, 23)
	if err != nil {
		t.Errorf("GroupCluster.DeleteCluster returned error: %v", err)
	}
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/group_epic_boards.go000066400000000000000000000056571475761473200251110ustar00rootroot00000000000000//
// Copyright 2021, Patrick Webster
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"fmt"
	"net/http"
)

// GroupEpicBoardsService handles communication with the group epic board
// related methods of the GitLab API.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_epic_boards.html
type GroupEpicBoardsService struct {
	client *Client
}

// GroupEpicBoard represents a GitLab group epic board.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_epic_boards.html
type GroupEpicBoard struct {
	ID     int             `json:"id"`
	Name   string          `json:"name"`
	Group  *Group          `json:"group"`
	Labels []*LabelDetails `json:"labels"`
	Lists  []*BoardList    `json:"lists"`
}

func (b GroupEpicBoard) String() string {
	return Stringify(b)
}

// ListGroupEpicBoardsOptions represents the available
// ListGroupEpicBoards() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_epic_boards.html#list-all-epic-boards-in-a-group
type ListGroupEpicBoardsOptions ListOptions

// ListGroupEpicBoards gets a list of all epic boards in a group.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_epic_boards.html#list-all-epic-boards-in-a-group
func (s *GroupEpicBoardsService) ListGroupEpicBoards(gid interface{}, opt *ListGroupEpicBoardsOptions, options ...RequestOptionFunc) ([]*GroupEpicBoard, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/epic_boards", PathEscape(group))

	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var gs []*GroupEpicBoard
	resp, err := s.client.Do(req, &gs)
	if err != nil {
		return nil, resp, err
	}

	return gs, resp, nil
}

// GetGroupEpicBoard gets a single epic board of a group.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_epic_boards.html#single-group-epic-board
func (s *GroupEpicBoardsService) GetGroupEpicBoard(gid interface{}, board int, options ...RequestOptionFunc) (*GroupEpicBoard, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/epic_boards/%d", PathEscape(group), board)

	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	gib := new(GroupEpicBoard)
	resp, err := s.client.Do(req, gib)
	if err != nil {
		return nil, resp, err
	}

	return gib, resp, nil
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/group_epic_boards_test.go000066400000000000000000000133461475761473200261420ustar00rootroot00000000000000package gitlab

import (
	"fmt"
	"net/http"
	"testing"

	"github.com/stretchr/testify/require"
)

func TestGroupEpicBoardsService_ListGroupEpicBoards(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/5/epic_boards", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
		[
			{
			  "id": 1,
			  "name": "group epic board",
			  "group": {
				"id": 5,
				"name": "Documentcloud",
				"web_url": "http://example.com/groups/documentcloud"
			  },
			  "hide_backlog_list": false,
			  "hide_closed_list": false,
			  "labels": [
				{
				  "id": 1,
				  "name": "Board Label",
				  "color": "#c21e56",
				  "group_id": 5,
				  "description": "label applied to the epic board"
				}
			  ],
			  "lists": [
				{
				  "id": 1,
				  "label": {
					"id": 69,
					"name": "Testing",
					"color": "#F0AD4E",
					"description": null
				  },
				  "position": 1,
				  "list_type": "label"
				},
				{
				  "id": 2,
				  "label": {
					"id": 70,
					"name": "Ready",
					"color": "#FF0000",
					"description": null
				  },
				  "position": 2,
				  "list_type": "label"
				},
				{
				  "id": 3,
				  "label": {
					"id": 71,
					"name": "Production",
					"color": "#FF5F00",
					"description": null
				  },
				  "position": 3,
				  "list_type": "label"
				}
			  ]
			}
		  ]		  
		`)
	})

	want := []*GroupEpicBoard{{
		ID:   1,
		Name: "group epic board",
		Group: &Group{
			ID:     5,
			Name:   "Documentcloud",
			WebURL: "http://example.com/groups/documentcloud",
		},
		Labels: []*LabelDetails{
			{
				ID:          1,
				Name:        "Board Label",
				Color:       "#c21e56",
				Description: "label applied to the epic board",
			},
		},
		Lists: []*BoardList{
			{
				ID: 1,
				Label: &Label{
					ID:          69,
					Name:        "Testing",
					Color:       "#F0AD4E",
					Description: "",
				},
				Position: 1,
			},
			{
				ID: 2,
				Label: &Label{
					ID:          70,
					Name:        "Ready",
					Color:       "#FF0000",
					Description: "",
				},
				Position: 2,
			},
			{
				ID: 3,
				Label: &Label{
					ID:          71,
					Name:        "Production",
					Color:       "#FF5F00",
					Description: "",
				},
				Position: 3,
			},
		},
	}}

	gibs, resp, err := client.GroupEpicBoards.ListGroupEpicBoards(5, nil, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, gibs)

	gibs, resp, err = client.GroupEpicBoards.ListGroupEpicBoards(5.01, nil, nil)
	require.EqualError(t, err, "invalid ID type 5.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, gibs)

	gibs, resp, err = client.GroupEpicBoards.ListGroupEpicBoards(5, nil, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, gibs)

	gibs, resp, err = client.GroupEpicBoards.ListGroupEpicBoards(3, nil, nil)
	require.Error(t, err)
	require.Nil(t, gibs)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestGroupEpicBoardsService_GetGroupEpicBoard(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/5/epic_boards/1", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
		{
			"id": 1,
			"name": "group epic board",
			"group": {
			  "id": 5,
			  "name": "Documentcloud",
			  "web_url": "http://example.com/groups/documentcloud"
			},
			"labels": [
			  {
				"id": 1,
				"name": "Board Label",
				"color": "#c21e56",
				"group_id": 5,
				"description": "label applied to the epic board"
			  }
			],
			"lists" : [
			  {
				"id" : 1,
				"label" : {
				  "id": 69,
				  "name" : "Testing",
				  "color" : "#F0AD4E",
				  "description" : null
				},
				"position" : 1,
				"list_type": "label"
			  },
			  {
				"id" : 2,
				"label" : {
				  "id": 70,
				  "name" : "Ready",
				  "color" : "#FF0000",
				  "description" : null
				},
				"position" : 2,
				"list_type": "label"
			  },
			  {
				"id" : 3,
				"label" : {
				  "id": 71,
				  "name" : "Production",
				  "color" : "#FF5F00",
				  "description" : null
				},
				"position" : 3,
				"list_type": "label"
			  }
			]
		  }		
		`)
	})

	want := &GroupEpicBoard{
		ID:   1,
		Name: "group epic board",
		Group: &Group{
			ID:     5,
			Name:   "Documentcloud",
			WebURL: "http://example.com/groups/documentcloud",
		},
		Labels: []*LabelDetails{
			{
				ID:          1,
				Name:        "Board Label",
				Color:       "#c21e56",
				Description: "label applied to the epic board",
			},
		},
		Lists: []*BoardList{
			{
				ID: 1,
				Label: &Label{
					ID:          69,
					Name:        "Testing",
					Color:       "#F0AD4E",
					Description: "",
				},
				Position: 1,
			},
			{
				ID: 2,
				Label: &Label{
					ID:          70,
					Name:        "Ready",
					Color:       "#FF0000",
					Description: "",
				},
				Position: 2,
			},
			{
				ID: 3,
				Label: &Label{
					ID:          71,
					Name:        "Production",
					Color:       "#FF5F00",
					Description: "",
				},
				Position: 3,
			},
		},
	}

	gib, resp, err := client.GroupEpicBoards.GetGroupEpicBoard(5, 1, nil, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, gib)

	gib, resp, err = client.GroupEpicBoards.GetGroupEpicBoard(5.01, 1, nil, nil)
	require.EqualError(t, err, "invalid ID type 5.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, gib)

	gib, resp, err = client.GroupEpicBoards.GetGroupEpicBoard(5, 1, nil, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, gib)

	gib, resp, err = client.GroupEpicBoards.GetGroupEpicBoard(3, 1, nil, nil)
	require.Error(t, err)
	require.Nil(t, gib)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/group_hooks.go000066400000000000000000000315071475761473200237530ustar00rootroot00000000000000//
// Copyright 2021, Eric Stevens
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"fmt"
	"net/http"
	"time"
)

// GroupHook represents a GitLab group hook.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/groups.html#list-group-hooks
type GroupHook struct {
	ID                        int                 `json:"id"`
	URL                       string              `json:"url"`
	GroupID                   int                 `json:"group_id"`
	PushEvents                bool                `json:"push_events"`
	PushEventsBranchFilter    string              `json:"push_events_branch_filter"`
	IssuesEvents              bool                `json:"issues_events"`
	ConfidentialIssuesEvents  bool                `json:"confidential_issues_events"`
	ConfidentialNoteEvents    bool                `json:"confidential_note_events"`
	MergeRequestsEvents       bool                `json:"merge_requests_events"`
	TagPushEvents             bool                `json:"tag_push_events"`
	NoteEvents                bool                `json:"note_events"`
	JobEvents                 bool                `json:"job_events"`
	PipelineEvents            bool                `json:"pipeline_events"`
	WikiPageEvents            bool                `json:"wiki_page_events"`
	DeploymentEvents          bool                `json:"deployment_events"`
	ReleasesEvents            bool                `json:"releases_events"`
	SubGroupEvents            bool                `json:"subgroup_events"`
	MemberEvents              bool                `json:"member_events"`
	EnableSSLVerification     bool                `json:"enable_ssl_verification"`
	AlertStatus               string              `json:"alert_status"`
	CreatedAt                 *time.Time          `json:"created_at"`
	CustomWebhookTemplate     string              `json:"custom_webhook_template"`
	ResourceAccessTokenEvents bool                `json:"resource_access_token_events"`
	CustomHeaders             []*HookCustomHeader `url:"custom_headers,omitempty" json:"custom_headers,omitempty"`
}

// ListGroupHooksOptions represents the available ListGroupHooks() options.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/groups.html#list-group-hooks
type ListGroupHooksOptions ListOptions

// ListGroupHooks gets a list of group hooks.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/groups.html#list-group-hooks
func (s *GroupsService) ListGroupHooks(gid interface{}, opt *ListGroupHooksOptions, options ...RequestOptionFunc) ([]*GroupHook, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/hooks", PathEscape(group))

	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}
	var gh []*GroupHook
	resp, err := s.client.Do(req, &gh)
	if err != nil {
		return nil, resp, err
	}

	return gh, resp, nil
}

// GetGroupHook gets a specific hook for a group.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/groups.html#get-group-hook
func (s *GroupsService) GetGroupHook(pid interface{}, hook int, options ...RequestOptionFunc) (*GroupHook, *Response, error) {
	group, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/hooks/%d", PathEscape(group), hook)

	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	gh := new(GroupHook)
	resp, err := s.client.Do(req, gh)
	if err != nil {
		return nil, resp, err
	}

	return gh, resp, nil
}

// AddGroupHookOptions represents the available AddGroupHook() options.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/groups.html#add-group-hook
type AddGroupHookOptions struct {
	URL                       *string              `url:"url,omitempty" json:"url,omitempty"`
	PushEvents                *bool                `url:"push_events,omitempty"  json:"push_events,omitempty"`
	PushEventsBranchFilter    *string              `url:"push_events_branch_filter,omitempty"  json:"push_events_branch_filter,omitempty"`
	IssuesEvents              *bool                `url:"issues_events,omitempty"  json:"issues_events,omitempty"`
	ConfidentialIssuesEvents  *bool                `url:"confidential_issues_events,omitempty"  json:"confidential_issues_events,omitempty"`
	ConfidentialNoteEvents    *bool                `url:"confidential_note_events,omitempty"  json:"confidential_note_events,omitempty"`
	MergeRequestsEvents       *bool                `url:"merge_requests_events,omitempty"  json:"merge_requests_events,omitempty"`
	TagPushEvents             *bool                `url:"tag_push_events,omitempty"  json:"tag_push_events,omitempty"`
	NoteEvents                *bool                `url:"note_events,omitempty"  json:"note_events,omitempty"`
	JobEvents                 *bool                `url:"job_events,omitempty"  json:"job_events,omitempty"`
	PipelineEvents            *bool                `url:"pipeline_events,omitempty"  json:"pipeline_events,omitempty"`
	WikiPageEvents            *bool                `url:"wiki_page_events,omitempty"  json:"wiki_page_events,omitempty"`
	DeploymentEvents          *bool                `url:"deployment_events,omitempty" json:"deployment_events,omitempty"`
	ReleasesEvents            *bool                `url:"releases_events,omitempty" json:"releases_events,omitempty"`
	SubGroupEvents            *bool                `url:"subgroup_events,omitempty" json:"subgroup_events,omitempty"`
	MemberEvents              *bool                `url:"member_events,omitempty" json:"member_events,omitempty"`
	EnableSSLVerification     *bool                `url:"enable_ssl_verification,omitempty"  json:"enable_ssl_verification,omitempty"`
	Token                     *string              `url:"token,omitempty" json:"token,omitempty"`
	ResourceAccessTokenEvents *bool                `url:"resource_access_token_events,omitempty" json:"resource_access_token_events,omitempty"`
	CustomWebhookTemplate     *string              `url:"custom_webhook_template,omitempty" json:"custom_webhook_template,omitempty"`
	CustomHeaders             *[]*HookCustomHeader `url:"custom_headers,omitempty" json:"custom_headers,omitempty"`
}

// AddGroupHook create a new group scoped webhook.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/groups.html#add-group-hook
func (s *GroupsService) AddGroupHook(gid interface{}, opt *AddGroupHookOptions, options ...RequestOptionFunc) (*GroupHook, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/hooks", PathEscape(group))

	req, err := s.client.NewRequest(http.MethodPost, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	gh := new(GroupHook)
	resp, err := s.client.Do(req, gh)
	if err != nil {
		return nil, resp, err
	}

	return gh, resp, nil
}

// EditGroupHookOptions represents the available EditGroupHook() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/groups.html#edit-group-hook
type EditGroupHookOptions struct {
	URL                       *string              `url:"url,omitempty" json:"url,omitempty"`
	PushEvents                *bool                `url:"push_events,omitempty" json:"push_events,omitempty"`
	PushEventsBranchFilter    *string              `url:"push_events_branch_filter,omitempty"  json:"push_events_branch_filter,omitempty"`
	IssuesEvents              *bool                `url:"issues_events,omitempty" json:"issues_events,omitempty"`
	ConfidentialIssuesEvents  *bool                `url:"confidential_issues_events,omitempty" json:"confidential_issues_events,omitempty"`
	ConfidentialNoteEvents    *bool                `url:"confidential_note_events,omitempty" json:"confidential_note_events,omitempty"`
	MergeRequestsEvents       *bool                `url:"merge_requests_events,omitempty" json:"merge_requests_events,omitempty"`
	TagPushEvents             *bool                `url:"tag_push_events,omitempty" json:"tag_push_events,omitempty"`
	NoteEvents                *bool                `url:"note_events,omitempty" json:"note_events,omitempty"`
	JobEvents                 *bool                `url:"job_events,omitempty" json:"job_events,omitempty"`
	PipelineEvents            *bool                `url:"pipeline_events,omitempty" json:"pipeline_events,omitempty"`
	WikiPageEvents            *bool                `url:"wiki_page_events,omitempty" json:"wiki_page_events,omitempty"`
	DeploymentEvents          *bool                `url:"deployment_events,omitempty" json:"deployment_events,omitempty"`
	ReleasesEvents            *bool                `url:"releases_events,omitempty" json:"releases_events,omitempty"`
	SubGroupEvents            *bool                `url:"subgroup_events,omitempty" json:"subgroup_events,omitempty"`
	MemberEvents              *bool                `url:"member_events,omitempty" json:"member_events,omitempty"`
	EnableSSLVerification     *bool                `url:"enable_ssl_verification,omitempty" json:"enable_ssl_verification,omitempty"`
	Token                     *string              `url:"token,omitempty" json:"token,omitempty"`
	ResourceAccessTokenEvents *bool                `url:"resource_access_token_events,omitempty" json:"resource_access_token_events,omitempty"`
	CustomWebhookTemplate     *string              `url:"custom_webhook_template,omitempty" json:"custom_webhook_template,omitempty"`
	CustomHeaders             *[]*HookCustomHeader `url:"custom_headers,omitempty" json:"custom_headers,omitempty"`
}

// EditGroupHook edits a hook for a specified group.
//
// Gitlab API docs:
// https://docs.gitlab.com/ee/api/groups.html#edit-group-hook
func (s *GroupsService) EditGroupHook(pid interface{}, hook int, opt *EditGroupHookOptions, options ...RequestOptionFunc) (*GroupHook, *Response, error) {
	group, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/hooks/%d", PathEscape(group), hook)

	req, err := s.client.NewRequest(http.MethodPut, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	gh := new(GroupHook)
	resp, err := s.client.Do(req, gh)
	if err != nil {
		return nil, resp, err
	}

	return gh, resp, nil
}

// DeleteGroupHook removes a hook from a group. This is an idempotent
// method and can be called multiple times.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/groups.html#delete-group-hook
func (s *GroupsService) DeleteGroupHook(pid interface{}, hook int, options ...RequestOptionFunc) (*Response, error) {
	group, err := parseID(pid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("groups/%s/hooks/%d", PathEscape(group), hook)

	req, err := s.client.NewRequest(http.MethodDelete, u, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}

// TriggerTestGroupHook triggers a test hook for a specified group.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_webhooks.html#trigger-a-test-group-hook
func (s *GroupsService) TriggerTestGroupHook(pid interface{}, hook int, trigger GroupHookTrigger, options ...RequestOptionFunc) (*Response, error) {
	group, err := parseID(pid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("groups/%s/hooks/%d/test/%s", PathEscape(group), hook, trigger)

	req, err := s.client.NewRequest(http.MethodPost, u, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}

// SetGroupCustomHeader creates or updates a group custom webhook header.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/groups.html#set-a-custom-header
func (s *GroupsService) SetGroupCustomHeader(gid interface{}, hook int, key string, opt *SetHookCustomHeaderOptions, options ...RequestOptionFunc) (*Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("groups/%s/hooks/%d/custom_headers/%s", PathEscape(group), hook, key)

	req, err := s.client.NewRequest(http.MethodPut, u, opt, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}

// DeleteGroupCustomHeader deletes a group custom webhook header.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/groups.html#delete-a-custom-header
func (s *GroupsService) DeleteGroupCustomHeader(gid interface{}, hook int, key string, options ...RequestOptionFunc) (*Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("groups/%s/hooks/%d/custom_headers/%s", PathEscape(group), hook, key)

	req, err := s.client.NewRequest(http.MethodDelete, u, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/group_hooks_test.go000066400000000000000000000324411475761473200250100ustar00rootroot00000000000000//
// Copyright 2021, Eric Stevens
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"reflect"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
)

func TestListGroupHooks(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1/hooks", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprint(w, `
[
	{
		"id": 1,
		"url": "http://example.com/hook",
		"group_id": 3,
		"push_events": true,
		"push_events_branch_filter": "main",
		"issues_events": true,
		"confidential_issues_events": true,
		"merge_requests_events": true,
		"tag_push_events": true,
		"note_events": true,
		"job_events": true,
		"pipeline_events": true,
		"wiki_page_events": true,
		"deployment_events": true,
		"releases_events": true,
		"subgroup_events": true,
		"member_events": true,
		"enable_ssl_verification": true,
		"alert_status": "executable",
		"created_at": "2012-10-12T17:04:47Z",
		"resource_access_token_events": true,
		"custom_headers": [
			{"key": "Authorization"},
			{"key": "OtherHeader"}
		]
	}
]`)
	})

	groupHooks, _, err := client.Groups.ListGroupHooks(1, nil)
	if err != nil {
		t.Error(err)
	}

	datePointer := time.Date(2012, 10, 12, 17, 4, 47, 0, time.UTC)
	want := []*GroupHook{{
		ID:                        1,
		URL:                       "http://example.com/hook",
		GroupID:                   3,
		PushEvents:                true,
		PushEventsBranchFilter:    "main",
		IssuesEvents:              true,
		ConfidentialIssuesEvents:  true,
		MergeRequestsEvents:       true,
		TagPushEvents:             true,
		NoteEvents:                true,
		JobEvents:                 true,
		PipelineEvents:            true,
		WikiPageEvents:            true,
		DeploymentEvents:          true,
		ReleasesEvents:            true,
		SubGroupEvents:            true,
		MemberEvents:              true,
		EnableSSLVerification:     true,
		AlertStatus:               "executable",
		CreatedAt:                 &datePointer,
		ResourceAccessTokenEvents: true,
		CustomHeaders: []*HookCustomHeader{
			{
				Key: "Authorization",
			},
			{
				Key: "OtherHeader",
			},
		},
	}}

	if !reflect.DeepEqual(groupHooks, want) {
		t.Errorf("listGroupHooks returned \ngot:\n%v\nwant:\n%v", Stringify(groupHooks), Stringify(want))
	}
}

func TestGetGroupHook(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1/hooks/1", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprint(w, `
{
	"id": 1,
	"url": "http://example.com/hook",
	"group_id": 3,
	"push_events": true,
	"push_events_branch_filter": "main",
	"issues_events": true,
	"confidential_issues_events": true,
	"merge_requests_events": true,
	"tag_push_events": true,
	"note_events": true,
	"job_events": true,
	"pipeline_events": true,
	"wiki_page_events": true,
	"deployment_events": true,
	"releases_events": true,
	"subgroup_events": true,
	"member_events": true,
	"enable_ssl_verification": true,
	"alert_status": "executable",
	"created_at": "2012-10-12T17:04:47Z",
	"resource_access_token_events": true,
	"custom_headers": [
		{"key": "Authorization"},
		{"key": "OtherHeader"}
	]
}`)
	})

	groupHook, _, err := client.Groups.GetGroupHook(1, 1)
	if err != nil {
		t.Error(err)
	}

	datePointer := time.Date(2012, 10, 12, 17, 4, 47, 0, time.UTC)
	want := &GroupHook{
		ID:                        1,
		URL:                       "http://example.com/hook",
		GroupID:                   3,
		PushEvents:                true,
		PushEventsBranchFilter:    "main",
		IssuesEvents:              true,
		ConfidentialIssuesEvents:  true,
		MergeRequestsEvents:       true,
		TagPushEvents:             true,
		NoteEvents:                true,
		JobEvents:                 true,
		PipelineEvents:            true,
		WikiPageEvents:            true,
		DeploymentEvents:          true,
		ReleasesEvents:            true,
		SubGroupEvents:            true,
		MemberEvents:              true,
		EnableSSLVerification:     true,
		AlertStatus:               "executable",
		CreatedAt:                 &datePointer,
		ResourceAccessTokenEvents: true,
		CustomHeaders: []*HookCustomHeader{
			{
				Key: "Authorization",
			},
			{
				Key: "OtherHeader",
			},
		},
	}

	if !reflect.DeepEqual(groupHook, want) {
		t.Errorf("getGroupHooks returned \ngot:\n%v\nwant:\n%v", Stringify(groupHook), Stringify(want))
	}
}

func TestAddGroupHook(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1/hooks", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		fmt.Fprint(w, `
{
	"id": 1,
	"url": "http://example.com/hook",
	"group_id": 3,
	"push_events": true,
	"push_events_branch_filter": "main",
	"issues_events": true,
	"confidential_issues_events": true,
	"merge_requests_events": true,
	"tag_push_events": true,
	"note_events": true,
	"job_events": true,
	"pipeline_events": true,
	"wiki_page_events": true,
	"deployment_events": true,
	"releases_events": true,
	"subgroup_events": true,
	"member_events": true,
	"enable_ssl_verification": true,
	"created_at": "2012-10-12T17:04:47Z",
	"custom_webhook_template": "addTestValue",
	"resource_access_token_events": true,
	"custom_headers": [
		{"key": "Authorization", "value": "testMe"},
		{"key": "OtherHeader", "value": "otherTest"}
	]
}`)
	})

	url := "http://www.example.com/hook"
	opt := &AddGroupHookOptions{
		URL: &url,
	}

	groupHooks, _, err := client.Groups.AddGroupHook(1, opt)
	if err != nil {
		t.Error(err)
	}

	datePointer := time.Date(2012, 10, 12, 17, 4, 47, 0, time.UTC)
	want := &GroupHook{
		ID:                        1,
		URL:                       "http://example.com/hook",
		GroupID:                   3,
		PushEvents:                true,
		PushEventsBranchFilter:    "main",
		IssuesEvents:              true,
		ConfidentialIssuesEvents:  true,
		ConfidentialNoteEvents:    false,
		MergeRequestsEvents:       true,
		TagPushEvents:             true,
		NoteEvents:                true,
		JobEvents:                 true,
		PipelineEvents:            true,
		WikiPageEvents:            true,
		DeploymentEvents:          true,
		ReleasesEvents:            true,
		SubGroupEvents:            true,
		MemberEvents:              true,
		EnableSSLVerification:     true,
		CreatedAt:                 &datePointer,
		CustomWebhookTemplate:     "addTestValue",
		ResourceAccessTokenEvents: true,
		CustomHeaders: []*HookCustomHeader{
			{
				Key:   "Authorization",
				Value: "testMe",
			},
			{
				Key:   "OtherHeader",
				Value: "otherTest",
			},
		},
	}

	if !reflect.DeepEqual(groupHooks, want) {
		t.Errorf("AddGroupHook returned \ngot:\n%v\nwant:\n%v", Stringify(groupHooks), Stringify(want))
	}
}

func TestEditGroupHook(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1/hooks/1", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPut)
		fmt.Fprint(w, `
{
	"id": 1,
	"url": "http://example.com/hook",
	"group_id": 3,
	"push_events": true,
	"push_events_branch_filter": "main",
	"issues_events": true,
	"confidential_issues_events": true,
	"merge_requests_events": true,
	"tag_push_events": true,
	"note_events": true,
	"job_events": true,
	"pipeline_events": true,
	"wiki_page_events": true,
	"deployment_events": true,
	"releases_events": true,
	"subgroup_events": true,
	"member_events": true,
	"enable_ssl_verification": true,
	"created_at": "2012-10-12T17:04:47Z",
	"custom_webhook_template": "testValue",
	"resource_access_token_events": true,
	"custom_headers": [
		{"key": "Authorization", "value": "testMe"},
		{"key": "OtherHeader", "value": "otherTest"}
	]
}`)
	})

	url := "http://www.example.com/hook"
	opt := &EditGroupHookOptions{
		URL: &url,
	}

	groupHooks, _, err := client.Groups.EditGroupHook(1, 1, opt)
	if err != nil {
		t.Error(err)
	}

	datePointer := time.Date(2012, 10, 12, 17, 4, 47, 0, time.UTC)
	want := &GroupHook{
		ID:                        1,
		URL:                       "http://example.com/hook",
		GroupID:                   3,
		PushEvents:                true,
		PushEventsBranchFilter:    "main",
		IssuesEvents:              true,
		ConfidentialIssuesEvents:  true,
		ConfidentialNoteEvents:    false,
		MergeRequestsEvents:       true,
		TagPushEvents:             true,
		NoteEvents:                true,
		JobEvents:                 true,
		PipelineEvents:            true,
		WikiPageEvents:            true,
		DeploymentEvents:          true,
		ReleasesEvents:            true,
		SubGroupEvents:            true,
		MemberEvents:              true,
		EnableSSLVerification:     true,
		CreatedAt:                 &datePointer,
		CustomWebhookTemplate:     "testValue",
		ResourceAccessTokenEvents: true,
		CustomHeaders: []*HookCustomHeader{
			{
				Key:   "Authorization",
				Value: "testMe",
			},
			{
				Key:   "OtherHeader",
				Value: "otherTest",
			},
		},
	}

	if !reflect.DeepEqual(groupHooks, want) {
		t.Errorf("EditGroupHook returned \ngot:\n%v\nwant:\n%v", Stringify(groupHooks), Stringify(want))
	}
}

func TestTriggerTestGroupHook(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1/hooks/1/test/push_events", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		w.WriteHeader(http.StatusCreated)
		fmt.Fprint(w, `{"message":"201 Created"}`)
	})

	mux.HandleFunc("/api/v4/groups/1/hooks/1/test/invalid_trigger", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprint(w, `{"error": "trigger does not have a valid value"}`)
	})

	tests := []struct {
		name       string
		groupID    interface{}
		hookID     int
		trigger    GroupHookTrigger
		wantErr    bool
		wantStatus int
		wantErrMsg string
	}{
		{
			name:       "Valid trigger",
			groupID:    1,
			hookID:     1,
			trigger:    GroupHookTriggerPush,
			wantErr:    false,
			wantStatus: http.StatusCreated,
		},
		{
			name:       "Invalid group ID",
			groupID:    "invalid",
			hookID:     1,
			trigger:    GroupHookTriggerPush,
			wantErr:    true,
			wantStatus: http.StatusNotFound,
		},
		{
			name:       "Invalid trigger type",
			groupID:    1,
			hookID:     1,
			trigger:    "invalid_trigger",
			wantErr:    true,
			wantStatus: http.StatusBadRequest,
			wantErrMsg: "trigger does not have a valid value",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			resp, err := client.Groups.TriggerTestGroupHook(tt.groupID, tt.hookID, tt.trigger)

			if tt.wantErr {
				assert.Error(t, err)
				if tt.wantStatus != 0 {
					assert.Equal(t, tt.wantStatus, resp.StatusCode)
				}
				if tt.wantErrMsg != "" {
					assert.Contains(t, err.Error(), tt.wantErrMsg)
				}
			} else {
				assert.NoError(t, err)
				assert.NotNil(t, resp)
				assert.Equal(t, tt.wantStatus, resp.StatusCode)
			}
		})
	}
}

func TestDeleteGroupHook(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1/hooks/1", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodDelete)
	})

	_, err := client.Groups.DeleteGroupHook(1, 1)
	if err != nil {
		t.Error(err)
	}
}

func TestGetGroupWebhookHeader(t *testing.T) {
	mux, client := setup(t)

	// Removed most of the arguments to keep test slim
	mux.HandleFunc("/api/v4/groups/1/hooks/1", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprint(w, `{
			"id": 1,
			"custom_webhook_template": "{\"event\":\"{{object_kind}}\"}",
			"custom_headers": [
			  {
				"key": "Authorization"
			  },
			  {
				"key": "OtherKey"
			  }
			]
		  }`)
	})

	hook, _, err := client.Groups.GetGroupHook(1, 1)
	if err != nil {
		t.Errorf("Projects.GetGroupHook returned error: %v", err)
	}

	want := &GroupHook{
		ID:                    1,
		CustomWebhookTemplate: "{\"event\":\"{{object_kind}}\"}",
		CustomHeaders: []*HookCustomHeader{
			{
				Key: "Authorization",
			},
			{
				Key: "OtherKey",
			},
		},
	}

	if !reflect.DeepEqual(want, hook) {
		t.Errorf("Projects.GetGroupHook returned %+v, want %+v", hook, want)
	}
}

func TestSetGroupWebhookHeader(t *testing.T) {
	mux, client := setup(t)
	var bodyJson map[string]interface{}

	// Removed most of the arguments to keep test slim
	mux.HandleFunc("/api/v4/groups/1/hooks/1/custom_headers/Authorization", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPut)
		w.WriteHeader(http.StatusNoContent)

		// validate that the `value` body is sent properly
		body, err := io.ReadAll(r.Body)
		if err != nil {
			t.Fatalf("Unable to read body properly. Error: %v", err)
		}

		// Unmarshal the body into JSON so we can check it
		_ = json.Unmarshal(body, &bodyJson)

		fmt.Fprint(w, ``)
	})

	req, err := client.Groups.SetGroupCustomHeader(1, 1, "Authorization", &SetHookCustomHeaderOptions{Value: Ptr("testValue")})
	if err != nil {
		t.Errorf("Groups.SetGroupCustomHeader returned error: %v", err)
	}

	assert.Equal(t, bodyJson["value"], "testValue")
	assert.Equal(t, http.StatusNoContent, req.StatusCode)
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/group_import_export.go000066400000000000000000000107551475761473200255450ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"bytes"
	"fmt"
	"io"
	"mime/multipart"
	"net/http"
	"os"
	"path/filepath"
	"strconv"
)

// GroupImportExportService handles communication with the group import export
// related methods of the GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/group_import_export.html
type GroupImportExportService struct {
	client *Client
}

// ScheduleExport starts a new group export.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_import_export.html#schedule-new-export
func (s *GroupImportExportService) ScheduleExport(gid interface{}, options ...RequestOptionFunc) (*Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("groups/%s/export", PathEscape(group))

	req, err := s.client.NewRequest(http.MethodPost, u, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}

// ExportDownload downloads the finished export.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_import_export.html#export-download
func (s *GroupImportExportService) ExportDownload(gid interface{}, options ...RequestOptionFunc) (*bytes.Reader, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/export/download", PathEscape(group))

	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	exportDownload := new(bytes.Buffer)
	resp, err := s.client.Do(req, exportDownload)
	if err != nil {
		return nil, resp, err
	}

	return bytes.NewReader(exportDownload.Bytes()), resp, err
}

// GroupImportFileOptions represents the available ImportFile() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_import_export.html#import-a-file
type GroupImportFileOptions struct {
	Name     *string `url:"name,omitempty" json:"name,omitempty"`
	Path     *string `url:"path,omitempty" json:"path,omitempty"`
	File     *string `url:"file,omitempty" json:"file,omitempty"`
	ParentID *int    `url:"parent_id,omitempty" json:"parent_id,omitempty"`
}

// ImportFile imports a file.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_import_export.html#import-a-file
func (s *GroupImportExportService) ImportFile(opt *GroupImportFileOptions, options ...RequestOptionFunc) (*Response, error) {
	// First check if we got all required options.
	if opt.Name == nil || *opt.Name == "" {
		return nil, fmt.Errorf("Missing required option: Name")
	}
	if opt.Path == nil || *opt.Path == "" {
		return nil, fmt.Errorf("Missing required option: Path")
	}
	if opt.File == nil || *opt.File == "" {
		return nil, fmt.Errorf("Missing required option: File")
	}

	f, err := os.Open(*opt.File)
	if err != nil {
		return nil, err
	}
	defer f.Close()

	b := &bytes.Buffer{}
	w := multipart.NewWriter(b)

	_, filename := filepath.Split(*opt.File)
	fw, err := w.CreateFormFile("file", filename)
	if err != nil {
		return nil, err
	}

	_, err = io.Copy(fw, f)
	if err != nil {
		return nil, err
	}

	// Populate the additional fields.
	fw, err = w.CreateFormField("name")
	if err != nil {
		return nil, err
	}

	_, err = fw.Write([]byte(*opt.Name))
	if err != nil {
		return nil, err
	}

	fw, err = w.CreateFormField("path")
	if err != nil {
		return nil, err
	}

	_, err = fw.Write([]byte(*opt.Path))
	if err != nil {
		return nil, err
	}

	if opt.ParentID != nil {
		fw, err = w.CreateFormField("parent_id")
		if err != nil {
			return nil, err
		}

		_, err = fw.Write([]byte(strconv.Itoa(*opt.ParentID)))
		if err != nil {
			return nil, err
		}
	}

	if err = w.Close(); err != nil {
		return nil, err
	}

	req, err := s.client.NewRequest(http.MethodPost, "groups/import", nil, options)
	if err != nil {
		return nil, err
	}

	// Set the buffer as the request body.
	if err = req.SetBody(b); err != nil {
		return nil, err
	}

	// Overwrite the default content type.
	req.Header.Set("Content-Type", w.FormDataContentType())

	return s.client.Do(req, nil)
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/group_import_export_test.go000066400000000000000000000037771475761473200266120ustar00rootroot00000000000000package gitlab

import (
	"fmt"
	"io"
	"net/http"
	"os"
	"reflect"
	"testing"
)

func TestGroupScheduleExport(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1/export",
		func(w http.ResponseWriter, r *http.Request) {
			testMethod(t, r, http.MethodPost)
			fmt.Fprint(w, `{"message": "202 Accepted"}`)
		})

	_, err := client.GroupImportExport.ScheduleExport(1)
	if err != nil {
		t.Errorf("GroupImportExport.ScheduleExport returned error: %v", err)
	}
}

func TestGroupExportDownload(t *testing.T) {
	mux, client := setup(t)
	content := []byte("fake content")

	mux.HandleFunc("/api/v4/groups/1/export/download",
		func(w http.ResponseWriter, r *http.Request) {
			testMethod(t, r, http.MethodGet)
			w.Write(content)
		})

	export, _, err := client.GroupImportExport.ExportDownload(1)
	if err != nil {
		t.Errorf("GroupImportExport.ExportDownload returned error: %v", err)
	}

	data, err := io.ReadAll(export)
	if err != nil {
		t.Errorf("Error reading export: %v", err)
	}

	want := []byte("fake content")
	if !reflect.DeepEqual(want, data) {
		t.Errorf("GroupImportExport.GroupExportDownload returned %+v, want %+v", data, want)
	}
}

func TestGroupImport(t *testing.T) {
	mux, client := setup(t)

	content := []byte("temporary file's content")
	tmpfile, err := os.CreateTemp(os.TempDir(), "example.*.tar.gz")
	if err != nil {
		tmpfile.Close()
		t.Fatal(err)
	}
	if _, err := tmpfile.Write(content); err != nil {
		tmpfile.Close()
		t.Fatal(err)
	}
	if err := tmpfile.Close(); err != nil {
		t.Fatal(err)
	}
	defer os.Remove(tmpfile.Name()) // clean up

	mux.HandleFunc("/api/v4/groups/import",
		func(w http.ResponseWriter, r *http.Request) {
			testMethod(t, r, http.MethodPost)
			fmt.Fprint(w, `{"message": "202 Accepted"}`)
		})

	opt := &GroupImportFileOptions{
		Name:     Ptr("test"),
		Path:     Ptr("path"),
		File:     Ptr(tmpfile.Name()),
		ParentID: Ptr(1),
	}

	_, err = client.GroupImportExport.ImportFile(opt)
	if err != nil {
		t.Errorf("GroupImportExport.ImportFile returned error: %v", err)
	}
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/group_iterations.go000066400000000000000000000053571475761473200250150ustar00rootroot00000000000000//
// Copyright 2022, Daniel Steinke
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"fmt"
	"net/http"
	"time"
)

// IterationsAPI handles communication with the iterations related methods
// of the GitLab API
//
// GitLab API docs: https://docs.gitlab.com/ee/api/group_iterations.html
type GroupIterationsService struct {
	client *Client
}

// GroupInteration represents a GitLab iteration.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/group_iterations.html
type GroupIteration struct {
	ID          int        `json:"id"`
	IID         int        `json:"iid"`
	Sequence    int        `json:"sequence"`
	GroupID     int        `json:"group_id"`
	Title       string     `json:"title"`
	Description string     `json:"description"`
	State       int        `json:"state"`
	CreatedAt   *time.Time `json:"created_at"`
	UpdatedAt   *time.Time `json:"updated_at"`
	DueDate     *ISOTime   `json:"due_date"`
	StartDate   *ISOTime   `json:"start_date"`
	WebURL      string     `json:"web_url"`
}

func (i GroupIteration) String() string {
	return Stringify(i)
}

// ListGroupIterationsOptions contains the available ListGroupIterations()
// options
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_iterations.html#list-group-iterations
type ListGroupIterationsOptions struct {
	ListOptions
	State            *string `url:"state,omitempty" json:"state,omitempty"`
	Search           *string `url:"search,omitempty" json:"search,omitempty"`
	IncludeAncestors *bool   `url:"include_ancestors,omitempty" json:"include_ancestors,omitempty"`
}

// ListGroupIterations returns a list of group iterations.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_iterations.html#list-group-iterations
func (s *GroupIterationsService) ListGroupIterations(gid interface{}, opt *ListGroupIterationsOptions, options ...RequestOptionFunc) ([]*GroupIteration, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/iterations", PathEscape(group))

	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var gis []*GroupIteration
	resp, err := s.client.Do(req, &gis)
	if err != nil {
		return nil, nil, err
	}

	return gis, resp, nil
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/group_iterations_test.go000066400000000000000000000022661475761473200260500ustar00rootroot00000000000000package gitlab

import (
	"fmt"
	"net/http"
	"reflect"
	"testing"
)

func TestListGroupIterations(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/5/iterations",
		func(w http.ResponseWriter, r *http.Request) {
			testMethod(t, r, http.MethodGet)
			fmt.Fprintf(w, `[
        {
          "id": 53,
          "iid": 13,
          "sequence": 1,
          "group_id": 5,
          "title": "Iteration II",
          "description": "Ipsum Lorem ipsum",
          "state": 2,
          "web_url": "http://gitlab.example.com/groups/my-group/-/iterations/13"
        }
      ]`)
		})

	iterations, _, err := client.GroupIterations.ListGroupIterations(5, &ListGroupIterationsOptions{})
	if err != nil {
		t.Errorf("GroupIterations.ListGroupIterations returned error: %v", err)
	}

	want := []*GroupIteration{{
		ID:          53,
		IID:         13,
		Sequence:    1,
		GroupID:     5,
		Title:       "Iteration II",
		Description: "Ipsum Lorem ipsum",
		State:       2,
		WebURL:      "http://gitlab.example.com/groups/my-group/-/iterations/13",
	}}
	if !reflect.DeepEqual(want, iterations) {
		t.Errorf("GroupIterations.ListGroupIterations returned %+v, want %+v", iterations, want)
	}
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/group_labels.go000066400000000000000000000210141475761473200240620ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"fmt"
	"net/http"
)

// GroupLabelsService handles communication with the label related methods of the
// GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/group_labels.html
type GroupLabelsService struct {
	client *Client
}

// GroupLabel represents a GitLab group label.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/group_labels.html
type GroupLabel Label

func (l GroupLabel) String() string {
	return Stringify(l)
}

// ListGroupLabelsOptions represents the available ListGroupLabels() options.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/group_labels.html#list-group-labels
type ListGroupLabelsOptions struct {
	ListOptions
	WithCounts               *bool   `url:"with_counts,omitempty" json:"with_counts,omitempty"`
	IncludeAncestorGroups    *bool   `url:"include_ancestor_groups,omitempty" json:"include_ancestor_groups,omitempty"`
	IncludeDescendantGrouops *bool   `url:"include_descendant_groups,omitempty" json:"include_descendant_groups,omitempty"`
	OnlyGroupLabels          *bool   `url:"only_group_labels,omitempty" json:"only_group_labels,omitempty"`
	Search                   *string `url:"search,omitempty" json:"search,omitempty"`
}

// ListGroupLabels gets all labels for given group.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_labels.html#list-group-labels
func (s *GroupLabelsService) ListGroupLabels(gid interface{}, opt *ListGroupLabelsOptions, options ...RequestOptionFunc) ([]*GroupLabel, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/labels", PathEscape(group))

	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var l []*GroupLabel
	resp, err := s.client.Do(req, &l)
	if err != nil {
		return nil, resp, err
	}

	return l, resp, nil
}

// GetGroupLabel get a single label for a given group.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_labels.html#get-a-single-group-label
func (s *GroupLabelsService) GetGroupLabel(gid interface{}, lid interface{}, options ...RequestOptionFunc) (*GroupLabel, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	label, err := parseID(lid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/labels/%s", PathEscape(group), PathEscape(label))

	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	var l *GroupLabel
	resp, err := s.client.Do(req, &l)
	if err != nil {
		return nil, resp, err
	}

	return l, resp, nil
}

// CreateGroupLabelOptions represents the available CreateGroupLabel() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_labels.html#create-a-new-group-label
type CreateGroupLabelOptions struct {
	Name        *string `url:"name,omitempty" json:"name,omitempty"`
	Color       *string `url:"color,omitempty" json:"color,omitempty"`
	Description *string `url:"description,omitempty" json:"description,omitempty"`
	Priority    *int    `url:"priority,omitempty" json:"priority,omitempty"`
}

// CreateGroupLabel creates a new label for given group with given name and
// color.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_labels.html#create-a-new-group-label
func (s *GroupLabelsService) CreateGroupLabel(gid interface{}, opt *CreateGroupLabelOptions, options ...RequestOptionFunc) (*GroupLabel, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/labels", PathEscape(group))

	req, err := s.client.NewRequest(http.MethodPost, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	l := new(GroupLabel)
	resp, err := s.client.Do(req, l)
	if err != nil {
		return nil, resp, err
	}

	return l, resp, nil
}

// DeleteGroupLabelOptions represents the available DeleteGroupLabel() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_labels.html#delete-a-group-label
type DeleteGroupLabelOptions struct {
	Name *string `url:"name,omitempty" json:"name,omitempty"`
}

// DeleteGroupLabel deletes a group label given by its name or ID.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_labels.html#delete-a-group-label
func (s *GroupLabelsService) DeleteGroupLabel(gid interface{}, lid interface{}, opt *DeleteGroupLabelOptions, options ...RequestOptionFunc) (*Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("groups/%s/labels", PathEscape(group))

	if lid != nil {
		label, err := parseID(lid)
		if err != nil {
			return nil, err
		}
		u = fmt.Sprintf("groups/%s/labels/%s", PathEscape(group), PathEscape(label))
	}

	req, err := s.client.NewRequest(http.MethodDelete, u, opt, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}

// UpdateGroupLabelOptions represents the available UpdateGroupLabel() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_labels.html#update-a-group-label
type UpdateGroupLabelOptions struct {
	Name        *string `url:"name,omitempty" json:"name,omitempty"`
	NewName     *string `url:"new_name,omitempty" json:"new_name,omitempty"`
	Color       *string `url:"color,omitempty" json:"color,omitempty"`
	Description *string `url:"description,omitempty" json:"description,omitempty"`
	Priority    *int    `url:"priority,omitempty" json:"priority,omitempty"`
}

// UpdateGroupLabel updates an existing label with new name or now color. At least
// one parameter is required, to update the label.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_labels.html#update-a-group-label
func (s *GroupLabelsService) UpdateGroupLabel(gid interface{}, lid interface{}, opt *UpdateGroupLabelOptions, options ...RequestOptionFunc) (*GroupLabel, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/labels", PathEscape(group))

	if lid != nil {
		label, err := parseID(lid)
		if err != nil {
			return nil, nil, err
		}
		u = fmt.Sprintf("groups/%s/labels/%s", PathEscape(group), PathEscape(label))
	}

	req, err := s.client.NewRequest(http.MethodPut, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	l := new(GroupLabel)
	resp, err := s.client.Do(req, l)
	if err != nil {
		return nil, resp, err
	}

	return l, resp, nil
}

// SubscribeToGroupLabel subscribes the authenticated user to a label to receive
// notifications. If the user is already subscribed to the label, the status
// code 304 is returned.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_labels.html#subscribe-to-a-group-label
func (s *GroupLabelsService) SubscribeToGroupLabel(gid interface{}, lid interface{}, options ...RequestOptionFunc) (*GroupLabel, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	label, err := parseID(lid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/labels/%s/subscribe", PathEscape(group), PathEscape(label))

	req, err := s.client.NewRequest(http.MethodPost, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	l := new(GroupLabel)
	resp, err := s.client.Do(req, l)
	if err != nil {
		return nil, resp, err
	}

	return l, resp, nil
}

// UnsubscribeFromGroupLabel unsubscribes the authenticated user from a label to not
// receive notifications from it. If the user is not subscribed to the label, the
// status code 304 is returned.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_labels.html#unsubscribe-from-a-group-label
func (s *GroupLabelsService) UnsubscribeFromGroupLabel(gid interface{}, lid interface{}, options ...RequestOptionFunc) (*Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, err
	}
	label, err := parseID(lid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("groups/%s/labels/%s/unsubscribe", PathEscape(group), PathEscape(label))

	req, err := s.client.NewRequest(http.MethodPost, u, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/group_labels_test.go000066400000000000000000000133161475761473200251270ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"fmt"
	"net/http"
	"reflect"
	"testing"
)

func TestCreateGroupGroupLabel(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1/labels", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		fmt.Fprint(w, `{"id":1, "name": "MyGroupLabel", "color" : "#11FF22"}`)
	})

	l := &CreateGroupLabelOptions{
		Name:  Ptr("MyGroupLabel"),
		Color: Ptr("#11FF22"),
	}
	label, _, err := client.GroupLabels.CreateGroupLabel("1", l)
	if err != nil {
		t.Fatal(err)
	}
	want := &GroupLabel{ID: 1, Name: "MyGroupLabel", Color: "#11FF22"}
	if !reflect.DeepEqual(want, label) {
		t.Errorf("GroupLabels.CreateGroupLabel returned %+v, want %+v", label, want)
	}
}

func TestDeleteGroupLabelByID(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1/labels/1", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodDelete)
	})

	_, err := client.GroupLabels.DeleteGroupLabel("1", "1", nil)
	if err != nil {
		t.Fatal(err)
	}
}

func TestDeleteGroupLabelByName(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1/labels/MyGroupLabel", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodDelete)
	})

	_, err := client.GroupLabels.DeleteGroupLabel("1", "MyGroupLabel", nil)
	if err != nil {
		t.Fatal(err)
	}
}

func TestUpdateGroupLabel(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1/labels/MyGroupLabel", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPut)
		fmt.Fprint(w, `{"id":1, "name": "NewLabel", "color" : "#11FF23" , "description":"This is updated label"}`)
	})

	l := &UpdateGroupLabelOptions{
		NewName:     Ptr("NewLabel"),
		Color:       Ptr("#11FF23"),
		Description: Ptr("This is updated label"),
	}

	label, resp, err := client.GroupLabels.UpdateGroupLabel("1", "MyGroupLabel", l)

	if resp == nil {
		t.Fatal(err)
	}
	if err != nil {
		t.Fatal(err)
	}

	want := &GroupLabel{ID: 1, Name: "NewLabel", Color: "#11FF23", Description: "This is updated label"}

	if !reflect.DeepEqual(want, label) {
		t.Errorf("GroupLabels.UpdateGroupLabel returned %+v, want %+v", label, want)
	}
}

func TestSubscribeToGroupLabel(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1/labels/5/subscribe", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		fmt.Fprint(w, `{  "id" : 5, "name" : "kind/bug", "color" : "#d9534f", "description": "Bug reported by user", "open_issues_count": 1, "closed_issues_count": 0, "open_merge_requests_count": 1, "subscribed": true,"priority": null}`)
	})

	label, _, err := client.GroupLabels.SubscribeToGroupLabel("1", "5")
	if err != nil {
		t.Fatal(err)
	}
	want := &GroupLabel{ID: 5, Name: "kind/bug", Color: "#d9534f", Description: "Bug reported by user", OpenIssuesCount: 1, ClosedIssuesCount: 0, OpenMergeRequestsCount: 1, Subscribed: true}
	if !reflect.DeepEqual(want, label) {
		t.Errorf("GroupLabels.SubscribeToGroupLabel returned %+v, want %+v", label, want)
	}
}

func TestUnsubscribeFromGroupLabel(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1/labels/5/unsubscribe", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
	})

	_, err := client.GroupLabels.UnsubscribeFromGroupLabel("1", "5")
	if err != nil {
		t.Fatal(err)
	}
}

func TestListGroupLabels(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1/labels", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprint(w, `[{  "id" : 5, "name" : "kind/bug", "color" : "#d9534f", "description": "Bug reported by user", "open_issues_count": 1, "closed_issues_count": 0, "open_merge_requests_count": 1, "subscribed": true,"priority": null}]`)
	})

	o := &ListGroupLabelsOptions{
		ListOptions: ListOptions{
			Page:    1,
			PerPage: 10,
		},
	}
	label, _, err := client.GroupLabels.ListGroupLabels("1", o)
	if err != nil {
		t.Log(err.Error() == "invalid ID type 1.1, the ID must be an int or a string")
	}
	want := []*GroupLabel{{ID: 5, Name: "kind/bug", Color: "#d9534f", Description: "Bug reported by user", OpenIssuesCount: 1, ClosedIssuesCount: 0, OpenMergeRequestsCount: 1, Subscribed: true}}
	if !reflect.DeepEqual(want, label) {
		t.Errorf("GroupLabels.ListGroupLabels returned %+v, want %+v", label, want)
	}
}

func TestGetGroupLabel(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1/labels/5", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprint(w, `{  "id" : 5, "name" : "kind/bug", "color" : "#d9534f", "description": "Bug reported by user", "open_issues_count": 1, "closed_issues_count": 0, "open_merge_requests_count": 1, "subscribed": true,"priority": null}`)
	})

	label, _, err := client.GroupLabels.GetGroupLabel("1", 5)
	if err != nil {
		t.Log(err)
	}

	want := &GroupLabel{ID: 5, Name: "kind/bug", Color: "#d9534f", Description: "Bug reported by user", OpenIssuesCount: 1, ClosedIssuesCount: 0, OpenMergeRequestsCount: 1, Subscribed: true}
	if !reflect.DeepEqual(want, label) {
		t.Errorf("GroupLabels.GetGroupLabel returned %+v, want %+v", label, want)
	}
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/group_members.go000066400000000000000000000350371475761473200242640ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"fmt"
	"net/http"
	"time"
)

// GroupMembersService handles communication with the group members
// related methods of the GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/members.html
type GroupMembersService struct {
	client *Client
}

// GroupMember represents a GitLab group member.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/members.html
type GroupMember struct {
	ID                int                      `json:"id"`
	Username          string                   `json:"username"`
	Name              string                   `json:"name"`
	State             string                   `json:"state"`
	AvatarURL         string                   `json:"avatar_url"`
	WebURL            string                   `json:"web_url"`
	CreatedAt         *time.Time               `json:"created_at"`
	ExpiresAt         *ISOTime                 `json:"expires_at"`
	AccessLevel       AccessLevelValue         `json:"access_level"`
	Email             string                   `json:"email,omitempty"`
	GroupSAMLIdentity *GroupMemberSAMLIdentity `json:"group_saml_identity"`
	MemberRole        *MemberRole              `json:"member_role"`
}

// GroupMemberSAMLIdentity represents the SAML Identity link for the group member.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/members.html#list-all-members-of-a-group-or-project
type GroupMemberSAMLIdentity struct {
	ExternUID      string `json:"extern_uid"`
	Provider       string `json:"provider"`
	SAMLProviderID int    `json:"saml_provider_id"`
}

// BillableGroupMember represents a GitLab billable group member.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/members.html#list-all-billable-members-of-a-group
type BillableGroupMember struct {
	ID             int        `json:"id"`
	Username       string     `json:"username"`
	Name           string     `json:"name"`
	State          string     `json:"state"`
	AvatarURL      string     `json:"avatar_url"`
	WebURL         string     `json:"web_url"`
	Email          string     `json:"email"`
	LastActivityOn *ISOTime   `json:"last_activity_on"`
	MembershipType string     `json:"membership_type"`
	Removable      bool       `json:"removable"`
	CreatedAt      *time.Time `json:"created_at"`
	IsLastOwner    bool       `json:"is_last_owner"`
	LastLoginAt    *time.Time `json:"last_login_at"`
}

// BillableUserMembership represents a Membership of a billable user of a group
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/members.html#list-memberships-for-a-billable-member-of-a-group
type BillableUserMembership struct {
	ID               int                 `json:"id"`
	SourceID         int                 `json:"source_id"`
	SourceFullName   string              `json:"source_full_name"`
	SourceMembersURL string              `json:"source_members_url"`
	CreatedAt        *time.Time          `json:"created_at"`
	ExpiresAt        *time.Time          `json:"expires_at"`
	AccessLevel      *AccessLevelDetails `json:"access_level"`
}

// ListGroupMembersOptions represents the available ListGroupMembers() and
// ListAllGroupMembers() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/members.html#list-all-members-of-a-group-or-project
type ListGroupMembersOptions struct {
	ListOptions
	Query   *string `url:"query,omitempty" json:"query,omitempty"`
	UserIDs *[]int  `url:"user_ids[],omitempty" json:"user_ids,omitempty"`
}

// ListGroupMembers get a list of group members viewable by the authenticated
// user. Inherited members through ancestor groups are not included.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/members.html#list-all-members-of-a-group-or-project
func (s *GroupsService) ListGroupMembers(gid interface{}, opt *ListGroupMembersOptions, options ...RequestOptionFunc) ([]*GroupMember, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/members", PathEscape(group))

	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var gm []*GroupMember
	resp, err := s.client.Do(req, &gm)
	if err != nil {
		return nil, resp, err
	}

	return gm, resp, nil
}

// ListAllGroupMembers get a list of group members viewable by the authenticated
// user. Returns a list including inherited members through ancestor groups.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/members.html#list-all-members-of-a-group-or-project-including-inherited-and-invited-members
func (s *GroupsService) ListAllGroupMembers(gid interface{}, opt *ListGroupMembersOptions, options ...RequestOptionFunc) ([]*GroupMember, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/members/all", PathEscape(group))

	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var gm []*GroupMember
	resp, err := s.client.Do(req, &gm)
	if err != nil {
		return nil, resp, err
	}

	return gm, resp, nil
}

// AddGroupMemberOptions represents the available AddGroupMember() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/members.html#add-a-member-to-a-group-or-project
type AddGroupMemberOptions struct {
	UserID       *int              `url:"user_id,omitempty" json:"user_id,omitempty"`
	Username     *string           `url:"username,omitempty" json:"username,omitempty"`
	AccessLevel  *AccessLevelValue `url:"access_level,omitempty" json:"access_level,omitempty"`
	ExpiresAt    *string           `url:"expires_at,omitempty" json:"expires_at"`
	MemberRoleID *int              `url:"member_role_id,omitempty" json:"member_role_id,omitempty"`
}

// GetGroupMember gets a member of a group.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/members.html#get-a-member-of-a-group-or-project
func (s *GroupMembersService) GetGroupMember(gid interface{}, user int, options ...RequestOptionFunc) (*GroupMember, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/members/%d", PathEscape(group), user)

	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	gm := new(GroupMember)
	resp, err := s.client.Do(req, gm)
	if err != nil {
		return nil, resp, err
	}

	return gm, resp, nil
}

// GetInheritedGroupMember get a member of a group or project, including
// inherited and invited members
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/members.html#get-a-member-of-a-group-or-project-including-inherited-and-invited-members
func (s *GroupMembersService) GetInheritedGroupMember(gid interface{}, user int, options ...RequestOptionFunc) (*GroupMember, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/members/all/%d", PathEscape(group), user)

	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	gm := new(GroupMember)
	resp, err := s.client.Do(req, gm)
	if err != nil {
		return nil, resp, err
	}

	return gm, resp, err
}

// ListBillableGroupMembersOptions represents the available
// ListBillableGroupMembers() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/members.html#list-all-billable-members-of-a-group
type ListBillableGroupMembersOptions struct {
	ListOptions
	Search *string `url:"search,omitempty" json:"search,omitempty"`
	Sort   *string `url:"sort,omitempty" json:"sort,omitempty"`
}

// ListBillableGroupMembers Gets a list of group members that count as billable.
// The list includes members in the subgroup or subproject.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/members.html#list-all-billable-members-of-a-group
func (s *GroupsService) ListBillableGroupMembers(gid interface{}, opt *ListBillableGroupMembersOptions, options ...RequestOptionFunc) ([]*BillableGroupMember, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/billable_members", PathEscape(group))

	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var bgm []*BillableGroupMember
	resp, err := s.client.Do(req, &bgm)
	if err != nil {
		return nil, resp, err
	}

	return bgm, resp, nil
}

// ListMembershipsForBillableGroupMemberOptions represents the available
// ListMembershipsForBillableGroupMember() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/members.html#list-memberships-for-a-billable-member-of-a-group
type ListMembershipsForBillableGroupMemberOptions = ListOptions

// ListMembershipsForBillableGroupMember gets a list of memberships for a
// billable member of a group.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/members.html#list-memberships-for-a-billable-member-of-a-group
func (s *GroupsService) ListMembershipsForBillableGroupMember(gid interface{}, user int, opt *ListMembershipsForBillableGroupMemberOptions, options ...RequestOptionFunc) ([]*BillableUserMembership, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/billable_members/%d/memberships", PathEscape(group), user)

	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var bum []*BillableUserMembership
	resp, err := s.client.Do(req, &bum)
	if err != nil {
		return nil, resp, err
	}

	return bum, resp, nil
}

// RemoveBillableGroupMember removes a given group members that count as billable.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/members.html#remove-a-billable-member-from-a-group
func (s *GroupsService) RemoveBillableGroupMember(gid interface{}, user int, options ...RequestOptionFunc) (*Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("groups/%s/billable_members/%d", PathEscape(group), user)

	req, err := s.client.NewRequest(http.MethodDelete, u, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}

// AddGroupMember adds a user to the list of group members.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/members.html#add-a-member-to-a-group-or-project
func (s *GroupMembersService) AddGroupMember(gid interface{}, opt *AddGroupMemberOptions, options ...RequestOptionFunc) (*GroupMember, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/members", PathEscape(group))

	req, err := s.client.NewRequest(http.MethodPost, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	gm := new(GroupMember)
	resp, err := s.client.Do(req, gm)
	if err != nil {
		return nil, resp, err
	}

	return gm, resp, nil
}

// ShareWithGroup shares a group with the group.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/groups.html#share-groups-with-groups
func (s *GroupMembersService) ShareWithGroup(gid interface{}, opt *ShareWithGroupOptions, options ...RequestOptionFunc) (*Group, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/share", PathEscape(group))

	req, err := s.client.NewRequest(http.MethodPost, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	g := new(Group)
	resp, err := s.client.Do(req, g)
	if err != nil {
		return nil, resp, err
	}

	return g, resp, nil
}

// DeleteShareWithGroup allows to unshare a group from a group.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/groups.html#delete-link-sharing-group-with-another-group
func (s *GroupMembersService) DeleteShareWithGroup(gid interface{}, groupID int, options ...RequestOptionFunc) (*Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("groups/%s/share/%d", PathEscape(group), groupID)

	req, err := s.client.NewRequest(http.MethodDelete, u, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}

// EditGroupMemberOptions represents the available EditGroupMember()
// options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/members.html#edit-a-member-of-a-group-or-project
type EditGroupMemberOptions struct {
	AccessLevel  *AccessLevelValue `url:"access_level,omitempty" json:"access_level,omitempty"`
	ExpiresAt    *string           `url:"expires_at,omitempty" json:"expires_at,omitempty"`
	MemberRoleID *int              `url:"member_role_id,omitempty" json:"member_role_id,omitempty"`
}

// EditGroupMember updates a member of a group.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/members.html#edit-a-member-of-a-group-or-project
func (s *GroupMembersService) EditGroupMember(gid interface{}, user int, opt *EditGroupMemberOptions, options ...RequestOptionFunc) (*GroupMember, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/members/%d", PathEscape(group), user)

	req, err := s.client.NewRequest(http.MethodPut, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	gm := new(GroupMember)
	resp, err := s.client.Do(req, gm)
	if err != nil {
		return nil, resp, err
	}

	return gm, resp, nil
}

// RemoveGroupMemberOptions represents the available options to remove a group member.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/members.html#remove-a-member-from-a-group-or-project
type RemoveGroupMemberOptions struct {
	SkipSubresources  *bool `url:"skip_subresources,omitempty" json:"skip_subresources,omitempty"`
	UnassignIssuables *bool `url:"unassign_issuables,omitempty" json:"unassign_issuables,omitempty"`
}

// RemoveGroupMember removes user from user team.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/members.html#remove-a-member-from-a-group-or-project
func (s *GroupMembersService) RemoveGroupMember(gid interface{}, user int, opt *RemoveGroupMemberOptions, options ...RequestOptionFunc) (*Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("groups/%s/members/%d", PathEscape(group), user)

	req, err := s.client.NewRequest(http.MethodDelete, u, opt, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/group_members_test.go000066400000000000000000000323661475761473200253250ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"fmt"
	"net/http"
	"reflect"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestListBillableGroupMembers(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1/billable_members",
		func(w http.ResponseWriter, r *http.Request) {
			testMethod(t, r, http.MethodGet)
			fmt.Fprint(w,
				`[
					{
						"id":1,
						"username":"ray",
						"name":"Raymond",
						"state":"active",
						"avatar_url":"https://foo.bar/mypic",
						"web_url":"http://192.168.1.8:3000/root",
						"last_activity_on":"2021-01-27",
						"membership_type": "group_member",
						"removable": true,
						"created_at": "2017-10-23T11:41:28.793Z",
						"is_last_owner": false,
						"last_login_at": "2022-12-12T09:22:51.581Z"
					}
				]`)
		})

	billableMembers, _, err := client.Groups.ListBillableGroupMembers(1, &ListBillableGroupMembersOptions{})
	if err != nil {
		t.Errorf("Groups.ListBillableGroupMembers returned error: %v", err)
	}

	createdAt, _ := time.Parse(time.RFC3339, "2017-10-23T11:41:28.793Z")
	lastLoginAt, _ := time.Parse(time.RFC3339, "2022-12-12T09:22:51.581Z")
	lastActivityOn, _ := time.Parse(time.RFC3339, "2021-01-27T00:00:00Z")
	lastActivityOnISOTime := ISOTime(lastActivityOn)

	want := []*BillableGroupMember{
		{
			ID:             1,
			Username:       "ray",
			Name:           "Raymond",
			State:          "active",
			AvatarURL:      "https://foo.bar/mypic",
			WebURL:         "http://192.168.1.8:3000/root",
			LastActivityOn: &lastActivityOnISOTime,
			MembershipType: "group_member",
			Removable:      true,
			CreatedAt:      &createdAt,
			IsLastOwner:    false,
			LastLoginAt:    &lastLoginAt,
		},
	}
	assert.Equal(t, want, billableMembers, "Expected returned Groups.ListBillableGroupMembers to equal")
}

func TestListMembershipsForBillableGroupMember(t *testing.T) {
	mux, client := setup(t)
	mux.HandleFunc("/api/v4/groups/1/billable_members/42/memberships",
		func(w http.ResponseWriter, r *http.Request) {
			testMethod(t, r, http.MethodGet)
			fmt.Fprint(w,
				`[
					{
						"id":21,
						"source_id":36,
						"source_full_name":"Root Group / Test Group",
						"source_members_url":"https://gitlab.example.com/groups/root-group/test-group/-/group_members",
						"created_at":"2021-03-31T17:28:44.812Z",
						"access_level": {
							"string_value": "Developer",
							"integer_value": 30
						}
					}
				]`)
		})

	memberships, _, err := client.Groups.ListMembershipsForBillableGroupMember(1, 42, &ListMembershipsForBillableGroupMemberOptions{})
	if err != nil {
		t.Errorf("Groups.ListMembershipsForBillableGroupMember returned error: %v", err)
	}

	createdAt, _ := time.Parse(time.RFC3339, "2021-03-31T17:28:44.812Z")

	want := []*BillableUserMembership{
		{
			ID:               21,
			SourceID:         36,
			SourceFullName:   "Root Group / Test Group",
			SourceMembersURL: "https://gitlab.example.com/groups/root-group/test-group/-/group_members",
			CreatedAt:        &createdAt,
			AccessLevel: &AccessLevelDetails{
				IntegerValue: 30,
				StringValue:  "Developer",
			},
		},
	}
	assert.Equal(t, want, memberships, "Expected returned Groups.ListMembershipsForBillableGroupMember to equal")
}

func TestListGroupMembersWithoutEmail(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1/members",
		func(w http.ResponseWriter, r *http.Request) {
			testMethod(t, r, http.MethodGet)
			fmt.Fprint(w,
				`[
					{
						"id": 1,
						"username": "raymond_smith",
						"name": "Raymond Smith",
						"state": "active",
						"avatar_url": "https://www.gravatar.com/avatar/c2525a7f58ae3776070e44c106c48e15?s=80&d=identicon",
						"web_url": "http://192.168.1.8:3000/root",
						"created_at": "2012-10-21T14:13:35Z",
						"expires_at": "2012-10-22",
						"access_level": 30,
						"group_saml_identity": null
					}
				]`)
		})

	members, _, err := client.Groups.ListGroupMembers(1, &ListGroupMembersOptions{})
	if err != nil {
		t.Errorf("Groups.ListGroupMembers returned error: %v", err)
	}

	createdAt, _ := time.Parse(time.RFC3339, "2012-10-21T14:13:35Z")
	expiresAt, _ := time.Parse(time.RFC3339, "2012-10-22T00:00:00Z")
	expiresAtISOTime := ISOTime(expiresAt)
	want := []*GroupMember{
		{
			ID:          1,
			Username:    "raymond_smith",
			Name:        "Raymond Smith",
			State:       "active",
			AvatarURL:   "https://www.gravatar.com/avatar/c2525a7f58ae3776070e44c106c48e15?s=80&d=identicon",
			WebURL:      "http://192.168.1.8:3000/root",
			CreatedAt:   &createdAt,
			ExpiresAt:   &expiresAtISOTime,
			AccessLevel: 30,
		},
	}
	if !reflect.DeepEqual(want, members) {
		t.Errorf("Groups.ListBillableGroupMembers returned %+v, want %+v", members[0], want[0])
	}
}

func TestListGroupMembersWithEmail(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1/members",
		func(w http.ResponseWriter, r *http.Request) {
			testMethod(t, r, http.MethodGet)
			fmt.Fprint(w,
				`[
					{
						"id": 1,
						"username": "raymond_smith",
						"name": "Raymond Smith",
						"state": "active",
						"avatar_url": "https://www.gravatar.com/avatar/c2525a7f58ae3776070e44c106c48e15?s=80&d=identicon",
						"web_url": "http://192.168.1.8:3000/root",
						"created_at": "2012-10-21T14:13:35Z",
						"expires_at": "2012-10-22",
						"access_level": 30,
						"email": "john@example.com",
						"group_saml_identity": null
					}
				]`)
		})

	members, _, err := client.Groups.ListGroupMembers(1, &ListGroupMembersOptions{})
	if err != nil {
		t.Errorf("Groups.ListGroupMembers returned error: %v", err)
	}

	createdAt, _ := time.Parse(time.RFC3339, "2012-10-21T14:13:35Z")
	expiresAt, _ := time.Parse(time.RFC3339, "2012-10-22T00:00:00Z")
	expiresAtISOTime := ISOTime(expiresAt)
	want := []*GroupMember{
		{
			ID:          1,
			Username:    "raymond_smith",
			Name:        "Raymond Smith",
			State:       "active",
			AvatarURL:   "https://www.gravatar.com/avatar/c2525a7f58ae3776070e44c106c48e15?s=80&d=identicon",
			WebURL:      "http://192.168.1.8:3000/root",
			CreatedAt:   &createdAt,
			ExpiresAt:   &expiresAtISOTime,
			AccessLevel: 30,
			Email:       "john@example.com",
		},
	}
	if !reflect.DeepEqual(want, members) {
		t.Errorf("Groups.ListBillableGroupMembers returned %+v, want %+v", members[0], want[0])
	}
}

func TestListGroupMembersWithoutSAML(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1/members",
		func(w http.ResponseWriter, r *http.Request) {
			testMethod(t, r, http.MethodGet)
			fmt.Fprint(w,
				`[
					{
						"id": 1,
						"username": "raymond_smith",
						"name": "Raymond Smith",
						"state": "active",
						"avatar_url": "https://www.gravatar.com/avatar/c2525a7f58ae3776070e44c106c48e15?s=80&d=identicon",
						"web_url": "http://192.168.1.8:3000/root",
						"created_at": "2012-10-21T14:13:35Z",
						"expires_at": "2012-10-22",
						"access_level": 30,
						"group_saml_identity": null
					}
				]`)
		})

	members, _, err := client.Groups.ListGroupMembers(1, &ListGroupMembersOptions{})
	if err != nil {
		t.Errorf("Groups.ListGroupMembers returned error: %v", err)
	}

	createdAt, _ := time.Parse(time.RFC3339, "2012-10-21T14:13:35Z")
	expiresAt, _ := time.Parse(time.RFC3339, "2012-10-22T00:00:00Z")
	expiresAtISOTime := ISOTime(expiresAt)
	want := []*GroupMember{
		{
			ID:                1,
			Username:          "raymond_smith",
			Name:              "Raymond Smith",
			State:             "active",
			AvatarURL:         "https://www.gravatar.com/avatar/c2525a7f58ae3776070e44c106c48e15?s=80&d=identicon",
			WebURL:            "http://192.168.1.8:3000/root",
			CreatedAt:         &createdAt,
			ExpiresAt:         &expiresAtISOTime,
			AccessLevel:       30,
			GroupSAMLIdentity: nil,
		},
	}
	if !reflect.DeepEqual(want, members) {
		t.Errorf("Groups.ListBillableGroupMembers returned %+v, want %+v", members[0], want[0])
	}
}

func TestListGroupMembersWithSAML(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1/members",
		func(w http.ResponseWriter, r *http.Request) {
			testMethod(t, r, http.MethodGet)
			fmt.Fprint(w,
				`[
					{
						"id": 2,
						"username": "john_doe",
						"name": "John Doe",
						"state": "active",
						"avatar_url": "https://www.gravatar.com/avatar/c2525a7f58ae3776070e44c106c48e15?s=80&d=identicon",
						"web_url": "http://192.168.1.8:3000/root",
						"created_at": "2012-10-21T14:13:35Z",
						"expires_at": "2012-10-22",
						"access_level": 30,
						"group_saml_identity": {
							"extern_uid":"ABC-1234567890",
							"provider": "group_saml",
							"saml_provider_id": 10
						}
					}
				]`)
		})

	members, _, err := client.Groups.ListGroupMembers(1, &ListGroupMembersOptions{})
	if err != nil {
		t.Errorf("Groups.ListGroupMembers returned error: %v", err)
	}

	createdAt, _ := time.Parse(time.RFC3339, "2012-10-21T14:13:35Z")
	expiresAt, _ := time.Parse(time.RFC3339, "2012-10-22T00:00:00Z")
	expiresAtISOTime := ISOTime(expiresAt)
	want := []*GroupMember{
		{
			ID:          2,
			Username:    "john_doe",
			Name:        "John Doe",
			State:       "active",
			AvatarURL:   "https://www.gravatar.com/avatar/c2525a7f58ae3776070e44c106c48e15?s=80&d=identicon",
			WebURL:      "http://192.168.1.8:3000/root",
			CreatedAt:   &createdAt,
			ExpiresAt:   &expiresAtISOTime,
			AccessLevel: 30,
			GroupSAMLIdentity: &GroupMemberSAMLIdentity{
				ExternUID:      "ABC-1234567890",
				Provider:       "group_saml",
				SAMLProviderID: 10,
			},
		},
	}
	if !reflect.DeepEqual(want, members) {
		t.Errorf("Groups.ListBillableGroupMembers returned %+v, want %+v", members[0], want[0])
	}
}

func TestGetGroupMemberCustomRole(t *testing.T) {
	mux, client := setup(t)

	path := fmt.Sprintf("/%sgroups/1/members/2", apiVersionPath)
	mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)

		// This is pulled straight from a `/group//members/` call, then obfuscated.
		fmt.Fprint(w, `
		{
			"id":1,
			"username":"test",
			"name":"testName",
			"access_level":30,
			"member_role":{
				"id":1,
				"group_id":2,
				"name":"TestingCustomRole",
				"description":"",
				"base_access_level":30,
				"admin_cicd_variables":true,
				"admin_group_member":null,
				"admin_merge_request":null,
				"admin_push_rules":null,
				"admin_terraform_state":null,
				"admin_vulnerability":null,
				"archive_project":null,
				"manage_group_access_tokens":null,
				"manage_project_access_tokens":null,
				"read_code":null,
				"read_dependency":null,
				"read_vulnerability":null,
				"remove_group":null,
				"remove_project":null
			}
		}
		`)
	})

	want := &GroupMember{
		ID:          1,
		Username:    "test",
		Name:        "testName",
		AccessLevel: AccessLevelValue(30),
		MemberRole: &MemberRole{
			ID:                 1,
			GroupID:            2,
			Name:               "TestingCustomRole",
			Description:        "",
			BaseAccessLevel:    AccessLevelValue(30),
			AdminCICDVariables: true,
		},
	}
	member, _, err := client.GroupMembers.GetGroupMember(1, 2)

	assert.NoError(t, err)
	assert.Equal(t, want, member)
}

func TestGetGroupMemberAll(t *testing.T) {
	mux, client := setup(t)

	path := fmt.Sprintf("/%sgroups/1/members/all/2", apiVersionPath)
	mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)

		fmt.Fprint(w, `
		{
		  "id": 2,
		  "name": "aaa",
		  "username": "aaaName",
		  "state": "active",
		  "avatar_url": "https://secure.gravatar.com/avatar/e547676d82f1e16954b2280a5b4cbe79?s=80&d=identicon",
		  "web_url": "https://gitlab.example.cn/aaa",
		  "access_level": 30,
		  "created_at": "2024-06-19T07:14:02.793Z",
		  "expires_at": null
		}
		`)
	})

	createAt, _ := time.Parse(time.RFC3339, "2024-06-19T07:14:02.793Z")

	want := &GroupMember{
		ID:          2,
		Name:        "aaa",
		Username:    "aaaName",
		State:       "active",
		AvatarURL:   "https://secure.gravatar.com/avatar/e547676d82f1e16954b2280a5b4cbe79?s=80&d=identicon",
		WebURL:      "https://gitlab.example.cn/aaa",
		AccessLevel: AccessLevelValue(30),
		CreatedAt:   &createAt,
	}

	pm, resp, err := client.GroupMembers.GetInheritedGroupMember(1, 2, nil, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, pm)

	member, resp, err := client.GroupMembers.GetInheritedGroupMember(1.01, 2, nil, nil)
	require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, member)

	member, resp, err = client.GroupMembers.GetInheritedGroupMember(1, 1, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, member)

	member, resp, err = client.GroupMembers.GetInheritedGroupMember(2, 1, nil, nil)
	require.Error(t, err)
	require.Nil(t, member)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/group_milestones.go000066400000000000000000000261051475761473200250100ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"fmt"
	"net/http"
	"time"
)

// GroupMilestonesService handles communication with the milestone related
// methods of the GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/group_milestones.html
type GroupMilestonesService struct {
	client *Client
}

// GroupMilestone represents a GitLab milestone.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/group_milestones.html
type GroupMilestone struct {
	ID          int        `json:"id"`
	IID         int        `json:"iid"`
	GroupID     int        `json:"group_id"`
	Title       string     `json:"title"`
	Description string     `json:"description"`
	StartDate   *ISOTime   `json:"start_date"`
	DueDate     *ISOTime   `json:"due_date"`
	State       string     `json:"state"`
	UpdatedAt   *time.Time `json:"updated_at"`
	CreatedAt   *time.Time `json:"created_at"`
	Expired     *bool      `json:"expired"`
}

func (m GroupMilestone) String() string {
	return Stringify(m)
}

// ListGroupMilestonesOptions represents the available
// ListGroupMilestones() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_milestones.html#list-group-milestones
type ListGroupMilestonesOptions struct {
	ListOptions
	IIDs                    *[]int   `url:"iids[],omitempty" json:"iids,omitempty"`
	State                   *string  `url:"state,omitempty" json:"state,omitempty"`
	Title                   *string  `url:"title,omitempty" json:"title,omitempty"`
	Search                  *string  `url:"search,omitempty" json:"search,omitempty"`
	SearchTitle             *string  `url:"search_title,omitempty" json:"search_title,omitempty"`
	IncludeParentMilestones *bool    `url:"include_parent_milestones,omitempty" json:"include_parent_milestones,omitempty"`
	IncludeAncestors        *bool    `url:"include_ancestors,omitempty" json:"include_ancestors,omitempty"`
	IncludeDescendents      *bool    `url:"include_descendents,omitempty" json:"include_descendents,omitempty"`
	UpdatedBefore           *ISOTime `url:"updated_before,omitempty" json:"updated_before,omitempty"`
	UpdatedAfter            *ISOTime `url:"updated_after,omitempty" json:"updated_after,omitempty"`
	ContainingDate          *ISOTime `url:"containing_date,omitempty" json:"containing_date,omitempty"`
	StartDate               *ISOTime `url:"start_date,omitempty" json:"start_date,omitempty"`
	EndDate                 *ISOTime `url:"end_date,omitempty" json:"end_date,omitempty"`
}

// ListGroupMilestones returns a list of group milestones.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_milestones.html#list-group-milestones
func (s *GroupMilestonesService) ListGroupMilestones(gid interface{}, opt *ListGroupMilestonesOptions, options ...RequestOptionFunc) ([]*GroupMilestone, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/milestones", PathEscape(group))

	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var m []*GroupMilestone
	resp, err := s.client.Do(req, &m)
	if err != nil {
		return nil, resp, err
	}

	return m, resp, nil
}

// GetGroupMilestone gets a single group milestone.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_milestones.html#get-single-milestone
func (s *GroupMilestonesService) GetGroupMilestone(gid interface{}, milestone int, options ...RequestOptionFunc) (*GroupMilestone, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/milestones/%d", PathEscape(group), milestone)

	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	m := new(GroupMilestone)
	resp, err := s.client.Do(req, m)
	if err != nil {
		return nil, resp, err
	}

	return m, resp, nil
}

// CreateGroupMilestoneOptions represents the available CreateGroupMilestone() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_milestones.html#create-new-milestone
type CreateGroupMilestoneOptions struct {
	Title       *string  `url:"title,omitempty" json:"title,omitempty"`
	Description *string  `url:"description,omitempty" json:"description,omitempty"`
	StartDate   *ISOTime `url:"start_date,omitempty" json:"start_date,omitempty"`
	DueDate     *ISOTime `url:"due_date,omitempty" json:"due_date,omitempty"`
}

// CreateGroupMilestone creates a new group milestone.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_milestones.html#create-new-milestone
func (s *GroupMilestonesService) CreateGroupMilestone(gid interface{}, opt *CreateGroupMilestoneOptions, options ...RequestOptionFunc) (*GroupMilestone, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/milestones", PathEscape(group))

	req, err := s.client.NewRequest(http.MethodPost, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	m := new(GroupMilestone)
	resp, err := s.client.Do(req, m)
	if err != nil {
		return nil, resp, err
	}

	return m, resp, nil
}

// UpdateGroupMilestoneOptions represents the available UpdateGroupMilestone() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_milestones.html#edit-milestone
type UpdateGroupMilestoneOptions struct {
	Title       *string  `url:"title,omitempty" json:"title,omitempty"`
	Description *string  `url:"description,omitempty" json:"description,omitempty"`
	StartDate   *ISOTime `url:"start_date,omitempty" json:"start_date,omitempty"`
	DueDate     *ISOTime `url:"due_date,omitempty" json:"due_date,omitempty"`
	StateEvent  *string  `url:"state_event,omitempty" json:"state_event,omitempty"`
}

// UpdateGroupMilestone updates an existing group milestone.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_milestones.html#edit-milestone
func (s *GroupMilestonesService) UpdateGroupMilestone(gid interface{}, milestone int, opt *UpdateGroupMilestoneOptions, options ...RequestOptionFunc) (*GroupMilestone, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/milestones/%d", PathEscape(group), milestone)

	req, err := s.client.NewRequest(http.MethodPut, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	m := new(GroupMilestone)
	resp, err := s.client.Do(req, m)
	if err != nil {
		return nil, resp, err
	}

	return m, resp, nil
}

// DeleteGroupMilestone deletes a specified group milestone.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_milestones.html#delete-group-milestone
func (s *GroupMilestonesService) DeleteGroupMilestone(pid interface{}, milestone int, options ...RequestOptionFunc) (*Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("groups/%s/milestones/%d", PathEscape(project), milestone)

	req, err := s.client.NewRequest(http.MethodDelete, u, nil, options)
	if err != nil {
		return nil, err
	}
	return s.client.Do(req, nil)
}

// GetGroupMilestoneIssuesOptions represents the available GetGroupMilestoneIssues() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_milestones.html#get-all-issues-assigned-to-a-single-milestone
type GetGroupMilestoneIssuesOptions ListOptions

// GetGroupMilestoneIssues gets all issues assigned to a single group milestone.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_milestones.html#get-all-issues-assigned-to-a-single-milestone
func (s *GroupMilestonesService) GetGroupMilestoneIssues(gid interface{}, milestone int, opt *GetGroupMilestoneIssuesOptions, options ...RequestOptionFunc) ([]*Issue, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/milestones/%d/issues", PathEscape(group), milestone)

	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var i []*Issue
	resp, err := s.client.Do(req, &i)
	if err != nil {
		return nil, resp, err
	}

	return i, resp, nil
}

// GetGroupMilestoneMergeRequestsOptions represents the available
// GetGroupMilestoneMergeRequests() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_milestones.html#get-all-merge-requests-assigned-to-a-single-milestone
type GetGroupMilestoneMergeRequestsOptions ListOptions

// GetGroupMilestoneMergeRequests gets all merge requests assigned to a
// single group milestone.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_milestones.html#get-all-merge-requests-assigned-to-a-single-milestone
func (s *GroupMilestonesService) GetGroupMilestoneMergeRequests(gid interface{}, milestone int, opt *GetGroupMilestoneMergeRequestsOptions, options ...RequestOptionFunc) ([]*MergeRequest, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/milestones/%d/merge_requests", PathEscape(group), milestone)

	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var mr []*MergeRequest
	resp, err := s.client.Do(req, &mr)
	if err != nil {
		return nil, resp, err
	}

	return mr, resp, nil
}

// BurndownChartEvent reprensents a burnout chart event
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_milestones.html#get-all-burndown-chart-events-for-a-single-milestone
type BurndownChartEvent struct {
	CreatedAt *time.Time `json:"created_at"`
	Weight    *int       `json:"weight"`
	Action    *string    `json:"action"`
}

// GetGroupMilestoneBurndownChartEventsOptions represents the available
// GetGroupMilestoneBurndownChartEventsOptions() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_milestones.html#get-all-burndown-chart-events-for-a-single-milestone
type GetGroupMilestoneBurndownChartEventsOptions ListOptions

// GetGroupMilestoneBurndownChartEvents gets all merge requests assigned to a
// single group milestone.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_milestones.html#get-all-burndown-chart-events-for-a-single-milestone
func (s *GroupMilestonesService) GetGroupMilestoneBurndownChartEvents(gid interface{}, milestone int, opt *GetGroupMilestoneBurndownChartEventsOptions, options ...RequestOptionFunc) ([]*BurndownChartEvent, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/milestones/%d/burndown_events", PathEscape(group), milestone)

	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var be []*BurndownChartEvent
	resp, err := s.client.Do(req, &be)
	if err != nil {
		return nil, resp, err
	}

	return be, resp, nil
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/group_milestones_test.go000066400000000000000000000427551475761473200260600ustar00rootroot00000000000000package gitlab

import (
	"fmt"
	"net/http"
	"testing"

	"github.com/stretchr/testify/require"
)

func TestGroupMilestonesService_ListGroupMilestones(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/5/milestones", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
			[
			  {
				"id": 12,
				"iid": 3,
				"group_id": 5,
				"title": "10.0",
				"description": "Version",
				"state": "active",
				"expired": false,
				"web_url": "https://gitlab.com/groups/gitlab-org/-/milestones/42"
			  }
			]
		`)
	})

	want := []*GroupMilestone{{
		ID:          12,
		IID:         3,
		GroupID:     5,
		Title:       "10.0",
		Description: "Version",
		State:       "active",
		Expired:     Ptr(false),
	}}

	gms, resp, err := client.GroupMilestones.ListGroupMilestones(5, nil, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, gms)

	gms, resp, err = client.GroupMilestones.ListGroupMilestones(5.01, nil, nil)
	require.EqualError(t, err, "invalid ID type 5.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, gms)

	gms, resp, err = client.GroupMilestones.ListGroupMilestones(5, nil, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, gms)

	gms, resp, err = client.GroupMilestones.ListGroupMilestones(7, nil, nil)
	require.Error(t, err)
	require.Nil(t, gms)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestGroupMilestonesService_GetGroupMilestone(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/5/milestones/12", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
		  {
			"id": 12,
			"iid": 3,
			"group_id": 5,
			"title": "10.0",
			"description": "Version",
			"state": "active",
			"expired": false,
			"web_url": "https://gitlab.com/groups/gitlab-org/-/milestones/42"
		  }
		`)
	})

	want := &GroupMilestone{
		ID:          12,
		IID:         3,
		GroupID:     5,
		Title:       "10.0",
		Description: "Version",
		State:       "active",
		Expired:     Ptr(false),
	}

	gm, resp, err := client.GroupMilestones.GetGroupMilestone(5, 12, nil, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, gm)

	gm, resp, err = client.GroupMilestones.GetGroupMilestone(5.01, 12, nil, nil)
	require.EqualError(t, err, "invalid ID type 5.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, gm)

	gm, resp, err = client.GroupMilestones.GetGroupMilestone(5, 12, nil, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, gm)

	gm, resp, err = client.GroupMilestones.GetGroupMilestone(7, 12, nil, nil)
	require.Error(t, err)
	require.Nil(t, gm)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestGroupMilestonesService_CreateGroupMilestone(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/5/milestones", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		fmt.Fprintf(w, `
		  {
			"id": 12,
			"iid": 3,
			"group_id": 5,
			"title": "10.0",
			"description": "Version",
			"state": "active",
			"expired": false,
			"web_url": "https://gitlab.com/groups/gitlab-org/-/milestones/42"
		  }
		`)
	})

	want := &GroupMilestone{
		ID:          12,
		IID:         3,
		GroupID:     5,
		Title:       "10.0",
		Description: "Version",
		State:       "active",
		Expired:     Ptr(false),
	}

	gm, resp, err := client.GroupMilestones.CreateGroupMilestone(5, nil, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, gm)

	gm, resp, err = client.GroupMilestones.CreateGroupMilestone(5.01, nil, nil)
	require.EqualError(t, err, "invalid ID type 5.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, gm)

	gm, resp, err = client.GroupMilestones.CreateGroupMilestone(5, nil, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, gm)

	gm, resp, err = client.GroupMilestones.CreateGroupMilestone(7, nil, nil)
	require.Error(t, err)
	require.Nil(t, gm)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestGroupMilestonesService_UpdateGroupMilestone(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/5/milestones/12", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPut)
		fmt.Fprintf(w, `
		  {
			"id": 12,
			"iid": 3,
			"group_id": 5,
			"title": "10.0",
			"description": "Version",
			"state": "active",
			"expired": false,
			"web_url": "https://gitlab.com/groups/gitlab-org/-/milestones/42"
		  }
		`)
	})

	want := &GroupMilestone{
		ID:          12,
		IID:         3,
		GroupID:     5,
		Title:       "10.0",
		Description: "Version",
		State:       "active",
		Expired:     Ptr(false),
	}

	gm, resp, err := client.GroupMilestones.UpdateGroupMilestone(5, 12, nil, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, gm)

	gm, resp, err = client.GroupMilestones.UpdateGroupMilestone(5.01, 12, nil, nil)
	require.EqualError(t, err, "invalid ID type 5.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, gm)

	gm, resp, err = client.GroupMilestones.UpdateGroupMilestone(5, 12, nil, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, gm)

	gm, resp, err = client.GroupMilestones.UpdateGroupMilestone(7, 12, nil, nil)
	require.Error(t, err)
	require.Nil(t, gm)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestGroupMilestonesService_DeleteGroupMilestone(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/5/milestones/12", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodDelete)
	})

	resp, err := client.GroupMilestones.DeleteGroupMilestone(5, 12, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)

	resp, err = client.GroupMilestones.DeleteGroupMilestone(5.01, 12, nil)
	require.EqualError(t, err, "invalid ID type 5.01, the ID must be an int or a string")
	require.Nil(t, resp)

	resp, err = client.GroupMilestones.DeleteGroupMilestone(5, 12, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)

	resp, err = client.GroupMilestones.DeleteGroupMilestone(3, 12, nil)
	require.Error(t, err)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestGroupMilestonesService_GetGroupMilestoneIssues(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/5/milestones/12/issues", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
			[
			   {
				  "project_id" : 5,
				  "milestone" : {
					 "due_date" : null,
					 "project_id" : 5,
					 "state" : "closed",
					 "description" : "Rerum est voluptatem provident consequuntur molestias similique ipsum dolor.",
					 "iid" : 3,
					 "id" : 11,
					 "title" : "v3.0"
				  },
				  "author" : {
					 "state" : "active",
					 "web_url" : "https://gitlab.example.com/root",
					 "avatar_url" : null,
					 "username" : "root",
					 "id" : 1,
					 "name" : "Administrator"
				  },
				  "description" : "Omnis vero earum sunt corporis dolor et placeat.",
				  "state" : "closed",
				  "iid" : 1,
				  "assignees" : [{
					 "avatar_url" : null,
					 "web_url" : "https://gitlab.example.com/venky333",
					 "state" : "active",
					 "username" : "venky333",
					 "id" : 9,
					 "name" : "Venkatesh Thalluri"
				  }],
				  "assignee" : {
					 "avatar_url" : null,
					 "web_url" : "https://gitlab.example.com/venky333",
					 "state" : "active",
					 "username" : "venky333",
					 "id" : 9,
					 "name" : "Venkatesh Thalluri"
				  },
				  "id" : 41
				}
			]
		`)
	})

	want := []*Issue{{
		ID:          41,
		IID:         1,
		ExternalID:  "",
		State:       "closed",
		Description: "Omnis vero earum sunt corporis dolor et placeat.",
		Author: &IssueAuthor{
			ID:        1,
			State:     "active",
			WebURL:    "https://gitlab.example.com/root",
			Name:      "Administrator",
			AvatarURL: "",
			Username:  "root",
		},
		Milestone: &Milestone{
			ID:          11,
			IID:         3,
			ProjectID:   5,
			Title:       "v3.0",
			Description: "Rerum est voluptatem provident consequuntur molestias similique ipsum dolor.",
			StartDate:   nil,
			DueDate:     nil,
			State:       "closed",
			WebURL:      "",
			UpdatedAt:   nil,
			CreatedAt:   nil,
			Expired:     nil,
		},
		ProjectID: 5,
		Assignees: []*IssueAssignee{{
			ID:        9,
			State:     "active",
			WebURL:    "https://gitlab.example.com/venky333",
			Name:      "Venkatesh Thalluri",
			AvatarURL: "",
			Username:  "venky333",
		}},
		Assignee: &IssueAssignee{
			ID:        9,
			State:     "active",
			WebURL:    "https://gitlab.example.com/venky333",
			Name:      "Venkatesh Thalluri",
			AvatarURL: "",
			Username:  "venky333",
		},
	}}

	is, resp, err := client.GroupMilestones.GetGroupMilestoneIssues(5, 12, nil, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, is)

	is, resp, err = client.GroupMilestones.GetGroupMilestoneIssues(5.01, 12, nil, nil)
	require.EqualError(t, err, "invalid ID type 5.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, is)

	is, resp, err = client.GroupMilestones.GetGroupMilestoneIssues(5, 12, nil, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, is)

	is, resp, err = client.GroupMilestones.GetGroupMilestoneIssues(7, 12, nil, nil)
	require.Error(t, err)
	require.Nil(t, is)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestGroupMilestonesService_GetGroupMilestoneMergeRequests(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/3/milestones/12/merge_requests", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
			[
			  {
				"id": 1,
				"iid": 1,
				"project_id": 3,
				"title": "test1",
				"description": "fixed login page css paddings",
				"state": "merged",
				"merged_by": {
				  "id": 87854,
				  "name": "Douwe Maan",
				  "username": "DouweM",
				  "state": "active",
				  "avatar_url": "https://gitlab.example.com/uploads/-/system/user/avatar/87854/avatar.png",
				  "web_url": "https://gitlab.com/DouweM"
				},
				"closed_by": null,
				"closed_at": null,
				"target_branch": "master",
				"source_branch": "test1",
				"upvotes": 0,
				"downvotes": 0,
				"author": {
				  "id": 1,
				  "name": "Administrator",
				  "username": "admin",
				  "state": "active",
				  "avatar_url": null,
				  "web_url" : "https://gitlab.example.com/admin"
				},
				"assignee": {
				  "id": 1,
				  "name": "Administrator",
				  "username": "admin",
				  "state": "active",
				  "avatar_url": null,
				  "web_url" : "https://gitlab.example.com/admin"
				},
				"assignees": [{
				  "name": "Venkatesh Thalluri",
				  "username": "venkatesh.thalluri",
				  "id": 12,
				  "state": "active",
				  "avatar_url": "http://www.gravatar.com/avatar/46f6f7dc858ada7be1853f7fb96e81da?s=80&d=identicon",
				  "web_url": "https://gitlab.example.com/axel.block"
				}],
				"reviewers": [{
				  "id": 2,
				  "name": "Sam Bauch",
				  "username": "kenyatta_oconnell",
				  "state": "active",
				  "avatar_url": "https://www.gravatar.com/avatar/956c92487c6f6f7616b536927e22c9a0?s=80&d=identicon",
				  "web_url": "http://gitlab.example.com//kenyatta_oconnell"
				}],
				"source_project_id": 2,
				"target_project_id": 3,
				"draft": false,
				"work_in_progress": false,
				"milestone": {
				  "id": 5,
				  "iid": 1,
				  "project_id": 3,
				  "title": "v2.0",
				  "description": "Assumenda aut placeat expedita exercitationem labore sunt enim earum.",
				  "state": "closed",
				  "web_url": "https://gitlab.example.com/my-group/my-project/milestones/1"
				},
				"merge_when_pipeline_succeeds": true,
				"detailed_merge_status": "mergeable",
				"sha": "8888888888888888888888888888888888888888",
				"merge_commit_sha": null,
				"squash_commit_sha": null,
				"user_notes_count": 1,
				"discussion_locked": null,
				"should_remove_source_branch": true,
				"force_remove_source_branch": false,
				"allow_collaboration": false,
				"allow_maintainer_to_push": false,
				"web_url": "http://gitlab.example.com/my-group/my-project/merge_requests/1",
				"references": {
				  "short": "!1",
				  "relative": "my-group/my-project!1",
				  "full": "my-group/my-project!1"
				},
				"squash": false,
				"task_completion_status":{
				  "count":0,
				  "completed_count":0
				}
			  }
			]
		`)
	})

	want := []*MergeRequest{{
		ID:           1,
		IID:          1,
		TargetBranch: "master",
		SourceBranch: "test1",
		ProjectID:    3,
		Title:        "test1",
		State:        "merged",
		Upvotes:      0,
		Downvotes:    0,
		Author: &BasicUser{
			ID:        1,
			Username:  "admin",
			Name:      "Administrator",
			State:     "active",
			CreatedAt: nil,
			AvatarURL: "",
			WebURL:    "https://gitlab.example.com/admin",
		},
		Assignee: &BasicUser{
			ID: 1, Username: "admin",
			Name:      "Administrator",
			State:     "active",
			AvatarURL: "",
			WebURL:    "https://gitlab.example.com/admin",
		},
		Assignees: []*BasicUser{{
			ID:        12,
			Username:  "venkatesh.thalluri",
			Name:      "Venkatesh Thalluri",
			State:     "active",
			AvatarURL: "http://www.gravatar.com/avatar/46f6f7dc858ada7be1853f7fb96e81da?s=80&d=identicon", WebURL: "https://gitlab.example.com/axel.block",
		}},
		Reviewers: []*BasicUser{{
			ID:        2,
			Username:  "kenyatta_oconnell",
			Name:      "Sam Bauch",
			State:     "active",
			AvatarURL: "https://www.gravatar.com/avatar/956c92487c6f6f7616b536927e22c9a0?s=80&d=identicon", WebURL: "http://gitlab.example.com//kenyatta_oconnell",
		}},
		SourceProjectID: 2,
		TargetProjectID: 3,
		Description:     "fixed login page css paddings",
		WorkInProgress:  false,
		Milestone: &Milestone{
			ID:          5,
			IID:         1,
			ProjectID:   3,
			Title:       "v2.0",
			Description: "Assumenda aut placeat expedita exercitationem labore sunt enim earum.",
			State:       "closed",
			WebURL:      "https://gitlab.example.com/my-group/my-project/milestones/1",
		},
		MergeWhenPipelineSucceeds: true,
		DetailedMergeStatus:       "mergeable",
		MergeError:                "",
		MergedBy: &BasicUser{
			ID:        87854,
			Username:  "DouweM",
			Name:      "Douwe Maan",
			State:     "active",
			AvatarURL: "https://gitlab.example.com/uploads/-/system/user/avatar/87854/avatar.png",
			WebURL:    "https://gitlab.com/DouweM",
		},
		Subscribed:               false,
		SHA:                      "8888888888888888888888888888888888888888",
		MergeCommitSHA:           "",
		SquashCommitSHA:          "",
		UserNotesCount:           1,
		ChangesCount:             "",
		ShouldRemoveSourceBranch: true,
		ForceRemoveSourceBranch:  false,
		AllowCollaboration:       false,
		WebURL:                   "http://gitlab.example.com/my-group/my-project/merge_requests/1",
		References: &IssueReferences{
			Short:    "!1",
			Relative: "my-group/my-project!1",
			Full:     "my-group/my-project!1",
		},
		DiscussionLocked:     false,
		Squash:               false,
		DivergedCommitsCount: 0,
		RebaseInProgress:     false,
		ApprovalsBeforeMerge: 0,
		Reference:            "",
		FirstContribution:    false,
		TaskCompletionStatus: &TasksCompletionStatus{
			Count:          0,
			CompletedCount: 0,
		},
		HasConflicts:                false,
		BlockingDiscussionsResolved: false,
		Overflow:                    false,
	}}

	mrs, resp, err := client.GroupMilestones.GetGroupMilestoneMergeRequests(3, 12, nil, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, mrs)

	mrs, resp, err = client.GroupMilestones.GetGroupMilestoneMergeRequests(3.01, 12, nil, nil)
	require.EqualError(t, err, "invalid ID type 3.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, mrs)

	mrs, resp, err = client.GroupMilestones.GetGroupMilestoneMergeRequests(3, 12, nil, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, mrs)

	mrs, resp, err = client.GroupMilestones.GetGroupMilestoneMergeRequests(7, 12, nil, nil)
	require.Error(t, err)
	require.Nil(t, mrs)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestGroupMilestonesService_GetGroupMilestoneBurndownChartEvents(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/3/milestones/12/burndown_events", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
			[
				{
					"weight": 10,
					"action": "update" 
				}
			]
		`)
	})

	want := []*BurndownChartEvent{{
		Weight: Ptr(10),
		Action: Ptr("update"),
	}}

	bces, resp, err := client.GroupMilestones.GetGroupMilestoneBurndownChartEvents(3, 12, nil, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, bces)

	bces, resp, err = client.GroupMilestones.GetGroupMilestoneBurndownChartEvents(3.01, 12, nil, nil)
	require.EqualError(t, err, "invalid ID type 3.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, bces)

	bces, resp, err = client.GroupMilestones.GetGroupMilestoneBurndownChartEvents(3, 12, nil, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, bces)

	bces, resp, err = client.GroupMilestones.GetGroupMilestoneBurndownChartEvents(7, 12, nil, nil)
	require.Error(t, err)
	require.Nil(t, bces)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/group_protected_environments.go000066400000000000000000000302231475761473200274220ustar00rootroot00000000000000//
// Copyright 2023, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"fmt"
	"net/http"
)

// GroupProtectedEnvironmentsService handles communication with the group-level
// protected environment methods of the GitLab API.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_protected_environments.html
type GroupProtectedEnvironmentsService struct {
	client *Client
}

// GroupProtectedEnvironment represents a group-level protected environment.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_protected_environments.html
type GroupProtectedEnvironment struct {
	Name                  string                               `json:"name"`
	DeployAccessLevels    []*GroupEnvironmentAccessDescription `json:"deploy_access_levels"`
	RequiredApprovalCount int                                  `json:"required_approval_count"`
	ApprovalRules         []*GroupEnvironmentApprovalRule      `json:"approval_rules"`
}

// GroupEnvironmentAccessDescription represents the access decription for a
// group-level protected environment.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_protected_environments.html
type GroupEnvironmentAccessDescription struct {
	ID                     int              `json:"id"`
	AccessLevel            AccessLevelValue `json:"access_level"`
	AccessLevelDescription string           `json:"access_level_description"`
	UserID                 int              `json:"user_id"`
	GroupID                int              `json:"group_id"`
	GroupInheritanceType   int              `json:"group_inheritance_type"`
}

// GroupEnvironmentApprovalRule represents the approval rules for a group-level
// protected environment.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_protected_environments.html#protect-a-single-environment
type GroupEnvironmentApprovalRule struct {
	ID                     int              `json:"id"`
	UserID                 int              `json:"user_id"`
	GroupID                int              `json:"group_id"`
	AccessLevel            AccessLevelValue `json:"access_level"`
	AccessLevelDescription string           `json:"access_level_description"`
	RequiredApprovalCount  int              `json:"required_approvals"`
	GroupInheritanceType   int              `json:"group_inheritance_type"`
}

// ListGroupProtectedEnvironmentsOptions represents the available
// ListGroupProtectedEnvironments() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_protected_environments.html#list-group-level-protected-environments
type ListGroupProtectedEnvironmentsOptions ListOptions

// ListGroupProtectedEnvironments returns a list of protected environments from
// a group.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_protected_environments.html#list-group-level-protected-environments
func (s *GroupProtectedEnvironmentsService) ListGroupProtectedEnvironments(gid interface{}, opt *ListGroupProtectedEnvironmentsOptions, options ...RequestOptionFunc) ([]*GroupProtectedEnvironment, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/protected_environments", PathEscape(group))

	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var pes []*GroupProtectedEnvironment
	resp, err := s.client.Do(req, &pes)
	if err != nil {
		return nil, resp, err
	}

	return pes, resp, nil
}

// GetGroupProtectedEnvironment returns a single group-level protected
// environment.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_protected_environments.html#get-a-single-protected-environment
func (s *GroupProtectedEnvironmentsService) GetGroupProtectedEnvironment(gid interface{}, environment string, options ...RequestOptionFunc) (*GroupProtectedEnvironment, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/protected_environments/%s", PathEscape(group), environment)

	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	pe := new(GroupProtectedEnvironment)
	resp, err := s.client.Do(req, pe)
	if err != nil {
		return nil, resp, err
	}

	return pe, resp, nil
}

// ProtectGroupEnvironmentOptions represents the available
// ProtectGroupEnvironment() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_protected_environments.html#protect-a-single-environment
type ProtectGroupEnvironmentOptions struct {
	Name                  *string                                 `url:"name,omitempty" json:"name,omitempty"`
	DeployAccessLevels    *[]*GroupEnvironmentAccessOptions       `url:"deploy_access_levels,omitempty" json:"deploy_access_levels,omitempty"`
	RequiredApprovalCount *int                                    `url:"required_approval_count,omitempty" json:"required_approval_count,omitempty"`
	ApprovalRules         *[]*GroupEnvironmentApprovalRuleOptions `url:"approval_rules,omitempty" json:"approval_rules,omitempty"`
}

// GroupEnvironmentAccessOptions represents the options for an access decription
// for a group-level protected environment.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_protected_environments.html#protect-a-single-environment
type GroupEnvironmentAccessOptions struct {
	AccessLevel          *AccessLevelValue `url:"access_level,omitempty" json:"access_level,omitempty"`
	UserID               *int              `url:"user_id,omitempty" json:"user_id,omitempty"`
	GroupID              *int              `url:"group_id,omitempty" json:"group_id,omitempty"`
	GroupInheritanceType *int              `url:"group_inheritance_type,omitempty" json:"group_inheritance_type,omitempty"`
}

// GroupEnvironmentApprovalRuleOptions represents the approval rules for a
// group-level protected environment.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_protected_environments.html#protect-a-single-environment
type GroupEnvironmentApprovalRuleOptions struct {
	UserID                 *int              `url:"user_id,omitempty" json:"user_id,omitempty"`
	GroupID                *int              `url:"group_id,omitempty" json:"group_id,omitempty"`
	AccessLevel            *AccessLevelValue `url:"access_level,omitempty" json:"access_level,omitempty"`
	AccessLevelDescription *string           `url:"access_level_description,omitempty" json:"access_level_description,omitempty"`
	RequiredApprovalCount  *int              `url:"required_approvals,omitempty" json:"required_approvals,omitempty"`
	GroupInheritanceType   *int              `url:"group_inheritance_type,omitempty" json:"group_inheritance_type,omitempty"`
}

// ProtectGroupEnvironment protects a single group-level environment.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_protected_environments.html#protect-a-single-environment
func (s *GroupProtectedEnvironmentsService) ProtectGroupEnvironment(gid interface{}, opt *ProtectGroupEnvironmentOptions, options ...RequestOptionFunc) (*GroupProtectedEnvironment, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/protected_environments", PathEscape(group))

	req, err := s.client.NewRequest(http.MethodPost, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	pe := new(GroupProtectedEnvironment)
	resp, err := s.client.Do(req, pe)
	if err != nil {
		return nil, resp, err
	}

	return pe, resp, nil
}

// UpdateGroupProtectedEnvironmentOptions represents the available
// UpdateGroupProtectedEnvironment() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_protected_environments.html#update-a-protected-environment
type UpdateGroupProtectedEnvironmentOptions struct {
	Name                  *string                                       `url:"name,omitempty" json:"name,omitempty"`
	DeployAccessLevels    *[]*UpdateGroupEnvironmentAccessOptions       `url:"deploy_access_levels,omitempty" json:"deploy_access_levels,omitempty"`
	RequiredApprovalCount *int                                          `url:"required_approval_count,omitempty" json:"required_approval_count,omitempty"`
	ApprovalRules         *[]*UpdateGroupEnvironmentApprovalRuleOptions `url:"approval_rules,omitempty" json:"approval_rules,omitempty"`
}

// UpdateGroupEnvironmentAccessOptions represents the options for updates to the
// access decription for a group-level protected environment.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_protected_environments.html#update-a-protected-environment
type UpdateGroupEnvironmentAccessOptions struct {
	AccessLevel          *AccessLevelValue `url:"access_level,omitempty" json:"access_level,omitempty"`
	ID                   *int              `url:"id,omitempty" json:"id,omitempty"`
	UserID               *int              `url:"user_id,omitempty" json:"user_id,omitempty"`
	GroupID              *int              `url:"group_id,omitempty" json:"group_id,omitempty"`
	GroupInheritanceType *int              `url:"group_inheritance_type,omitempty" json:"group_inheritance_type,omitempty"`
	Destroy              *bool             `url:"_destroy,omitempty" json:"_destroy,omitempty"`
}

// UpdateGroupEnvironmentApprovalRuleOptions represents the updates to the
// approval rules for a group-level protected environment.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_protected_environments.html#update-a-protected-environment
type UpdateGroupEnvironmentApprovalRuleOptions struct {
	ID                     *int              `url:"id,omitempty" json:"id,omitempty"`
	UserID                 *int              `url:"user_id,omitempty" json:"user_id,omitempty"`
	GroupID                *int              `url:"group_id,omitempty" json:"group_id,omitempty"`
	AccessLevel            *AccessLevelValue `url:"access_level,omitempty" json:"access_level,omitempty"`
	AccessLevelDescription *string           `url:"access_level_description,omitempty" json:"access_level_description,omitempty"`
	RequiredApprovalCount  *int              `url:"required_approvals,omitempty" json:"required_approvals,omitempty"`
	GroupInheritanceType   *int              `url:"group_inheritance_type,omitempty" json:"group_inheritance_type,omitempty"`
	Destroy                *bool             `url:"_destroy,omitempty" json:"_destroy,omitempty"`
}

// UpdateGroupProtectedEnvironment updates a single group-level protected
// environment.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_protected_environments.html#update-a-protected-environment
func (s *GroupProtectedEnvironmentsService) UpdateGroupProtectedEnvironment(gid interface{}, environment string, opt *UpdateGroupProtectedEnvironmentOptions, options ...RequestOptionFunc) (*GroupProtectedEnvironment, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/protected_environments/%s", PathEscape(group), environment)

	req, err := s.client.NewRequest(http.MethodPut, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	pe := new(GroupProtectedEnvironment)
	resp, err := s.client.Do(req, pe)
	if err != nil {
		return nil, resp, err
	}

	return pe, resp, nil
}

// UnprotectGroupEnvironment unprotects the given protected group-level
// environment.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_protected_environments.html#unprotect-a-single-environment
func (s *GroupProtectedEnvironmentsService) UnprotectGroupEnvironment(gid interface{}, environment string, options ...RequestOptionFunc) (*Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("groups/%s/protected_environments/%s", PathEscape(group), environment)

	req, err := s.client.NewRequest(http.MethodDelete, u, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/group_protected_environments_test.go000066400000000000000000000410601475761473200304620ustar00rootroot00000000000000//
// Copyright 2023, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"fmt"
	"net/http"
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestGroupListProtectedEnvironments(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1/protected_environments", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprint(w, `[{
      "name":"staging",
      "deploy_access_levels": [
        {
          "access_level": 40,
          "access_level_description": "Maintainers",
          "group_inheritance_type": 0
        }
      ],
      "required_approval_count": 1,
      "approval_rules": [
        {
           "id": 38,
           "user_id": 42,
           "group_id": null,
           "access_level": null,
           "access_level_description": "qa-group",
           "required_approvals": 1,
           "group_inheritance_type": 0
        },
        {
           "id": 39,
           "user_id": null,
           "group_id": 135,
           "access_level": 30,
           "access_level_description": "security-group",
           "required_approvals": 2,
           "group_inheritance_type": 1
        }
      ]
    },{
      "name":"production",
      "deploy_access_levels": [
        {
          "access_level": 30,
          "access_level_description": "Developers + Maintainers"
        }
      ]
    }]`)
	})

	expected := []*GroupProtectedEnvironment{
		{
			Name: "staging",
			DeployAccessLevels: []*GroupEnvironmentAccessDescription{
				{
					AccessLevel:            40,
					AccessLevelDescription: "Maintainers",
				},
			},
			RequiredApprovalCount: 1,
			ApprovalRules: []*GroupEnvironmentApprovalRule{
				{
					ID:                     38,
					UserID:                 42,
					AccessLevelDescription: "qa-group",
					RequiredApprovalCount:  1,
				},
				{
					ID:                     39,
					GroupID:                135,
					AccessLevel:            30,
					AccessLevelDescription: "security-group",
					RequiredApprovalCount:  2,
					GroupInheritanceType:   1,
				},
			},
		},
		{
			Name: "production",
			DeployAccessLevels: []*GroupEnvironmentAccessDescription{
				{
					AccessLevel:            30,
					AccessLevelDescription: "Developers + Maintainers",
				},
			},
		},
	}

	opt := &ListGroupProtectedEnvironmentsOptions{}
	environments, _, err := client.GroupProtectedEnvironments.ListGroupProtectedEnvironments(1, opt)
	assert.NoError(t, err, "failed to get response")
	assert.Equal(t, expected, environments)
}

func TestGroupGetProtectedEnvironment(t *testing.T) {
	mux, client := setup(t)

	// Test with RequiredApprovalCount
	environmentName := "development"

	mux.HandleFunc(fmt.Sprintf("/api/v4/groups/1/protected_environments/%s", environmentName), func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `{
      "name":"%s",
      "deploy_access_levels": [
        {
          "access_level": 30,
          "access_level_description": "Developers + Maintainers",
          "group_inheritance_type": 1
        }
      ],
      "required_approval_count": 1,
      "approval_rules": [
        {
           "id": 1,
           "user_id": null,
           "group_id": 10,
           "access_level": 5,
           "access_level_description": "devops",
           "required_approvals": 0,
           "group_inheritance_type": 0
        }
      ]
    }`, environmentName)
	})

	expected := &GroupProtectedEnvironment{
		Name: environmentName,
		DeployAccessLevels: []*GroupEnvironmentAccessDescription{
			{
				AccessLevel:            30,
				AccessLevelDescription: "Developers + Maintainers",
				GroupInheritanceType:   1,
			},
		},
		RequiredApprovalCount: 1,
		ApprovalRules: []*GroupEnvironmentApprovalRule{
			{
				ID:                     1,
				GroupID:                10,
				AccessLevel:            5,
				AccessLevelDescription: "devops",
			},
		},
	}

	environment, _, err := client.GroupProtectedEnvironments.GetGroupProtectedEnvironment(1, environmentName)
	assert.NoError(t, err, "failed to get response")
	assert.Equal(t, expected, environment)

	// Test without RequiredApprovalCount nor ApprovalRules
	environmentName = "testing"

	mux.HandleFunc(fmt.Sprintf("/api/v4/groups/2/protected_environments/%s", environmentName), func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `{
      "name":"%s",
      "deploy_access_levels": [
        {
          "access_level": 30,
          "access_level_description": "Developers + Maintainers",
          "group_inheritance_type": 1
        }
      ]
    }`, environmentName)
	})

	expected = &GroupProtectedEnvironment{
		Name: environmentName,
		DeployAccessLevels: []*GroupEnvironmentAccessDescription{
			{
				AccessLevel:            30,
				AccessLevelDescription: "Developers + Maintainers",
				GroupInheritanceType:   1,
			},
		},
	}

	environment, _, err = client.GroupProtectedEnvironments.GetGroupProtectedEnvironment(2, environmentName)
	assert.NoError(t, err, "failed to get response")
	assert.Equal(t, expected, environment)
}

func TestGroupProtectEnvironments(t *testing.T) {
	mux, client := setup(t)

	// Test with RequiredApprovalCount and ApprovalRules
	environmentName := "other"

	mux.HandleFunc("/api/v4/groups/1/protected_environments", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		fmt.Fprintf(w, `{
      "name":"%s",
      "deploy_access_levels": [
        {
          "access_level": 30,
          "access_level_description": "Developers + Maintainers",
          "group_inheritance_type": 0
        }
      ],
      "required_approval_count": 2,
      "approval_rules": [
        {
           "id": 1,
           "user_id": null,
           "group_id": 10,
           "access_level": 5,
           "access_level_description": "devops",
           "required_approvals": 0,
           "group_inheritance_type": 0
        }
      ]
    }`, environmentName)
	})

	expected := &GroupProtectedEnvironment{
		Name: environmentName,
		DeployAccessLevels: []*GroupEnvironmentAccessDescription{
			{
				AccessLevel:            30,
				AccessLevelDescription: "Developers + Maintainers",
			},
		},
		RequiredApprovalCount: 2,
		ApprovalRules: []*GroupEnvironmentApprovalRule{
			{
				ID:                     1,
				GroupID:                10,
				AccessLevel:            5,
				AccessLevelDescription: "devops",
			},
		},
	}

	opt := &ProtectGroupEnvironmentOptions{
		Name: Ptr(environmentName),
		DeployAccessLevels: &[]*GroupEnvironmentAccessOptions{
			{AccessLevel: Ptr(AccessLevelValue(30))},
		},
		RequiredApprovalCount: Ptr(2),
		ApprovalRules: &[]*GroupEnvironmentApprovalRuleOptions{
			{
				GroupID:                Ptr(10),
				AccessLevel:            Ptr(AccessLevelValue(0)),
				AccessLevelDescription: Ptr("devops"),
			},
		},
	}

	environment, _, err := client.GroupProtectedEnvironments.ProtectGroupEnvironment(1, opt)
	assert.NoError(t, err, "failed to get response")
	assert.Equal(t, expected, environment)

	// Test without RequiredApprovalCount nor ApprovalRules
	environmentName = "staging"

	mux.HandleFunc("/api/v4/groups/2/protected_environments", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		fmt.Fprintf(w, `{
      "name":"%s",
      "deploy_access_levels": [
        {
          "access_level": 30,
          "access_level_description": "Developers + Maintainers"
        }
      ]
    }`, environmentName)
	})

	expected = &GroupProtectedEnvironment{
		Name: environmentName,
		DeployAccessLevels: []*GroupEnvironmentAccessDescription{
			{
				AccessLevel:            30,
				AccessLevelDescription: "Developers + Maintainers",
			},
		},
	}

	opt = &ProtectGroupEnvironmentOptions{
		Name: Ptr(environmentName),
		DeployAccessLevels: &[]*GroupEnvironmentAccessOptions{
			{AccessLevel: Ptr(AccessLevelValue(30))},
		},
	}
	environment, _, err = client.GroupProtectedEnvironments.ProtectGroupEnvironment(2, opt)
	assert.NoError(t, err, "failed to get response")
	assert.Equal(t, expected, environment)
}

func TestGroupUpdateProtectedEnvironments(t *testing.T) {
	mux, client := setup(t)

	// Test with DeployAccessLevels, RequiredApprovalCount, and ApprovalRules as if adding new to existing protected environment
	environmentName := "other"

	mux.HandleFunc(fmt.Sprintf("/api/v4/groups/1/protected_environments/%s", environmentName), func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPut)
		fmt.Fprintf(w, `{
      "name":"%s",
      "deploy_access_levels": [
        {
          "id": 42,
          "access_level": 30,
          "access_level_description": "Developers + Maintainers",
          "group_inheritance_type": 1
        }
      ],
      "required_approval_count": 2,
      "approval_rules": [
        {
           "id": 1,
           "user_id": null,
           "group_id": 10,
           "access_level": 5,
           "access_level_description": "devops",
           "required_approvals": 0,
           "group_inheritance_type": 0
        }
      ]
    }`, environmentName)
	})

	expected := &GroupProtectedEnvironment{
		Name: environmentName,
		DeployAccessLevels: []*GroupEnvironmentAccessDescription{
			{
				ID:                     42,
				AccessLevel:            30,
				AccessLevelDescription: "Developers + Maintainers",
				GroupInheritanceType:   1,
			},
		},
		RequiredApprovalCount: 2,
		ApprovalRules: []*GroupEnvironmentApprovalRule{
			{
				ID:                     1,
				GroupID:                10,
				AccessLevel:            5,
				AccessLevelDescription: "devops",
			},
		},
	}

	opt := &UpdateGroupProtectedEnvironmentOptions{
		Name: Ptr(environmentName),
		DeployAccessLevels: &[]*UpdateGroupEnvironmentAccessOptions{
			{
				AccessLevel:          Ptr(AccessLevelValue(30)),
				GroupInheritanceType: Ptr(1),
			},
		},
		RequiredApprovalCount: Ptr(2),
		ApprovalRules: &[]*UpdateGroupEnvironmentApprovalRuleOptions{
			{
				GroupID:                Ptr(10),
				AccessLevel:            Ptr(AccessLevelValue(5)),
				AccessLevelDescription: Ptr("devops"),
			},
		},
	}

	environment, _, err := client.GroupProtectedEnvironments.UpdateGroupProtectedEnvironment(1, environmentName, opt)
	assert.NoError(t, err, "failed to get response")
	assert.Equal(t, expected, environment)

	// Test with DeployAccessLevels only, as if adding new to existing protected environment
	mux.HandleFunc(fmt.Sprintf("/api/v4/groups/2/protected_environments/%s", environmentName), func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPut)
		fmt.Fprintf(w, `{
      "name":"%s",
      "deploy_access_levels": [
        {
          "id": 42,
          "access_level": 30,
          "access_level_description": "Developers + Maintainers"
        }
      ]
    }`, environmentName)
	})

	expected = &GroupProtectedEnvironment{
		Name: environmentName,
		DeployAccessLevels: []*GroupEnvironmentAccessDescription{
			{
				ID:                     42,
				AccessLevel:            30,
				AccessLevelDescription: "Developers + Maintainers",
			},
		},
	}

	opt = &UpdateGroupProtectedEnvironmentOptions{
		Name: Ptr(environmentName),
		DeployAccessLevels: &[]*UpdateGroupEnvironmentAccessOptions{
			{AccessLevel: Ptr(AccessLevelValue(30))},
		},
	}
	environment, _, err = client.GroupProtectedEnvironments.UpdateGroupProtectedEnvironment(2, environmentName, opt)
	assert.NoError(t, err, "failed to get response")
	assert.Equal(t, expected, environment)

	// Test update to DeployAccessLevel
	mux.HandleFunc(fmt.Sprintf("/api/v4/groups/3/protected_environments/%s", environmentName), func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPut)
		fmt.Fprintf(w, `{
      "name":"%s",
      "deploy_access_levels": [
        {
          "id": 42,
          "access_level": 30,
          "access_level_description": "Developers + Maintainers",
          "group_inheritance_type": 0
        }
      ],
	  "required_approval_count": 2
    }`, environmentName)
	})

	expected = &GroupProtectedEnvironment{
		Name: environmentName,
		DeployAccessLevels: []*GroupEnvironmentAccessDescription{
			{
				ID:                     42,
				AccessLevel:            30,
				AccessLevelDescription: "Developers + Maintainers",
				GroupInheritanceType:   0,
			},
		},
		RequiredApprovalCount: 2,
	}

	opt = &UpdateGroupProtectedEnvironmentOptions{
		Name: Ptr(environmentName),
		DeployAccessLevels: &[]*UpdateGroupEnvironmentAccessOptions{
			{
				ID:                   Ptr(42),
				AccessLevel:          Ptr(AccessLevelValue(30)),
				GroupInheritanceType: Ptr(0),
			},
		},
	}
	environment, _, err = client.GroupProtectedEnvironments.UpdateGroupProtectedEnvironment(3, environmentName, opt)
	assert.NoError(t, err, "failed to get response")
	assert.Equal(t, expected, environment)

	// Test update to ApprovalRules
	mux.HandleFunc(fmt.Sprintf("/api/v4/groups/4/protected_environments/%s", environmentName), func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPut)
		fmt.Fprintf(w, `{
      "name":"%s",
      "deploy_access_levels": [
        {
          "id": 42,
          "access_level": 30,
          "access_level_description": "Developers + Maintainers"
        }
      ],
      "required_approval_count": 2,
      "approval_rules": [
        {
           "id": 1,
           "user_id": null,
           "group_id": 10,
           "access_level": 5,
           "access_level_description": "devops",
           "required_approvals": 0,
           "group_inheritance_type": 0
        }
      ]
    }`, environmentName)
	})

	expected = &GroupProtectedEnvironment{
		Name: environmentName,
		DeployAccessLevels: []*GroupEnvironmentAccessDescription{
			{
				ID:                     42,
				AccessLevel:            30,
				AccessLevelDescription: "Developers + Maintainers",
			},
		},
		RequiredApprovalCount: 2,
		ApprovalRules: []*GroupEnvironmentApprovalRule{
			{
				ID:                     1,
				GroupID:                10,
				AccessLevel:            5,
				AccessLevelDescription: "devops",
			},
		},
	}

	opt = &UpdateGroupProtectedEnvironmentOptions{
		Name: Ptr(environmentName),
		ApprovalRules: &[]*UpdateGroupEnvironmentApprovalRuleOptions{
			{
				ID:                     Ptr(1),
				GroupID:                Ptr(10),
				AccessLevel:            Ptr(AccessLevelValue(5)),
				AccessLevelDescription: Ptr("devops"),
			},
		},
	}

	environment, _, err = client.GroupProtectedEnvironments.UpdateGroupProtectedEnvironment(4, environmentName, opt)
	assert.NoError(t, err, "failed to get response")
	assert.Equal(t, expected, environment)

	// Test destroy ApprovalRule
	mux.HandleFunc(fmt.Sprintf("/api/v4/groups/5/protected_environments/%s", environmentName), func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPut)
		fmt.Fprintf(w, `{
      "name":"%s",
      "deploy_access_levels": [
        {
          "id": 42,
          "access_level": 30,
          "access_level_description": "Developers + Maintainers"
        }
      ],
      "required_approval_count": 0,
      "approval_rules": []
    }`, environmentName)
	})

	expected = &GroupProtectedEnvironment{
		Name: environmentName,
		DeployAccessLevels: []*GroupEnvironmentAccessDescription{
			{
				ID:                     42,
				AccessLevel:            30,
				AccessLevelDescription: "Developers + Maintainers",
			},
		},
		RequiredApprovalCount: 0,
		ApprovalRules:         []*GroupEnvironmentApprovalRule{},
	}

	opt = &UpdateGroupProtectedEnvironmentOptions{
		Name: Ptr(environmentName),
		ApprovalRules: &[]*UpdateGroupEnvironmentApprovalRuleOptions{
			{
				ID:      Ptr(1),
				Destroy: Ptr(true),
			},
		},
		RequiredApprovalCount: Ptr(0),
	}

	environment, _, err = client.GroupProtectedEnvironments.UpdateGroupProtectedEnvironment(5, environmentName, opt)
	assert.NoError(t, err, "failed to get response")
	assert.Equal(t, expected, environment)
}

func TestGroupUnprotectEnvironments(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1/protected_environments/staging", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodDelete)
	})

	resp, err := client.GroupProtectedEnvironments.UnprotectGroupEnvironment(1, "staging")
	assert.NoError(t, err, "failed to get response")
	assert.Equal(t, http.StatusOK, resp.StatusCode)
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/group_repository_storage_move.go000066400000000000000000000160161475761473200276170ustar00rootroot00000000000000//
// Copyright 2023, Nick Westbury
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"fmt"
	"net/http"
	"time"
)

// GroupRepositoryStorageMoveService handles communication with the
// group repositories related methods of the GitLab API.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_repository_storage_moves.html
type GroupRepositoryStorageMoveService struct {
	client *Client
}

// GroupRepositoryStorageMove represents the status of a repository move.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_repository_storage_moves.html
type GroupRepositoryStorageMove struct {
	ID                     int              `json:"id"`
	CreatedAt              *time.Time       `json:"created_at"`
	State                  string           `json:"state"`
	SourceStorageName      string           `json:"source_storage_name"`
	DestinationStorageName string           `json:"destination_storage_name"`
	Group                  *RepositoryGroup `json:"group"`
}

type RepositoryGroup struct {
	ID     int    `json:"id"`
	Name   string `json:"name"`
	WebURL string `json:"web_url"`
}

// RetrieveAllGroupStorageMovesOptions represents the available
// RetrieveAllStorageMoves() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_repository_storage_moves.html#retrieve-all-group-repository-storage-moves
type RetrieveAllGroupStorageMovesOptions ListOptions

// RetrieveAllStorageMoves retrieves all group repository storage moves
// accessible by the authenticated user.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_repository_storage_moves.html#retrieve-all-group-repository-storage-moves
func (g GroupRepositoryStorageMoveService) RetrieveAllStorageMoves(opts RetrieveAllGroupStorageMovesOptions, options ...RequestOptionFunc) ([]*GroupRepositoryStorageMove, *Response, error) {
	req, err := g.client.NewRequest(http.MethodGet, "group_repository_storage_moves", opts, options)
	if err != nil {
		return nil, nil, err
	}

	var gsms []*GroupRepositoryStorageMove
	resp, err := g.client.Do(req, &gsms)
	if err != nil {
		return nil, resp, err
	}

	return gsms, resp, err
}

// RetrieveAllStorageMovesForGroup retrieves all repository storage moves for
// a single group accessible by the authenticated user.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_repository_storage_moves.html#retrieve-all-repository-storage-moves-for-a-single-group
func (g GroupRepositoryStorageMoveService) RetrieveAllStorageMovesForGroup(group int, opts RetrieveAllGroupStorageMovesOptions, options ...RequestOptionFunc) ([]*GroupRepositoryStorageMove, *Response, error) {
	u := fmt.Sprintf("groups/%d/repository_storage_moves", group)

	req, err := g.client.NewRequest(http.MethodGet, u, opts, options)
	if err != nil {
		return nil, nil, err
	}

	var gsms []*GroupRepositoryStorageMove
	resp, err := g.client.Do(req, &gsms)
	if err != nil {
		return nil, resp, err
	}

	return gsms, resp, err
}

// GetStorageMove gets a single group repository storage move.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_repository_storage_moves.html#get-a-single-group-repository-storage-move
func (g GroupRepositoryStorageMoveService) GetStorageMove(repositoryStorage int, options ...RequestOptionFunc) (*GroupRepositoryStorageMove, *Response, error) {
	u := fmt.Sprintf("group_repository_storage_moves/%d", repositoryStorage)

	req, err := g.client.NewRequest(http.MethodGet, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	gsm := new(GroupRepositoryStorageMove)
	resp, err := g.client.Do(req, gsm)
	if err != nil {
		return nil, resp, err
	}

	return gsm, resp, err
}

// GetStorageMoveForGroup gets a single repository storage move for a group.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_repository_storage_moves.html#get-a-single-repository-storage-move-for-a-group
func (g GroupRepositoryStorageMoveService) GetStorageMoveForGroup(group int, repositoryStorage int, options ...RequestOptionFunc) (*GroupRepositoryStorageMove, *Response, error) {
	u := fmt.Sprintf("groups/%d/repository_storage_moves/%d", group, repositoryStorage)

	req, err := g.client.NewRequest(http.MethodGet, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	gsm := new(GroupRepositoryStorageMove)
	resp, err := g.client.Do(req, gsm)
	if err != nil {
		return nil, resp, err
	}

	return gsm, resp, err
}

// ScheduleStorageMoveForGroupOptions represents the available
// ScheduleStorageMoveForGroup() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_repository_storage_moves.html#schedule-a-repository-storage-move-for-a-group
type ScheduleStorageMoveForGroupOptions struct {
	DestinationStorageName *string `url:"destination_storage_name,omitempty" json:"destination_storage_name,omitempty"`
}

// ScheduleStorageMoveForGroup schedule a repository to be moved for a group.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_repository_storage_moves.html#schedule-a-repository-storage-move-for-a-group
func (g GroupRepositoryStorageMoveService) ScheduleStorageMoveForGroup(group int, opts ScheduleStorageMoveForGroupOptions, options ...RequestOptionFunc) (*GroupRepositoryStorageMove, *Response, error) {
	u := fmt.Sprintf("groups/%d/repository_storage_moves", group)

	req, err := g.client.NewRequest(http.MethodPost, u, opts, options)
	if err != nil {
		return nil, nil, err
	}

	gsm := new(GroupRepositoryStorageMove)
	resp, err := g.client.Do(req, gsm)
	if err != nil {
		return nil, resp, err
	}

	return gsm, resp, err
}

// ScheduleAllGroupStorageMovesOptions represents the available
// ScheduleAllStorageMoves() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_repository_storage_moves.html#schedule-repository-storage-moves-for-all-groups-on-a-storage-shard
type ScheduleAllGroupStorageMovesOptions struct {
	SourceStorageName      *string `url:"source_storage_name,omitempty" json:"source_storage_name,omitempty"`
	DestinationStorageName *string `url:"destination_storage_name,omitempty" json:"destination_storage_name,omitempty"`
}

// ScheduleAllStorageMoves schedules all group repositories to be moved.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_repository_storage_moves.html#schedule-repository-storage-moves-for-all-groups-on-a-storage-shard
func (g GroupRepositoryStorageMoveService) ScheduleAllStorageMoves(opts ScheduleAllGroupStorageMovesOptions, options ...RequestOptionFunc) (*Response, error) {
	req, err := g.client.NewRequest(http.MethodPost, "group_repository_storage_moves", opts, options)
	if err != nil {
		return nil, err
	}

	return g.client.Do(req, nil)
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/group_repository_storage_move_test.go000066400000000000000000000124661475761473200306630ustar00rootroot00000000000000package gitlab

import (
	"fmt"
	"net/http"
	"testing"

	"github.com/stretchr/testify/require"
)

func TestGroupRepositoryStorageMove_RetrieveAllGroupStorageMoves(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/group_repository_storage_moves", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprint(w, `[
		{
		  "id":123,
		  "state":"scheduled",
		  "group":{
			"id":283,
			"Name":"Test Group",
			"web_url": "https://gitlab.example.com/groups/test_group"
		  }
		},
		{
		  "id":122,
		  "state":"finished",
		  "group":{
			"id":284,
			"Name":"Test Group 2",
			"web_url": "https://gitlab.example.com/groups/test_group_2"
		  }
		}]`)
	})

	opts := RetrieveAllGroupStorageMovesOptions{Page: 1, PerPage: 2}

	gsms, _, err := client.GroupRepositoryStorageMove.RetrieveAllStorageMoves(opts)
	require.NoError(t, err)

	want := []*GroupRepositoryStorageMove{
		{
			ID:    123,
			State: "scheduled",
			Group: &RepositoryGroup{
				ID:     283,
				Name:   "Test Group",
				WebURL: "https://gitlab.example.com/groups/test_group",
			},
		},
		{
			ID:    122,
			State: "finished",
			Group: &RepositoryGroup{
				ID:     284,
				Name:   "Test Group 2",
				WebURL: "https://gitlab.example.com/groups/test_group_2",
			},
		},
	}
	require.Equal(t, want, gsms)
}

func TestGroupRepositoryStorageMove_RetrieveAllStorageMovesForGroup(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/283/repository_storage_moves", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprint(w, `[
		{
		  "id":123,
		  "state":"scheduled",
		  "group":{
			"id":283,
			"Name":"Test Group",
			"web_url": "https://gitlab.example.com/groups/test_group"
		  }
		},
		{
		  "id":122,
		  "state":"finished",
		  "group":{
			"id":283,
			"Name":"Test Group",
			"web_url": "https://gitlab.example.com/groups/test_group"
		  }
		}]`)
	})

	opts := RetrieveAllGroupStorageMovesOptions{Page: 1, PerPage: 2}

	gsms, _, err := client.GroupRepositoryStorageMove.RetrieveAllStorageMovesForGroup(283, opts)
	require.NoError(t, err)

	want := []*GroupRepositoryStorageMove{
		{
			ID:    123,
			State: "scheduled",
			Group: &RepositoryGroup{
				ID:     283,
				Name:   "Test Group",
				WebURL: "https://gitlab.example.com/groups/test_group",
			},
		},
		{
			ID:    122,
			State: "finished",
			Group: &RepositoryGroup{
				ID:     283,
				Name:   "Test Group",
				WebURL: "https://gitlab.example.com/groups/test_group",
			},
		},
	}
	require.Equal(t, want, gsms)
}

func TestGroupRepositoryStorageMove_GetStorageMove(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/group_repository_storage_moves/123", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprint(w, `
		{
		  "id":123,
		  "state":"scheduled",
		  "group":{
			"id":283,
			"Name":"Test Group",
			"web_url": "https://gitlab.example.com/groups/test_group"
		  }
		}`)
	})

	gsm, _, err := client.GroupRepositoryStorageMove.GetStorageMove(123)
	require.NoError(t, err)

	want := &GroupRepositoryStorageMove{
		ID:    123,
		State: "scheduled",
		Group: &RepositoryGroup{
			ID:     283,
			Name:   "Test Group",
			WebURL: "https://gitlab.example.com/groups/test_group",
		},
	}
	require.Equal(t, want, gsm)
}

func TestGroupRepositoryStorageMove_GetStorageMoveForGroup(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/283/repository_storage_moves/123", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprint(w, `
		{
		  "id":123,
		  "state":"scheduled",
		  "group":{
			"id":283,
			"Name":"Test Group",
			"web_url": "https://gitlab.example.com/groups/test_group"
		  }
		}`)
	})

	gsm, _, err := client.GroupRepositoryStorageMove.GetStorageMoveForGroup(283, 123)
	require.NoError(t, err)

	want := &GroupRepositoryStorageMove{
		ID:    123,
		State: "scheduled",
		Group: &RepositoryGroup{
			ID:     283,
			Name:   "Test Group",
			WebURL: "https://gitlab.example.com/groups/test_group",
		},
	}
	require.Equal(t, want, gsm)
}

func TestGroupRepositoryStorageMove_ScheduleStorageMoveForGroup(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/283/repository_storage_moves", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		fmt.Fprint(w, `
		{
		  "id":123,
		  "state":"scheduled",
		  "group":{
			"id":283,
			"Name":"Test Group",
			"web_url": "https://gitlab.example.com/groups/test_group"
		  }
		}`)
	})

	ssm, _, err := client.GroupRepositoryStorageMove.ScheduleStorageMoveForGroup(283, ScheduleStorageMoveForGroupOptions{})
	require.NoError(t, err)

	want := &GroupRepositoryStorageMove{
		ID:    123,
		State: "scheduled",
		Group: &RepositoryGroup{
			ID:     283,
			Name:   "Test Group",
			WebURL: "https://gitlab.example.com/groups/test_group",
		},
	}
	require.Equal(t, want, ssm)
}

func TestGroupRepositoryStorageMove_ScheduleAllStorageMoves(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/group_repository_storage_moves", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		fmt.Fprint(w, `{"message": "202 Accepted"}`)
	})

	_, err := client.GroupRepositoryStorageMove.ScheduleAllStorageMoves(
		ScheduleAllGroupStorageMovesOptions{
			SourceStorageName: Ptr("default"),
		},
	)
	require.NoError(t, err)
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/group_security_settings.go000066400000000000000000000054771475761473200264260ustar00rootroot00000000000000//
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"fmt"
	"net/http"
)

// GroupSecuritySettingsService handles communication with the Group Security Settings
// related methods of the GitLab API.
//
// Gitlab API docs:
// https://docs.gitlab.com/ee/api/group_security_settings.html
type GroupSecuritySettingsService struct {
	client *Client
}

// GroupSecuritySettings represents the group security settings data.
//
// Gitlab API docs:
// https://docs.gitlab.com/ee/api/group_security_settings.html
type GroupSecuritySettings struct {
	SecretPushProtectionEnabled bool     `json:"secret_push_protection_enabled"`
	Errors                      []string `json:"errors"`
}

// Gets a string representation of the GroupSecuritySettings data.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_security_settings.html
func (s GroupSecuritySettings) String() string {
	return Stringify(s)
}

// GetGroupSecuritySettingsOptions represent the request options for updating
// the group security settings.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_security_settings.html#update-secret_push_protection_enabled-setting
type UpdateGroupSecuritySettingsOptions struct {
	SecretPushProtectionEnabled *bool  `url:"secret_push_protection_enabled,omitempty" json:"secret_push_protection_enabled,omitempty"`
	ProjectsToExclude           *[]int `url:"projects_to_exclude,omitempty" json:"projects_to_exclude,omitempty"`
}

// UpdateSecretPushProtectionEnabledSetting updates the secret_push_protection_enabled
// setting for the all projects in a group to the provided value.
//
// GitLab API Docs:
// https://docs.gitlab.com/ee/api/group_security_settings.html#update-secret_push_protection_enabled-setting
func (s *GroupSecuritySettingsService) UpdateSecretPushProtectionEnabledSetting(gid interface{}, opt UpdateGroupSecuritySettingsOptions, options ...RequestOptionFunc) (*GroupSecuritySettings, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/security_settings", PathEscape(group))

	req, err := s.client.NewRequest(http.MethodPut, u, opt, options)
	if err != nil {
		return nil, nil, err
	}
	settings := new(GroupSecuritySettings)
	resp, err := s.client.Do(req, &settings)
	if err != nil {
		return nil, resp, err
	}

	return settings, resp, err
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/group_security_settings_test.go000066400000000000000000000015261475761473200274540ustar00rootroot00000000000000package gitlab

import (
	"fmt"
	"net/http"
	"testing"

	"github.com/stretchr/testify/require"
)

func TestGroupSecuritySettings_UpdateSecretPushProtectionEnabledSetting(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1/security_settings", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPut)
		fmt.Fprint(w, `{
			"secret_push_protection_enabled": true,
			"errors": []
		}`)
	})

	want := &GroupSecuritySettings{
		SecretPushProtectionEnabled: true,
		Errors:                      []string{},
	}

	d, resp, err := client.GroupSecuritySettings.UpdateSecretPushProtectionEnabledSetting(1, UpdateGroupSecuritySettingsOptions{
		SecretPushProtectionEnabled: Ptr(true),
		ProjectsToExclude:           Ptr([]int{1, 2}),
	})
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, d)
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/group_serviceaccounts.go000066400000000000000000000150251475761473200260250ustar00rootroot00000000000000//
// Copyright 2023, James Hong
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"fmt"
	"net/http"
)

// GroupServiceAccount represents a GitLab service account user.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/groups.html#create-service-account-user
type GroupServiceAccount struct {
	ID       int    `json:"id"`
	Name     string `json:"name"`
	UserName string `json:"username"`
}

// ListServiceAccountsOptions represents the available ListServiceAccounts() options.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/group_service_accounts.html#list-service-account-users
type ListServiceAccountsOptions struct {
	ListOptions
	OrderBy *string `url:"order_by,omitempty" json:"order_by,omitempty"`
	Sort    *string `url:"sort,omitempty" json:"sort,omitempty"`
}

// ListServiceAccounts gets a list of service acxcounts.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/group_service_accounts.html#list-service-account-users
func (s *GroupsService) ListServiceAccounts(gid interface{}, opt *ListServiceAccountsOptions, options ...RequestOptionFunc) ([]*GroupServiceAccount, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/service_accounts", PathEscape(group))

	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var sa []*GroupServiceAccount
	resp, err := s.client.Do(req, &sa)
	if err != nil {
		return nil, resp, err
	}

	return sa, resp, nil
}

// CreateServiceAccountOptions represents the available CreateServiceAccount() options.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/group_service_accounts.html#create-a-service-account-user
type CreateServiceAccountOptions struct {
	Name     *string `url:"name,omitempty" json:"name,omitempty"`
	Username *string `url:"username,omitempty" json:"username,omitempty"`
}

// Creates a service account user.
//
// This API endpoint works on top-level groups only. It does not work on subgroups.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/groups.html#create-service-account-user
func (s *GroupsService) CreateServiceAccount(gid interface{}, opt *CreateServiceAccountOptions, options ...RequestOptionFunc) (*GroupServiceAccount, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/service_accounts", PathEscape(group))

	req, err := s.client.NewRequest(http.MethodPost, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	sa := new(GroupServiceAccount)
	resp, err := s.client.Do(req, sa)
	if err != nil {
		return nil, resp, err
	}

	return sa, resp, nil
}

// CreateServiceAccountPersonalAccessTokenOptions represents the available
// CreateServiceAccountPersonalAccessToken() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_service_accounts.html#create-a-personal-access-token-for-a-service-account-user
type CreateServiceAccountPersonalAccessTokenOptions struct {
	Scopes    *[]string `url:"scopes,omitempty" json:"scopes,omitempty"`
	Name      *string   `url:"name,omitempty" json:"name,omitempty"`
	ExpiresAt *ISOTime  `url:"expires_at,omitempty" json:"expires_at,omitempty"`
}

// CreateServiceAccountPersonalAccessToken add a new Personal Access Token for a
// service account user for a group.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_service_accounts.html#create-a-personal-access-token-for-a-service-account-user
func (s *GroupsService) CreateServiceAccountPersonalAccessToken(gid interface{}, serviceAccount int, opt *CreateServiceAccountPersonalAccessTokenOptions, options ...RequestOptionFunc) (*PersonalAccessToken, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/service_accounts/%d/personal_access_tokens", PathEscape(group), serviceAccount)

	req, err := s.client.NewRequest(http.MethodPost, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	pat := new(PersonalAccessToken)
	resp, err := s.client.Do(req, pat)
	if err != nil {
		return nil, resp, err
	}

	return pat, resp, nil
}

// RotateServiceAccountPersonalAccessTokenOptions represents the available RotateServiceAccountPersonalAccessToken()
// options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_service_accounts.html#rotate-a-personal-access-token-for-a-service-account-user
type RotateServiceAccountPersonalAccessTokenOptions struct {
	ExpiresAt *ISOTime `url:"expires_at,omitempty" json:"expires_at,omitempty"`
}

// RotateServiceAccountPersonalAccessToken rotates a Personal Access Token for a
// service account user for a group.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/groups.html#create-personal-access-token-for-service-account-user
func (s *GroupsService) RotateServiceAccountPersonalAccessToken(gid interface{}, serviceAccount, token int, opt *RotateServiceAccountPersonalAccessTokenOptions, options ...RequestOptionFunc) (*PersonalAccessToken, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/service_accounts/%d/personal_access_tokens/%d/rotate", PathEscape(group), serviceAccount, token)

	req, err := s.client.NewRequest(http.MethodPost, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	pat := new(PersonalAccessToken)
	resp, err := s.client.Do(req, pat)
	if err != nil {
		return nil, resp, err
	}

	return pat, resp, nil
}

// DeleteServiceAccount Deletes a service account user.
//
// This API endpoint works on top-level groups only. It does not work on subgroups.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/group_service_accounts.html#delete-a-service-account-user
func (s *GroupsService) DeleteServiceAccount(gid interface{}, serviceAccount int, options ...RequestOptionFunc) (*Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("groups/%s/service_accounts/%d", PathEscape(group), serviceAccount)

	req, err := s.client.NewRequest(http.MethodDelete, u, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/group_serviceaccounts_test.go000066400000000000000000000107141475761473200270640ustar00rootroot00000000000000//
// Copyright 2023, James Hong
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"fmt"
	"net/http"
	"reflect"
	"testing"
	"time"

	"github.com/stretchr/testify/require"
)

func TestCreateServiceAccount(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1/service_accounts", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		fmt.Fprint(w, `
      {
	      "id": 57,
	      "username": "service_account_group_345_6018816a18e515214e0c34c2b33523fc",
	      "name": "Service account user"
      }`)
	})

	sa, _, err := client.Groups.CreateServiceAccount(1, &CreateServiceAccountOptions{
		Name:     Ptr("Service account user"),
		Username: Ptr("service_account_group_345_6018816a18e515214e0c34c2b33523fc"),
	})
	if err != nil {
		t.Error(err)
	}

	want := &GroupServiceAccount{
		ID:       57,
		UserName: "service_account_group_345_6018816a18e515214e0c34c2b33523fc",
		Name:     "Service account user",
	}

	if !reflect.DeepEqual(sa, want) {
		t.Errorf("CreateServiceAccount returned \ngot:\n%v\nwant:\n%v", Stringify(sa), Stringify(want))
	}
}

func TestCreateServiceAccountPersonalAccessToken(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1/service_accounts/57/personal_access_tokens", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		fmt.Fprint(w, `
      {
      	"id":6,
      	"name":"service_account_token",
      	"revoked":false,
      	"created_at":"2023-06-13T07:47:13.000Z",
      	"scopes":["api"],
      	"user_id":71,
      	"last_used_at":null,
      	"active":true,
      	"expires_at":"2024-06-12",
      	"token":"random_token"
      }`)
	})

	expireTime, err := ParseISOTime("2024-06-12")
	require.NoError(t, err)

	options := &CreateServiceAccountPersonalAccessTokenOptions{
		Scopes:    Ptr([]string{"api"}),
		Name:      Ptr("service_account_token"),
		ExpiresAt: Ptr(expireTime),
	}
	pat, _, err := client.Groups.CreateServiceAccountPersonalAccessToken(1, 57, options)
	if err != nil {
		t.Error(err)
	}

	datePointer := time.Date(2023, 0o6, 13, 0o7, 47, 13, 0, time.UTC)
	expiresAt := ISOTime(time.Date(2024, time.June, 12, 0, 0, 0, 0, time.UTC))

	want := &PersonalAccessToken{
		ID:         6,
		Name:       "service_account_token",
		Revoked:    false,
		CreatedAt:  &datePointer,
		Scopes:     []string{"api"},
		UserID:     71,
		LastUsedAt: nil,
		Active:     true,
		ExpiresAt:  &expiresAt,
		Token:      "random_token",
	}

	if !reflect.DeepEqual(pat, want) {
		t.Errorf("CreateServiceAccountPersonalAccessToken returned \ngot:\n%v\nwant:\n%v", Stringify(pat), Stringify(want))
	}
}

func TestRotateServiceAccountPersonalAccessToken(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1/service_accounts/57/personal_access_tokens/6/rotate", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		fmt.Fprint(w, `
      {
      	"id":7,
      	"name":"service_account_token",
      	"revoked":false,
      	"created_at":"2023-06-13T07:54:49.000Z",
      	"scopes":["api"],
      	"user_id":71,
      	"last_used_at":null,
      	"active":true,
      	"expires_at":"2025-06-20",
      	"token":"random_token_2"
      }`)
	})

	datePointer := time.Date(2023, 0o6, 13, 0o7, 54, 49, 0, time.UTC)
	expiresAt := ISOTime(time.Date(2025, time.June, 20, 0, 0, 0, 0, time.UTC))
	opts := &RotateServiceAccountPersonalAccessTokenOptions{ExpiresAt: &expiresAt}
	pat, _, err := client.Groups.RotateServiceAccountPersonalAccessToken(1, 57, 6, opts)
	if err != nil {
		t.Error(err)
	}

	want := &PersonalAccessToken{
		ID:         7,
		Name:       "service_account_token",
		Revoked:    false,
		CreatedAt:  &datePointer,
		Scopes:     []string{"api"},
		UserID:     71,
		LastUsedAt: nil,
		Active:     true,
		ExpiresAt:  &expiresAt,
		Token:      "random_token_2",
	}

	if !reflect.DeepEqual(pat, want) {
		t.Errorf("RotateServiceAccountPersonalAccessToken returned \ngot:\n%v\nwant:\n%v", Stringify(pat), Stringify(want))
	}
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/group_ssh_certificates.go000066400000000000000000000061441475761473200261510ustar00rootroot00000000000000package gitlab

import (
	"fmt"
	"net/http"
	"time"
)

// GroupSSHCertificatesService handles communication with the group
// SSH certificate related methods of the GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/group_ssh_certificates.html
type GroupSSHCertificatesService struct {
	client *Client
}

// GroupSSHCertificate represents a GitLab Group SSH certificate.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/member_roles.html
type GroupSSHCertificate struct {
	ID        int        `json:"id"`
	Title     string     `json:"title"`
	Key       string     `json:"key"`
	CreatedAt *time.Time `json:"created_at"`
}

// ListGroupSSHCertificates gets a list of SSH certificates for a specified
// group.
//
// Gitlab API docs:
// https://docs.gitlab.com/ee/api/group_ssh_certificates.html#get-all-ssh-certificates-for-a-particular-group
func (s *GroupSSHCertificatesService) ListGroupSSHCertificates(gid interface{}, options ...RequestOptionFunc) ([]*GroupSSHCertificate, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/ssh_certificates", PathEscape(group))

	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	var certs []*GroupSSHCertificate
	resp, err := s.client.Do(req, &certs)
	if err != nil {
		return nil, resp, err
	}

	return certs, resp, nil
}

// CreateGroupSSHCertificateOptions represents the available
// CreateGroupSSHCertificate() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_ssh_certificates.html#create-ssh-certificate
type CreateGroupSSHCertificateOptions struct {
	Key   *string `url:"key,omitempty" json:"key,omitempty"`
	Title *string `url:"title,omitempty" json:"title,omitempty"`
}

// CreateMemberRole creates a new member role for a specified group.
//
// Gitlab API docs:
// https://docs.gitlab.com/ee/api/group_ssh_certificates.html#create-ssh-certificate
func (s *GroupSSHCertificatesService) CreateGroupSSHCertificate(gid interface{}, opt *CreateGroupSSHCertificateOptions, options ...RequestOptionFunc) (*GroupSSHCertificate, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/ssh_certificates", PathEscape(group))

	req, err := s.client.NewRequest(http.MethodPost, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	cert := new(GroupSSHCertificate)
	resp, err := s.client.Do(req, cert)
	if err != nil {
		return nil, resp, err
	}

	return cert, resp, nil
}

// DeleteGroupSSHCertificate deletes a SSH certificate from a specified group.
//
// Gitlab API docs:
// https://docs.gitlab.com/ee/api/group_ssh_certificates.html#delete-group-ssh-certificate
func (s *GroupSSHCertificatesService) DeleteGroupSSHCertificate(gid interface{}, cert int, options ...RequestOptionFunc) (*Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("groups/%s/ssh_certificates/%d", PathEscape(group), cert)

	req, err := s.client.NewRequest(http.MethodDelete, u, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/group_ssh_certificates_test.go000066400000000000000000000036031475761473200272050ustar00rootroot00000000000000package gitlab

import (
	"net/http"
	"testing"
	"time"

	"github.com/stretchr/testify/require"
)

func TestListGroupSSHCertificates(t *testing.T) {
	mux, client := setup(t)

	path := "/api/v4/groups/1/ssh_certificates"

	mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		mustWriteHTTPResponse(t, w, "testdata/list_group_ssh_certificates.json")
	})

	certificates, _, err := client.GroupSSHCertificates.ListGroupSSHCertificates(1)
	require.NoError(t, err)

	want := []*GroupSSHCertificate{
		{
			ID:        1876,
			Title:     "SSH Certificate",
			Key:       "ssh-rsa FAKE-KEY example@gitlab.com",
			CreatedAt: Ptr(time.Date(2022, time.March, 20, 20, 42, 40, 221000000, time.UTC)),
		},
	}

	require.Equal(t, want, certificates)
}

func TestCreateGroupSSHCertificate(t *testing.T) {
	mux, client := setup(t)

	path := "/api/v4/groups/84/ssh_certificates"

	mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		mustWriteHTTPResponse(t, w, "testdata/create_group_ssh_certificates.json")
	})

	cert, _, err := client.GroupSSHCertificates.CreateGroupSSHCertificate(84, &CreateGroupSSHCertificateOptions{
		Key:   Ptr("ssh-rsa FAKE-KEY example@gitlab.com"),
		Title: Ptr("SSH Certificate"),
	})
	require.NoError(t, err)

	want := &GroupSSHCertificate{
		ID:        1876,
		Title:     "SSH Certificate",
		Key:       "ssh-rsa FAKE-KEY example@gitlab.com",
		CreatedAt: Ptr(time.Date(2022, time.March, 20, 20, 42, 40, 221000000, time.UTC)),
	}

	require.Equal(t, want, cert)
}

func TestDeleteGroupSSHCertificate(t *testing.T) {
	mux, client := setup(t)

	path := "/api/v4/groups/1/ssh_certificates/1876"

	mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodDelete)
	})

	_, err := client.GroupSSHCertificates.DeleteGroupSSHCertificate(1, 1876)
	require.NoError(t, err)
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/group_variables.go000066400000000000000000000165271475761473200246050ustar00rootroot00000000000000//
// Copyright 2021, Patrick Webster
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"fmt"
	"net/http"
	"net/url"
)

// GroupVariablesService handles communication with the
// group variables related methods of the GitLab API.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_level_variables.html
type GroupVariablesService struct {
	client *Client
}

// GroupVariable represents a GitLab group Variable.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_level_variables.html
type GroupVariable struct {
	Key              string            `json:"key"`
	Value            string            `json:"value"`
	VariableType     VariableTypeValue `json:"variable_type"`
	Protected        bool              `json:"protected"`
	Masked           bool              `json:"masked"`
	Hidden           bool              `json:"hidden"`
	Raw              bool              `json:"raw"`
	EnvironmentScope string            `json:"environment_scope"`
	Description      string            `json:"description"`
}

func (v GroupVariable) String() string {
	return Stringify(v)
}

// ListGroupVariablesOptions represents the available options for listing variables
// for a group.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_level_variables.html#list-group-variables
type ListGroupVariablesOptions ListOptions

// ListVariables gets a list of all variables for a group.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_level_variables.html#list-group-variables
func (s *GroupVariablesService) ListVariables(gid interface{}, opt *ListGroupVariablesOptions, options ...RequestOptionFunc) ([]*GroupVariable, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/variables", PathEscape(group))

	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var vs []*GroupVariable
	resp, err := s.client.Do(req, &vs)
	if err != nil {
		return nil, resp, err
	}

	return vs, resp, nil
}

// GetGroupVariableOptions represents the available GetVariable()
// options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_level_variables.html#show-variable-details
type GetGroupVariableOptions struct {
	Filter *VariableFilter `url:"filter,omitempty" json:"filter,omitempty"`
}

// GetVariable gets a variable.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_level_variables.html#show-variable-details
func (s *GroupVariablesService) GetVariable(gid interface{}, key string, opt *GetGroupVariableOptions, options ...RequestOptionFunc) (*GroupVariable, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/variables/%s", PathEscape(group), url.PathEscape(key))

	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	v := new(GroupVariable)
	resp, err := s.client.Do(req, v)
	if err != nil {
		return nil, resp, err
	}

	return v, resp, nil
}

// CreateGroupVariableOptions represents the available CreateVariable()
// options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_level_variables.html#create-variable
type CreateGroupVariableOptions struct {
	Key              *string            `url:"key,omitempty" json:"key,omitempty"`
	Value            *string            `url:"value,omitempty" json:"value,omitempty"`
	Description      *string            `url:"description,omitempty" json:"description,omitempty"`
	EnvironmentScope *string            `url:"environment_scope,omitempty" json:"environment_scope,omitempty"`
	Masked           *bool              `url:"masked,omitempty" json:"masked,omitempty"`
	MaskedAndHidden  *bool              `url:"masked_and_hidden,omitempty" json:"masked_and_hidden,omitempty"`
	Protected        *bool              `url:"protected,omitempty" json:"protected,omitempty"`
	Raw              *bool              `url:"raw,omitempty" json:"raw,omitempty"`
	VariableType     *VariableTypeValue `url:"variable_type,omitempty" json:"variable_type,omitempty"`
}

// CreateVariable creates a new group variable.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_level_variables.html#create-variable
func (s *GroupVariablesService) CreateVariable(gid interface{}, opt *CreateGroupVariableOptions, options ...RequestOptionFunc) (*GroupVariable, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/variables", PathEscape(group))

	req, err := s.client.NewRequest(http.MethodPost, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	v := new(GroupVariable)
	resp, err := s.client.Do(req, v)
	if err != nil {
		return nil, resp, err
	}

	return v, resp, nil
}

// UpdateGroupVariableOptions represents the available UpdateVariable()
// options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_level_variables.html#update-variable
type UpdateGroupVariableOptions struct {
	Value            *string            `url:"value,omitempty" json:"value,omitempty"`
	Description      *string            `url:"description,omitempty" json:"description,omitempty"`
	EnvironmentScope *string            `url:"environment_scope,omitempty" json:"environment_scope,omitempty"`
	Masked           *bool              `url:"masked,omitempty" json:"masked,omitempty"`
	Protected        *bool              `url:"protected,omitempty" json:"protected,omitempty"`
	Raw              *bool              `url:"raw,omitempty" json:"raw,omitempty"`
	VariableType     *VariableTypeValue `url:"variable_type,omitempty" json:"variable_type,omitempty"`
}

// UpdateVariable updates the position of an existing
// group issue board list.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_level_variables.html#update-variable
func (s *GroupVariablesService) UpdateVariable(gid interface{}, key string, opt *UpdateGroupVariableOptions, options ...RequestOptionFunc) (*GroupVariable, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/variables/%s", PathEscape(group), url.PathEscape(key))

	req, err := s.client.NewRequest(http.MethodPut, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	v := new(GroupVariable)
	resp, err := s.client.Do(req, v)
	if err != nil {
		return nil, resp, err
	}

	return v, resp, nil
}

// RemoveVariable removes a group's variable.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_level_variables.html#remove-variable
func (s *GroupVariablesService) RemoveVariable(gid interface{}, key string, options ...RequestOptionFunc) (*Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("groups/%s/variables/%s", PathEscape(group), url.PathEscape(key))

	req, err := s.client.NewRequest(http.MethodDelete, u, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/group_variables_test.go000066400000000000000000000142041475761473200256320ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"fmt"
	"net/http"
	"reflect"
	"testing"
)

func TestListGroupVariabless(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1/variables",
		func(w http.ResponseWriter, r *http.Request) {
			testMethod(t, r, http.MethodGet)
			fmt.Fprint(w, `[{"key": "TEST_VARIABLE_1","value": "test1","protected": false,"masked": true,"hidden": true}]`)
		})

	variables, _, err := client.GroupVariables.ListVariables(1, &ListGroupVariablesOptions{})
	if err != nil {
		t.Errorf("GroupVariables.ListVariables returned error: %v", err)
	}

	want := []*GroupVariable{
		{
			Key:       "TEST_VARIABLE_1",
			Value:     "test1",
			Protected: false,
			Masked:    true,
			Hidden:    true,
		},
	}

	if !reflect.DeepEqual(want, variables) {
		t.Errorf("GroupVariables.ListVariablesreturned %+v, want %+v", variables, want)
	}
}

func TestGetGroupVariable(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1/variables/TEST_VARIABLE_1",
		func(w http.ResponseWriter, r *http.Request) {
			testMethod(t, r, http.MethodGet)
			testParams(t, r, "filter%5Benvironment_scope%5D=prod")
			fmt.Fprint(w, `{"key": "TEST_VARIABLE_1","value": "test1","protected": false,"masked": true,"hidden": false}`)
		})

	variable, _, err := client.GroupVariables.GetVariable(1, "TEST_VARIABLE_1", &GetGroupVariableOptions{Filter: &VariableFilter{EnvironmentScope: "prod"}})
	if err != nil {
		t.Errorf("GroupVariables.GetVariable returned error: %v", err)
	}

	want := &GroupVariable{Key: "TEST_VARIABLE_1", Value: "test1", Protected: false, Masked: true, Hidden: false}
	if !reflect.DeepEqual(want, variable) {
		t.Errorf("GroupVariables.GetVariable returned %+v, want %+v", variable, want)
	}
}

func TestCreateGroupVariable(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1/variables",
		func(w http.ResponseWriter, r *http.Request) {
			testMethod(t, r, http.MethodPost)
			fmt.Fprint(w, `{"key": "TEST_VARIABLE_1","value":"test1","protected": false,"masked": true,"hidden": false}`)
		})

	opt := &CreateGroupVariableOptions{
		Key:             Ptr("TEST_VARIABLE_1"),
		Value:           Ptr("test1"),
		Protected:       Ptr(false),
		Masked:          Ptr(true),
		MaskedAndHidden: Ptr(false),
	}

	variable, _, err := client.GroupVariables.CreateVariable(1, opt, nil)
	if err != nil {
		t.Errorf("GroupVariables.CreateVariable returned error: %v", err)
	}

	want := &GroupVariable{Key: "TEST_VARIABLE_1", Value: "test1", Protected: false, Masked: true, Hidden: false}
	if !reflect.DeepEqual(want, variable) {
		t.Errorf("GroupVariables.CreateVariable returned %+v, want %+v", variable, want)
	}
}

func TestCreateGroupVariable_MaskedAndHidden(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1/variables",
		func(w http.ResponseWriter, r *http.Request) {
			testMethod(t, r, http.MethodPost)
			fmt.Fprint(w, `{"key": "TEST_VARIABLE_1","protected": false,"masked": true,"hidden": true}`)
		})

	opt := &CreateGroupVariableOptions{
		Key:             Ptr("TEST_VARIABLE_1"),
		Value:           Ptr("test1"),
		Protected:       Ptr(false),
		Masked:          Ptr(true),
		MaskedAndHidden: Ptr(true),
	}

	variable, _, err := client.GroupVariables.CreateVariable(1, opt, nil)
	if err != nil {
		t.Errorf("GroupVariables.CreateVariable returned error: %v", err)
	}

	want := &GroupVariable{Key: "TEST_VARIABLE_1", Protected: false, Masked: true, Hidden: true}
	if !reflect.DeepEqual(want, variable) {
		t.Errorf("GroupVariables.CreateVariable returned %+v, want %+v", variable, want)
	}
}

func TestDeleteGroupVariable(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1/variables/TEST_VARIABLE_1",
		func(w http.ResponseWriter, r *http.Request) {
			testMethod(t, r, http.MethodDelete)
			w.WriteHeader(http.StatusAccepted)
		})

	resp, err := client.GroupVariables.RemoveVariable(1, "TEST_VARIABLE_1")
	if err != nil {
		t.Errorf("GroupVariables.RemoveVariable returned error: %v", err)
	}

	want := http.StatusAccepted
	got := resp.StatusCode
	if got != want {
		t.Errorf("GroupVariables.RemoveVariable returned %d, want %d", got, want)
	}
}

func TestUpdateGroupVariable(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1/variables/TEST_VARIABLE_1",
		func(w http.ResponseWriter, r *http.Request) {
			testMethod(t, r, http.MethodPut)
			fmt.Fprint(w, `{"key": "TEST_VARIABLE_1","value": "test1","protected": false,"masked": true,"hidden": false}`)
		})

	variable, _, err := client.GroupVariables.UpdateVariable(1, "TEST_VARIABLE_1", &UpdateGroupVariableOptions{})
	if err != nil {
		t.Errorf("GroupVariables.UpdateVariable returned error: %v", err)
	}

	want := &GroupVariable{Key: "TEST_VARIABLE_1", Value: "test1", Protected: false, Masked: true, Hidden: false}
	if !reflect.DeepEqual(want, variable) {
		t.Errorf("Groups.UpdatedGroup returned %+v, want %+v", variable, want)
	}
}

func TestUpdateGroupVariable_MaskedAndHidden(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1/variables/TEST_VARIABLE_1",
		func(w http.ResponseWriter, r *http.Request) {
			testMethod(t, r, http.MethodPut)
			fmt.Fprint(w, `{"key": "TEST_VARIABLE_1","protected": false,"masked": true,"hidden": true}`)
		})

	variable, _, err := client.GroupVariables.UpdateVariable(1, "TEST_VARIABLE_1", &UpdateGroupVariableOptions{})
	if err != nil {
		t.Errorf("GroupVariables.UpdateVariable returned error: %v", err)
	}

	want := &GroupVariable{Key: "TEST_VARIABLE_1", Protected: false, Masked: true, Hidden: true}
	if !reflect.DeepEqual(want, variable) {
		t.Errorf("Groups.UpdatedGroup returned %+v, want %+v", variable, want)
	}
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/group_wikis.go000066400000000000000000000142731475761473200237570ustar00rootroot00000000000000//
// Copyright 2021, Markus Lackner
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package gitlab

import (
	"fmt"
	"net/http"
	"net/url"
)

// GroupWikisService handles communication with the group wikis related methods of
// the Gitlab API.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/group_wikis.html
type GroupWikisService struct {
	client *Client
}

// GroupWiki represents a GitLab groups wiki.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/group_wikis.html
type GroupWiki struct {
	Content  string          `json:"content"`
	Encoding string          `json:"encoding"`
	Format   WikiFormatValue `json:"format"`
	Slug     string          `json:"slug"`
	Title    string          `json:"title"`
}

func (w GroupWiki) String() string {
	return Stringify(w)
}

// ListGroupWikisOptions represents the available ListGroupWikis options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_wikis.html#list-wiki-pages
type ListGroupWikisOptions struct {
	WithContent *bool `url:"with_content,omitempty" json:"with_content,omitempty"`
}

// ListGroupWikis lists all pages of the wiki of the given group id.
// When with_content is set, it also returns the content of the pages.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_wikis.html#list-wiki-pages
func (s *GroupWikisService) ListGroupWikis(gid interface{}, opt *ListGroupWikisOptions, options ...RequestOptionFunc) ([]*GroupWiki, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/wikis", PathEscape(group))

	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var gws []*GroupWiki
	resp, err := s.client.Do(req, &gws)
	if err != nil {
		return nil, resp, err
	}

	return gws, resp, nil
}

// GetGroupWikiPageOptions represents options to GetGroupWikiPage
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_wikis.html#get-a-wiki-page
type GetGroupWikiPageOptions struct {
	RenderHTML *bool   `url:"render_html,omitempty" json:"render_html,omitempty"`
	Version    *string `url:"version,omitempty" json:"version,omitempty"`
}

// GetGroupWikiPage gets a wiki page for a given group.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_wikis.html#get-a-wiki-page
func (s *GroupWikisService) GetGroupWikiPage(gid interface{}, slug string, opt *GetGroupWikiPageOptions, options ...RequestOptionFunc) (*GroupWiki, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/wikis/%s", PathEscape(group), url.PathEscape(slug))

	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	gw := new(GroupWiki)
	resp, err := s.client.Do(req, gw)
	if err != nil {
		return nil, resp, err
	}

	return gw, resp, nil
}

// CreateGroupWikiPageOptions represents options to CreateGroupWikiPage.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_wikis.html#create-a-new-wiki-page
type CreateGroupWikiPageOptions struct {
	Content *string          `url:"content,omitempty" json:"content,omitempty"`
	Title   *string          `url:"title,omitempty" json:"title,omitempty"`
	Format  *WikiFormatValue `url:"format,omitempty" json:"format,omitempty"`
}

// CreateGroupWikiPage creates a new wiki page for the given group with
// the given title, slug, and content.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_wikis.html#create-a-new-wiki-page
func (s *GroupWikisService) CreateGroupWikiPage(gid interface{}, opt *CreateGroupWikiPageOptions, options ...RequestOptionFunc) (*GroupWiki, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/wikis", PathEscape(group))

	req, err := s.client.NewRequest(http.MethodPost, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	w := new(GroupWiki)
	resp, err := s.client.Do(req, w)
	if err != nil {
		return nil, resp, err
	}

	return w, resp, nil
}

// EditGroupWikiPageOptions represents options to EditGroupWikiPage.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_wikis.html#edit-an-existing-wiki-page
type EditGroupWikiPageOptions struct {
	Content *string          `url:"content,omitempty" json:"content,omitempty"`
	Title   *string          `url:"title,omitempty" json:"title,omitempty"`
	Format  *WikiFormatValue `url:"format,omitempty" json:"format,omitempty"`
}

// EditGroupWikiPage Updates an existing wiki page. At least one parameter is
// required to update the wiki page.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_wikis.html#edit-an-existing-wiki-page
func (s *GroupWikisService) EditGroupWikiPage(gid interface{}, slug string, opt *EditGroupWikiPageOptions, options ...RequestOptionFunc) (*GroupWiki, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/wikis/%s", PathEscape(group), url.PathEscape(slug))

	req, err := s.client.NewRequest(http.MethodPut, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	w := new(GroupWiki)
	resp, err := s.client.Do(req, w)
	if err != nil {
		return nil, resp, err
	}

	return w, resp, nil
}

// DeleteGroupWikiPage deletes a wiki page with a given slug.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_wikis.html#delete-a-wiki-page
func (s *GroupWikisService) DeleteGroupWikiPage(gid interface{}, slug string, options ...RequestOptionFunc) (*Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("groups/%s/wikis/%s", PathEscape(group), url.PathEscape(slug))

	req, err := s.client.NewRequest(http.MethodDelete, u, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/group_wikis_test.go000066400000000000000000000076141475761473200250170ustar00rootroot00000000000000package gitlab

import (
	"fmt"
	"net/http"
	"reflect"
	"testing"
)

func TestListGroupWikis(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1/wikis",
		func(w http.ResponseWriter, r *http.Request) {
			testMethod(t, r, http.MethodGet)
			fmt.Fprint(w, `[
				{
					"content": "content here",
					"format": "markdown",
					"slug": "deploy",
					"title": "deploy title"
				}
			]`)
		})

	groupwikis, _, err := client.GroupWikis.ListGroupWikis(1, &ListGroupWikisOptions{})
	if err != nil {
		t.Errorf("GroupWikis.ListGroupWikis returned error: %v", err)
	}

	want := []*GroupWiki{
		{
			Content: "content here",
			Format:  WikiFormatMarkdown,
			Slug:    "deploy",
			Title:   "deploy title",
		},
	}
	if !reflect.DeepEqual(want, groupwikis) {
		t.Errorf("GroupWikis.ListGroupWikis returned %+v, want %+v", groupwikis, want)
	}
}

func TestGetGroupWikiPage(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1/wikis/deploy",
		func(w http.ResponseWriter, r *http.Request) {
			testMethod(t, r, http.MethodGet)
			fmt.Fprint(w, `{
				"content": "content here",
				"format": "asciidoc",
				"slug": "deploy",
				"title": "deploy title",
				"encoding": "UTF-8"
			}`)
		})

	groupwiki, _, err := client.GroupWikis.GetGroupWikiPage(1, "deploy", &GetGroupWikiPageOptions{})
	if err != nil {
		t.Errorf("GroupWikis.GetGroupWikiPage returned error: %v", err)
	}

	want := &GroupWiki{
		Content:  "content here",
		Encoding: "UTF-8",
		Format:   WikiFormatASCIIDoc,
		Slug:     "deploy",
		Title:    "deploy title",
	}
	if !reflect.DeepEqual(want, groupwiki) {
		t.Errorf("GroupWikis.GetGroupWikiPage returned %+v, want %+v", groupwiki, want)
	}
}

func TestCreateGroupWikiPage(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1/wikis",
		func(w http.ResponseWriter, r *http.Request) {
			testMethod(t, r, http.MethodPost)
			fmt.Fprint(w, `{
				"content": "content here",
				"format": "rdoc",
				"slug": "deploy",
				"title": "deploy title"
			}`)
		})

	groupwiki, _, err := client.GroupWikis.CreateGroupWikiPage(1, &CreateGroupWikiPageOptions{
		Content: Ptr("content here"),
		Title:   Ptr("deploy title"),
		Format:  Ptr(WikiFormatRDoc),
	})
	if err != nil {
		t.Errorf("GroupWikis.CreateGroupWikiPage returned error: %v", err)
	}

	want := &GroupWiki{
		Content: "content here",
		Format:  WikiFormatRDoc,
		Slug:    "deploy",
		Title:   "deploy title",
	}
	if !reflect.DeepEqual(want, groupwiki) {
		t.Errorf("GroupWikis.CreateGroupWikiPage returned %+v, want %+v", groupwiki, want)
	}
}

func TestEditGroupWikiPage(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1/wikis/deploy",
		func(w http.ResponseWriter, r *http.Request) {
			testMethod(t, r, http.MethodPut)
			fmt.Fprint(w, `{
				"content": "content here",
				"format": "asciidoc",
				"slug": "deploy",
				"title": "deploy title"
			}`)
		})

	groupwiki, _, err := client.GroupWikis.EditGroupWikiPage(1, "deploy", &EditGroupWikiPageOptions{
		Content: Ptr("content here"),
		Title:   Ptr("deploy title"),
		Format:  Ptr(WikiFormatRDoc),
	})
	if err != nil {
		t.Errorf("GroupWikis.EditGroupWikiPage returned error: %v", err)
	}

	want := &GroupWiki{
		Content: "content here",
		Format:  WikiFormatASCIIDoc,
		Slug:    "deploy",
		Title:   "deploy title",
	}
	if !reflect.DeepEqual(want, groupwiki) {
		t.Errorf("GroupWikis.EditGroupWikiPage returned %+v, want %+v", groupwiki, want)
	}
}

func TestDeleteGroupWikiPage(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1/wikis/deploy",
		func(w http.ResponseWriter, r *http.Request) {
			testMethod(t, r, http.MethodDelete)
			w.WriteHeader(204)
		})

	r, err := client.GroupWikis.DeleteGroupWikiPage(1, "deploy")
	if err != nil {
		t.Errorf("GroupWikis.DeleteGroupWikiPage returned error: %v", err)
	}
	if r.StatusCode != 204 {
		t.Errorf("GroupWikis.DeleteGroupWikiPage returned wrong status code %d != 204", r.StatusCode)
	}
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/groups.go000066400000000000000000001372571475761473200227440ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"time"

	retryablehttp "github.com/hashicorp/go-retryablehttp"
)

// GroupsService handles communication with the group related methods of
// the GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/groups.html
type GroupsService struct {
	client *Client
}

// Group represents a GitLab group.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/groups.html
type Group struct {
	ID                              int                        `json:"id"`
	Name                            string                     `json:"name"`
	Path                            string                     `json:"path"`
	Description                     string                     `json:"description"`
	MembershipLock                  bool                       `json:"membership_lock"`
	Visibility                      VisibilityValue            `json:"visibility"`
	LFSEnabled                      bool                       `json:"lfs_enabled"`
	DefaultBranch                   string                     `json:"default_branch"`
	DefaultBranchProtectionDefaults *BranchProtectionDefaults  `json:"default_branch_protection_defaults"`
	AvatarURL                       string                     `json:"avatar_url"`
	WebURL                          string                     `json:"web_url"`
	RequestAccessEnabled            bool                       `json:"request_access_enabled"`
	RepositoryStorage               string                     `json:"repository_storage"`
	FullName                        string                     `json:"full_name"`
	FullPath                        string                     `json:"full_path"`
	FileTemplateProjectID           int                        `json:"file_template_project_id"`
	ParentID                        int                        `json:"parent_id"`
	Projects                        []*Project                 `json:"projects"`
	Statistics                      *Statistics                `json:"statistics"`
	CustomAttributes                []*CustomAttribute         `json:"custom_attributes"`
	ShareWithGroupLock              bool                       `json:"share_with_group_lock"`
	RequireTwoFactorAuth            bool                       `json:"require_two_factor_authentication"`
	TwoFactorGracePeriod            int                        `json:"two_factor_grace_period"`
	ProjectCreationLevel            ProjectCreationLevelValue  `json:"project_creation_level"`
	AutoDevopsEnabled               bool                       `json:"auto_devops_enabled"`
	SubGroupCreationLevel           SubGroupCreationLevelValue `json:"subgroup_creation_level"`
	EmailsEnabled                   bool                       `json:"emails_enabled"`
	MentionsDisabled                bool                       `json:"mentions_disabled"`
	RunnersToken                    string                     `json:"runners_token"`
	SharedProjects                  []*Project                 `json:"shared_projects"`
	SharedRunnersSetting            SharedRunnersSettingValue  `json:"shared_runners_setting"`
	SharedWithGroups                []struct {
		GroupID          int      `json:"group_id"`
		GroupName        string   `json:"group_name"`
		GroupFullPath    string   `json:"group_full_path"`
		GroupAccessLevel int      `json:"group_access_level"`
		ExpiresAt        *ISOTime `json:"expires_at"`
		MemberRoleID     int      `json:"member_role_id"`
	} `json:"shared_with_groups"`
	LDAPCN                         string             `json:"ldap_cn"`
	LDAPAccess                     AccessLevelValue   `json:"ldap_access"`
	LDAPGroupLinks                 []*LDAPGroupLink   `json:"ldap_group_links"`
	SAMLGroupLinks                 []*SAMLGroupLink   `json:"saml_group_links"`
	SharedRunnersMinutesLimit      int                `json:"shared_runners_minutes_limit"`
	ExtraSharedRunnersMinutesLimit int                `json:"extra_shared_runners_minutes_limit"`
	PreventForkingOutsideGroup     bool               `json:"prevent_forking_outside_group"`
	MarkedForDeletionOn            *ISOTime           `json:"marked_for_deletion_on"`
	CreatedAt                      *time.Time         `json:"created_at"`
	IPRestrictionRanges            string             `json:"ip_restriction_ranges"`
	AllowedEmailDomainsList        string             `json:"allowed_email_domains_list"`
	WikiAccessLevel                AccessControlValue `json:"wiki_access_level"`

	// Deprecated: Use EmailsEnabled instead
	EmailsDisabled bool `json:"emails_disabled"`

	// Deprecated: Use DefaultBranchProtectionDefaults instead
	DefaultBranchProtection int `json:"default_branch_protection"`
}

// BranchProtectionDefaults represents default Git protected branch permissions.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/groups.html#options-for-default_branch_protection_defaults
type BranchProtectionDefaults struct {
	AllowedToPush           []*GroupAccessLevel `json:"allowed_to_push,omitempty"`
	AllowForcePush          bool                `json:"allow_force_push,omitempty"`
	AllowedToMerge          []*GroupAccessLevel `json:"allowed_to_merge,omitempty"`
	DeveloperCanInitialPush bool                `json:"developer_can_initial_push,omitempty"`
}

// GroupAccessLevel represents default branch protection defaults access levels.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/groups.html#options-for-default_branch_protection_defaults
type GroupAccessLevel struct {
	AccessLevel *AccessLevelValue `url:"access_level,omitempty" json:"access_level,omitempty"`
}

// GroupAvatar represents a GitLab group avatar.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/groups.html
type GroupAvatar struct {
	Filename string
	Image    io.Reader
}

// MarshalJSON implements the json.Marshaler interface.
func (a *GroupAvatar) MarshalJSON() ([]byte, error) {
	if a.Filename == "" && a.Image == nil {
		return []byte(`""`), nil
	}
	type alias GroupAvatar
	return json.Marshal((*alias)(a))
}

// LDAPGroupLink represents a GitLab LDAP group link.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/groups.html#ldap-group-links
type LDAPGroupLink struct {
	CN          string           `json:"cn"`
	Filter      string           `json:"filter"`
	GroupAccess AccessLevelValue `json:"group_access"`
	Provider    string           `json:"provider"`
}

// SAMLGroupLink represents a GitLab SAML group link.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/groups.html#saml-group-links
type SAMLGroupLink struct {
	Name         string           `json:"name"`
	AccessLevel  AccessLevelValue `json:"access_level"`
	MemberRoleID int              `json:"member_role_id,omitempty"`
}

// ListGroupsOptions represents the available ListGroups() options.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/groups.html#list-groups
type ListGroupsOptions struct {
	ListOptions
	SkipGroups           *[]int            `url:"skip_groups,omitempty" del:"," json:"skip_groups,omitempty"`
	AllAvailable         *bool             `url:"all_available,omitempty" json:"all_available,omitempty"`
	Search               *string           `url:"search,omitempty" json:"search,omitempty"`
	OrderBy              *string           `url:"order_by,omitempty" json:"order_by,omitempty"`
	Sort                 *string           `url:"sort,omitempty" json:"sort,omitempty"`
	Statistics           *bool             `url:"statistics,omitempty" json:"statistics,omitempty"`
	WithCustomAttributes *bool             `url:"with_custom_attributes,omitempty" json:"with_custom_attributes,omitempty"`
	Owned                *bool             `url:"owned,omitempty" json:"owned,omitempty"`
	MinAccessLevel       *AccessLevelValue `url:"min_access_level,omitempty" json:"min_access_level,omitempty"`
	TopLevelOnly         *bool             `url:"top_level_only,omitempty" json:"top_level_only,omitempty"`
	RepositoryStorage    *string           `url:"repository_storage,omitempty" json:"repository_storage,omitempty"`
}

// ListGroups gets a list of groups (as user: my groups, as admin: all groups).
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/groups.html#list-groups
func (s *GroupsService) ListGroups(opt *ListGroupsOptions, options ...RequestOptionFunc) ([]*Group, *Response, error) {
	req, err := s.client.NewRequest(http.MethodGet, "groups", opt, options)
	if err != nil {
		return nil, nil, err
	}

	var gs []*Group
	resp, err := s.client.Do(req, &gs)
	if err != nil {
		return nil, resp, err
	}

	return gs, resp, nil
}

// ListSubGroupsOptions represents the available ListSubGroups() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/groups.html#list-a-groups-subgroups
type ListSubGroupsOptions ListGroupsOptions

// ListSubGroups gets a list of subgroups for a given group.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/groups.html#list-a-groups-subgroups
func (s *GroupsService) ListSubGroups(gid interface{}, opt *ListSubGroupsOptions, options ...RequestOptionFunc) ([]*Group, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/subgroups", PathEscape(group))

	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var gs []*Group
	resp, err := s.client.Do(req, &gs)
	if err != nil {
		return nil, resp, err
	}

	return gs, resp, nil
}

// ListDescendantGroupsOptions represents the available ListDescendantGroups()
// options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/groups.html#list-a-groups-descendant-groups
type ListDescendantGroupsOptions ListGroupsOptions

// ListDescendantGroups gets a list of subgroups for a given project.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/groups.html#list-a-groups-descendant-groups
func (s *GroupsService) ListDescendantGroups(gid interface{}, opt *ListDescendantGroupsOptions, options ...RequestOptionFunc) ([]*Group, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/descendant_groups", PathEscape(group))

	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var gs []*Group
	resp, err := s.client.Do(req, &gs)
	if err != nil {
		return nil, resp, err
	}

	return gs, resp, nil
}

// ListGroupProjectsOptions represents the available ListGroup() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/groups.html#list-a-groups-projects
type ListGroupProjectsOptions struct {
	ListOptions
	Archived                 *bool             `url:"archived,omitempty" json:"archived,omitempty"`
	IncludeSubGroups         *bool             `url:"include_subgroups,omitempty" json:"include_subgroups,omitempty"`
	MinAccessLevel           *AccessLevelValue `url:"min_access_level,omitempty" json:"min_access_level,omitempty"`
	OrderBy                  *string           `url:"order_by,omitempty" json:"order_by,omitempty"`
	Owned                    *bool             `url:"owned,omitempty" json:"owned,omitempty"`
	Search                   *string           `url:"search,omitempty" json:"search,omitempty"`
	Simple                   *bool             `url:"simple,omitempty" json:"simple,omitempty"`
	Sort                     *string           `url:"sort,omitempty" json:"sort,omitempty"`
	Starred                  *bool             `url:"starred,omitempty" json:"starred,omitempty"`
	Topic                    *string           `url:"topic,omitempty" json:"topic,omitempty"`
	Visibility               *VisibilityValue  `url:"visibility,omitempty" json:"visibility,omitempty"`
	WithCustomAttributes     *bool             `url:"with_custom_attributes,omitempty" json:"with_custom_attributes,omitempty"`
	WithIssuesEnabled        *bool             `url:"with_issues_enabled,omitempty" json:"with_issues_enabled,omitempty"`
	WithMergeRequestsEnabled *bool             `url:"with_merge_requests_enabled,omitempty" json:"with_merge_requests_enabled,omitempty"`
	WithSecurityReports      *bool             `url:"with_security_reports,omitempty" json:"with_security_reports,omitempty"`
	WithShared               *bool             `url:"with_shared,omitempty" json:"with_shared,omitempty"`
}

// ListGroupProjects get a list of group projects
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/groups.html#list-a-groups-projects
func (s *GroupsService) ListGroupProjects(gid interface{}, opt *ListGroupProjectsOptions, options ...RequestOptionFunc) ([]*Project, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/projects", PathEscape(group))

	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var ps []*Project
	resp, err := s.client.Do(req, &ps)
	if err != nil {
		return nil, resp, err
	}

	return ps, resp, nil
}

// GetGroupOptions represents the available GetGroup() options.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/groups.html#details-of-a-group
type GetGroupOptions struct {
	ListOptions
	WithCustomAttributes *bool `url:"with_custom_attributes,omitempty" json:"with_custom_attributes,omitempty"`
	WithProjects         *bool `url:"with_projects,omitempty" json:"with_projects,omitempty"`
}

// GetGroup gets all details of a group.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/groups.html#details-of-a-group
func (s *GroupsService) GetGroup(gid interface{}, opt *GetGroupOptions, options ...RequestOptionFunc) (*Group, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s", PathEscape(group))

	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	g := new(Group)
	resp, err := s.client.Do(req, g)
	if err != nil {
		return nil, resp, err
	}

	return g, resp, nil
}

// DownloadAvatar downloads a group avatar.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/groups.html#download-a-group-avatar
func (s *GroupsService) DownloadAvatar(gid interface{}, options ...RequestOptionFunc) (*bytes.Reader, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/avatar", PathEscape(group))

	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	avatar := new(bytes.Buffer)
	resp, err := s.client.Do(req, avatar)
	if err != nil {
		return nil, resp, err
	}

	return bytes.NewReader(avatar.Bytes()), resp, err
}

// CreateGroupOptions represents the available CreateGroup() options.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/groups.html#new-group
type CreateGroupOptions struct {
	Name                            *string                                 `url:"name,omitempty" json:"name,omitempty"`
	Path                            *string                                 `url:"path,omitempty" json:"path,omitempty"`
	Avatar                          *GroupAvatar                            `url:"-" json:"-"`
	DefaultBranch                   *string                                 `url:"default_branch,omitempty" json:"default_branch,omitempty"`
	Description                     *string                                 `url:"description,omitempty" json:"description,omitempty"`
	MembershipLock                  *bool                                   `url:"membership_lock,omitempty" json:"membership_lock,omitempty"`
	Visibility                      *VisibilityValue                        `url:"visibility,omitempty" json:"visibility,omitempty"`
	ShareWithGroupLock              *bool                                   `url:"share_with_group_lock,omitempty" json:"share_with_group_lock,omitempty"`
	RequireTwoFactorAuth            *bool                                   `url:"require_two_factor_authentication,omitempty" json:"require_two_factor_authentication,omitempty"`
	TwoFactorGracePeriod            *int                                    `url:"two_factor_grace_period,omitempty" json:"two_factor_grace_period,omitempty"`
	ProjectCreationLevel            *ProjectCreationLevelValue              `url:"project_creation_level,omitempty" json:"project_creation_level,omitempty"`
	AutoDevopsEnabled               *bool                                   `url:"auto_devops_enabled,omitempty" json:"auto_devops_enabled,omitempty"`
	SubGroupCreationLevel           *SubGroupCreationLevelValue             `url:"subgroup_creation_level,omitempty" json:"subgroup_creation_level,omitempty"`
	EmailsEnabled                   *bool                                   `url:"emails_enabled,omitempty" json:"emails_enabled,omitempty"`
	MentionsDisabled                *bool                                   `url:"mentions_disabled,omitempty" json:"mentions_disabled,omitempty"`
	LFSEnabled                      *bool                                   `url:"lfs_enabled,omitempty" json:"lfs_enabled,omitempty"`
	DefaultBranchProtectionDefaults *DefaultBranchProtectionDefaultsOptions `url:"default_branch_protection_defaults,omitempty" json:"default_branch_protection_defaults,omitempty"`
	RequestAccessEnabled            *bool                                   `url:"request_access_enabled,omitempty" json:"request_access_enabled,omitempty"`
	ParentID                        *int                                    `url:"parent_id,omitempty" json:"parent_id,omitempty"`
	SharedRunnersMinutesLimit       *int                                    `url:"shared_runners_minutes_limit,omitempty" json:"shared_runners_minutes_limit,omitempty"`
	ExtraSharedRunnersMinutesLimit  *int                                    `url:"extra_shared_runners_minutes_limit,omitempty" json:"extra_shared_runners_minutes_limit,omitempty"`
	WikiAccessLevel                 *AccessControlValue                     `url:"wiki_access_level,omitempty" json:"wiki_access_level,omitempty"`

	// Deprecated: Use EmailsEnabled instead
	EmailsDisabled *bool `url:"emails_disabled,omitempty" json:"emails_disabled,omitempty"`

	// Deprecated: User DefaultBranchProtectionDefaults instead
	DefaultBranchProtection *int `url:"default_branch_protection,omitempty" json:"default_branch_protection,omitempty"`
}

// DefaultBranchProtectionDefaultsOptions represents the available options for
// using default_branch_protection_defaults in CreateGroup() or UpdateGroup()
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/groups.html#options-for-default_branch_protection_defaults
type DefaultBranchProtectionDefaultsOptions struct {
	AllowedToPush           *[]*GroupAccessLevel `url:"allowed_to_push,omitempty" json:"allowed_to_push,omitempty"`
	AllowForcePush          *bool                `url:"allow_force_push,omitempty" json:"allow_force_push,omitempty"`
	AllowedToMerge          *[]*GroupAccessLevel `url:"allowed_to_merge,omitempty" json:"allowed_to_merge,omitempty"`
	DeveloperCanInitialPush *bool                `url:"developer_can_initial_push,omitempty" json:"developer_can_initial_push,omitempty"`
}

// CreateGroup creates a new project group. Available only for users who can
// create groups.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/groups.html#new-group
func (s *GroupsService) CreateGroup(opt *CreateGroupOptions, options ...RequestOptionFunc) (*Group, *Response, error) {
	var err error
	var req *retryablehttp.Request

	if opt.Avatar == nil {
		req, err = s.client.NewRequest(http.MethodPost, "groups", opt, options)
	} else {
		req, err = s.client.UploadRequest(
			http.MethodPost,
			"groups",
			opt.Avatar.Image,
			opt.Avatar.Filename,
			UploadAvatar,
			opt,
			options,
		)
	}
	if err != nil {
		return nil, nil, err
	}

	g := new(Group)
	resp, err := s.client.Do(req, g)
	if err != nil {
		return nil, resp, err
	}

	return g, resp, nil
}

// TransferGroup transfers a project to the Group namespace. Available only
// for admin.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/groups.html#transfer-project-to-group
func (s *GroupsService) TransferGroup(gid interface{}, pid interface{}, options ...RequestOptionFunc) (*Group, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/projects/%s", PathEscape(group), PathEscape(project))

	req, err := s.client.NewRequest(http.MethodPost, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	g := new(Group)
	resp, err := s.client.Do(req, g)
	if err != nil {
		return nil, resp, err
	}

	return g, resp, nil
}

// TransferSubGroupOptions represents the available TransferSubGroup() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/groups.html#transfer-a-group-to-a-new-parent-group--turn-a-subgroup-to-a-top-level-group
type TransferSubGroupOptions struct {
	GroupID *int `url:"group_id,omitempty" json:"group_id,omitempty"`
}

// TransferSubGroup transfers a group to a new parent group or turn a subgroup
// to a top-level group. Available to administrators and users.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/groups.html#transfer-a-group-to-a-new-parent-group--turn-a-subgroup-to-a-top-level-group
func (s *GroupsService) TransferSubGroup(gid interface{}, opt *TransferSubGroupOptions, options ...RequestOptionFunc) (*Group, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/transfer", PathEscape(group))

	req, err := s.client.NewRequest(http.MethodPost, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	g := new(Group)
	resp, err := s.client.Do(req, g)
	if err != nil {
		return nil, resp, err
	}

	return g, resp, nil
}

// UpdateGroupOptions represents the available UpdateGroup() options.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/groups.html#update-group
type UpdateGroupOptions struct {
	Name                                 *string                                 `url:"name,omitempty" json:"name,omitempty"`
	Path                                 *string                                 `url:"path,omitempty" json:"path,omitempty"`
	Avatar                               *GroupAvatar                            `url:"-" json:"avatar,omitempty"`
	DefaultBranch                        *string                                 `url:"default_branch,omitempty" json:"default_branch,omitempty"`
	Description                          *string                                 `url:"description,omitempty" json:"description,omitempty"`
	MembershipLock                       *bool                                   `url:"membership_lock,omitempty" json:"membership_lock,omitempty"`
	Visibility                           *VisibilityValue                        `url:"visibility,omitempty" json:"visibility,omitempty"`
	ShareWithGroupLock                   *bool                                   `url:"share_with_group_lock,omitempty" json:"share_with_group_lock,omitempty"`
	RequireTwoFactorAuth                 *bool                                   `url:"require_two_factor_authentication,omitempty" json:"require_two_factor_authentication,omitempty"`
	TwoFactorGracePeriod                 *int                                    `url:"two_factor_grace_period,omitempty" json:"two_factor_grace_period,omitempty"`
	ProjectCreationLevel                 *ProjectCreationLevelValue              `url:"project_creation_level,omitempty" json:"project_creation_level,omitempty"`
	AutoDevopsEnabled                    *bool                                   `url:"auto_devops_enabled,omitempty" json:"auto_devops_enabled,omitempty"`
	SubGroupCreationLevel                *SubGroupCreationLevelValue             `url:"subgroup_creation_level,omitempty" json:"subgroup_creation_level,omitempty"`
	EmailsEnabled                        *bool                                   `url:"emails_enabled,omitempty" json:"emails_enabled,omitempty"`
	MentionsDisabled                     *bool                                   `url:"mentions_disabled,omitempty" json:"mentions_disabled,omitempty"`
	LFSEnabled                           *bool                                   `url:"lfs_enabled,omitempty" json:"lfs_enabled,omitempty"`
	RequestAccessEnabled                 *bool                                   `url:"request_access_enabled,omitempty" json:"request_access_enabled,omitempty"`
	DefaultBranchProtectionDefaults      *DefaultBranchProtectionDefaultsOptions `url:"default_branch_protection_defaults,omitempty" json:"default_branch_protection_defaults,omitempty"`
	FileTemplateProjectID                *int                                    `url:"file_template_project_id,omitempty" json:"file_template_project_id,omitempty"`
	SharedRunnersMinutesLimit            *int                                    `url:"shared_runners_minutes_limit,omitempty" json:"shared_runners_minutes_limit,omitempty"`
	ExtraSharedRunnersMinutesLimit       *int                                    `url:"extra_shared_runners_minutes_limit,omitempty" json:"extra_shared_runners_minutes_limit,omitempty"`
	PreventForkingOutsideGroup           *bool                                   `url:"prevent_forking_outside_group,omitempty" json:"prevent_forking_outside_group,omitempty"`
	SharedRunnersSetting                 *SharedRunnersSettingValue              `url:"shared_runners_setting,omitempty" json:"shared_runners_setting,omitempty"`
	PreventSharingGroupsOutsideHierarchy *bool                                   `url:"prevent_sharing_groups_outside_hierarchy,omitempty" json:"prevent_sharing_groups_outside_hierarchy,omitempty"`
	IPRestrictionRanges                  *string                                 `url:"ip_restriction_ranges,omitempty" json:"ip_restriction_ranges,omitempty"`
	AllowedEmailDomainsList              *string                                 `url:"allowed_email_domains_list,omitempty" json:"allowed_email_domains_list,omitempty"`
	WikiAccessLevel                      *AccessControlValue                     `url:"wiki_access_level,omitempty" json:"wiki_access_level,omitempty"`

	// Deprecated: Use EmailsEnabled instead
	EmailsDisabled *bool `url:"emails_disabled,omitempty" json:"emails_disabled,omitempty"`

	// Deprecated: Use DefaultBranchProtectionDefaults instead
	DefaultBranchProtection *int `url:"default_branch_protection,omitempty" json:"default_branch_protection,omitempty"`
}

// UpdateGroup updates an existing group; only available to group owners and
// administrators.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/groups.html#update-group
func (s *GroupsService) UpdateGroup(gid interface{}, opt *UpdateGroupOptions, options ...RequestOptionFunc) (*Group, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s", PathEscape(group))

	var req *retryablehttp.Request

	if opt.Avatar == nil || (opt.Avatar.Filename == "" && opt.Avatar.Image == nil) {
		req, err = s.client.NewRequest(http.MethodPut, u, opt, options)
	} else {
		req, err = s.client.UploadRequest(
			http.MethodPut,
			u,
			opt.Avatar.Image,
			opt.Avatar.Filename,
			UploadAvatar,
			opt,
			options,
		)
	}
	if err != nil {
		return nil, nil, err
	}

	g := new(Group)
	resp, err := s.client.Do(req, g)
	if err != nil {
		return nil, resp, err
	}

	return g, resp, nil
}

// UploadAvatar uploads a group avatar.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/groups.html#upload-a-group-avatar
func (s *GroupsService) UploadAvatar(gid interface{}, avatar io.Reader, filename string, options ...RequestOptionFunc) (*Group, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s", PathEscape(group))

	req, err := s.client.UploadRequest(
		http.MethodPut,
		u,
		avatar,
		filename,
		UploadAvatar,
		nil,
		options,
	)
	if err != nil {
		return nil, nil, err
	}

	g := new(Group)
	resp, err := s.client.Do(req, g)
	if err != nil {
		return nil, resp, err
	}

	return g, resp, nil
}

// DeleteGroupOptions represents the available DeleteGroup() options.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/groups.html#update-group
type DeleteGroupOptions struct {
	PermanentlyRemove *bool   `url:"permanently_remove,omitempty" json:"permanently_remove,omitempty"`
	FullPath          *string `url:"full_path,omitempty" json:"full_path,omitempty"`
}

// DeleteGroup removes group with all projects inside.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/groups.html#remove-group
func (s *GroupsService) DeleteGroup(gid interface{}, opt *DeleteGroupOptions, options ...RequestOptionFunc) (*Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("groups/%s", PathEscape(group))

	req, err := s.client.NewRequest(http.MethodDelete, u, opt, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}

// RestoreGroup restores a previously deleted group
//
// GitLap API docs:
// https://docs.gitlab.com/ee/api/groups.html#restore-group-marked-for-deletion
func (s *GroupsService) RestoreGroup(gid interface{}, options ...RequestOptionFunc) (*Group, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/restore", PathEscape(group))

	req, err := s.client.NewRequest(http.MethodPost, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	g := new(Group)
	resp, err := s.client.Do(req, g)
	if err != nil {
		return nil, resp, err
	}

	return g, resp, nil
}

// SearchGroup get all groups that match your string in their name or path.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/groups.html#search-for-group
func (s *GroupsService) SearchGroup(query string, options ...RequestOptionFunc) ([]*Group, *Response, error) {
	var q struct {
		Search string `url:"search,omitempty" json:"search,omitempty"`
	}
	q.Search = query

	req, err := s.client.NewRequest(http.MethodGet, "groups", &q, options)
	if err != nil {
		return nil, nil, err
	}

	var gs []*Group
	resp, err := s.client.Do(req, &gs)
	if err != nil {
		return nil, resp, err
	}

	return gs, resp, nil
}

// ListProvisionedUsersOptions represents the available ListProvisionedUsers()
// options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/groups.html#list-provisioned-users
type ListProvisionedUsersOptions struct {
	ListOptions
	Username      *string    `url:"username,omitempty" json:"username,omitempty"`
	Search        *string    `url:"search,omitempty" json:"search,omitempty"`
	Active        *bool      `url:"active,omitempty" json:"active,omitempty"`
	Blocked       *bool      `url:"blocked,omitempty" json:"blocked,omitempty"`
	CreatedAfter  *time.Time `url:"created_after,omitempty" json:"created_after,omitempty"`
	CreatedBefore *time.Time `url:"created_before,omitempty" json:"created_before,omitempty"`
}

// ListProvisionedUsers gets a list of users provisioned by the given group.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/groups.html#list-provisioned-users
func (s *GroupsService) ListProvisionedUsers(gid interface{}, opt *ListProvisionedUsersOptions, options ...RequestOptionFunc) ([]*User, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/provisioned_users", PathEscape(group))

	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var us []*User
	resp, err := s.client.Do(req, &us)
	if err != nil {
		return nil, resp, err
	}

	return us, resp, nil
}

// ListGroupLDAPLinks lists the group's LDAP links. Available only for users who
// can edit groups.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/groups.html#list-ldap-group-links
func (s *GroupsService) ListGroupLDAPLinks(gid interface{}, options ...RequestOptionFunc) ([]*LDAPGroupLink, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/ldap_group_links", PathEscape(group))

	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	var gls []*LDAPGroupLink
	resp, err := s.client.Do(req, &gls)
	if err != nil {
		return nil, resp, err
	}

	return gls, resp, nil
}

// AddGroupLDAPLinkOptions represents the available AddGroupLDAPLink() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/groups.html#add-ldap-group-link-with-cn-or-filter
type AddGroupLDAPLinkOptions struct {
	CN          *string           `url:"cn,omitempty" json:"cn,omitempty"`
	Filter      *string           `url:"filter,omitempty" json:"filter,omitempty"`
	GroupAccess *AccessLevelValue `url:"group_access,omitempty" json:"group_access,omitempty"`
	Provider    *string           `url:"provider,omitempty" json:"provider,omitempty"`
}

// DeleteGroupLDAPLinkWithCNOrFilterOptions represents the available DeleteGroupLDAPLinkWithCNOrFilter() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/groups.html#delete-ldap-group-link-with-cn-or-filter
type DeleteGroupLDAPLinkWithCNOrFilterOptions struct {
	CN       *string `url:"cn,omitempty" json:"cn,omitempty"`
	Filter   *string `url:"filter,omitempty" json:"filter,omitempty"`
	Provider *string `url:"provider,omitempty" json:"provider,omitempty"`
}

// AddGroupLDAPLink creates a new group LDAP link. Available only for users who
// can edit groups.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/groups.html#add-ldap-group-link-with-cn-or-filter
func (s *GroupsService) AddGroupLDAPLink(gid interface{}, opt *AddGroupLDAPLinkOptions, options ...RequestOptionFunc) (*LDAPGroupLink, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/ldap_group_links", PathEscape(group))

	req, err := s.client.NewRequest(http.MethodPost, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	gl := new(LDAPGroupLink)
	resp, err := s.client.Do(req, gl)
	if err != nil {
		return nil, resp, err
	}

	return gl, resp, nil
}

// DeleteGroupLDAPLink deletes a group LDAP link. Available only for users who
// can edit groups.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/groups.html#delete-ldap-group-link
func (s *GroupsService) DeleteGroupLDAPLink(gid interface{}, cn string, options ...RequestOptionFunc) (*Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("groups/%s/ldap_group_links/%s", PathEscape(group), PathEscape(cn))

	req, err := s.client.NewRequest(http.MethodDelete, u, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}

// DeleteGroupLDAPLinkWithCNOrFilter deletes a group LDAP link. Available only for users who
// can edit groups.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/groups.html#delete-ldap-group-link-with-cn-or-filter
func (s *GroupsService) DeleteGroupLDAPLinkWithCNOrFilter(gid interface{}, opts *DeleteGroupLDAPLinkWithCNOrFilterOptions, options ...RequestOptionFunc) (*Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("groups/%s/ldap_group_links", PathEscape(group))

	req, err := s.client.NewRequest(http.MethodDelete, u, opts, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}

// DeleteGroupLDAPLinkForProvider deletes a group LDAP link from a specific
// provider. Available only for users who can edit groups.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/groups.html#delete-ldap-group-link
func (s *GroupsService) DeleteGroupLDAPLinkForProvider(gid interface{}, provider, cn string, options ...RequestOptionFunc) (*Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf(
		"groups/%s/ldap_group_links/%s/%s",
		PathEscape(group),
		PathEscape(provider),
		PathEscape(cn),
	)

	req, err := s.client.NewRequest(http.MethodDelete, u, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}

// ListGroupSAMLLinks lists the group's SAML links. Available only for users who
// can edit groups.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/groups.html#list-saml-group-links
func (s *GroupsService) ListGroupSAMLLinks(gid interface{}, options ...RequestOptionFunc) ([]*SAMLGroupLink, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/saml_group_links", PathEscape(group))

	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	var gl []*SAMLGroupLink
	resp, err := s.client.Do(req, &gl)
	if err != nil {
		return nil, resp, err
	}

	return gl, resp, nil
}

// GetGroupSAMLLink get a specific group SAML link. Available only for users who
// can edit groups.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/groups.html#get-saml-group-link
func (s *GroupsService) GetGroupSAMLLink(gid interface{}, samlGroupName string, options ...RequestOptionFunc) (*SAMLGroupLink, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/saml_group_links/%s", PathEscape(group), PathEscape(samlGroupName))

	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	gl := new(SAMLGroupLink)
	resp, err := s.client.Do(req, &gl)
	if err != nil {
		return nil, resp, err
	}

	return gl, resp, nil
}

// AddGroupSAMLLinkOptions represents the available AddGroupSAMLLink() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/groups.html#add-saml-group-link
type AddGroupSAMLLinkOptions struct {
	SAMLGroupName *string           `url:"saml_group_name,omitempty" json:"saml_group_name,omitempty"`
	AccessLevel   *AccessLevelValue `url:"access_level,omitempty" json:"access_level,omitempty"`
	MemberRoleID  *int              `url:"member_role_id,omitempty" json:"member_role_id,omitempty"`
}

// AddGroupSAMLLink creates a new group SAML link. Available only for users who
// can edit groups.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/groups.html#add-saml-group-link
func (s *GroupsService) AddGroupSAMLLink(gid interface{}, opt *AddGroupSAMLLinkOptions, options ...RequestOptionFunc) (*SAMLGroupLink, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/saml_group_links", PathEscape(group))

	req, err := s.client.NewRequest(http.MethodPost, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	gl := new(SAMLGroupLink)
	resp, err := s.client.Do(req, &gl)
	if err != nil {
		return nil, resp, err
	}

	return gl, resp, nil
}

// DeleteGroupSAMLLink deletes a group SAML link. Available only for users who
// can edit groups.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/groups.html#delete-saml-group-link
func (s *GroupsService) DeleteGroupSAMLLink(gid interface{}, samlGroupName string, options ...RequestOptionFunc) (*Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("groups/%s/saml_group_links/%s", PathEscape(group), PathEscape(samlGroupName))

	req, err := s.client.NewRequest(http.MethodDelete, u, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}

// ShareGroupWithGroupOptions represents the available ShareGroupWithGroup() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/groups.html#share-groups-with-groups
type ShareGroupWithGroupOptions struct {
	GroupID      *int              `url:"group_id,omitempty" json:"group_id,omitempty"`
	GroupAccess  *AccessLevelValue `url:"group_access,omitempty" json:"group_access,omitempty"`
	ExpiresAt    *ISOTime          `url:"expires_at,omitempty" json:"expires_at,omitempty"`
	MemberRoleID *int              `url:"member_role_id,omitempty" json:"member_role_id,omitempty"`
}

// ShareGroupWithGroup shares a group with another group.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/groups.html#create-a-link-to-share-a-group-with-another-group
func (s *GroupsService) ShareGroupWithGroup(gid interface{}, opt *ShareGroupWithGroupOptions, options ...RequestOptionFunc) (*Group, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/share", PathEscape(group))

	req, err := s.client.NewRequest(http.MethodPost, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	g := new(Group)
	resp, err := s.client.Do(req, g)
	if err != nil {
		return nil, resp, err
	}

	return g, resp, nil
}

// UnshareGroupFromGroup unshares a group from another group.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/groups.html#delete-link-sharing-group-with-another-group
func (s *GroupsService) UnshareGroupFromGroup(gid interface{}, groupID int, options ...RequestOptionFunc) (*Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("groups/%s/share/%d", PathEscape(group), groupID)

	req, err := s.client.NewRequest(http.MethodDelete, u, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}

// GroupPushRules represents a group push rule.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/groups.html#get-group-push-rules
type GroupPushRules struct {
	ID                         int        `json:"id"`
	CreatedAt                  *time.Time `json:"created_at"`
	CommitMessageRegex         string     `json:"commit_message_regex"`
	CommitMessageNegativeRegex string     `json:"commit_message_negative_regex"`
	BranchNameRegex            string     `json:"branch_name_regex"`
	DenyDeleteTag              bool       `json:"deny_delete_tag"`
	MemberCheck                bool       `json:"member_check"`
	PreventSecrets             bool       `json:"prevent_secrets"`
	AuthorEmailRegex           string     `json:"author_email_regex"`
	FileNameRegex              string     `json:"file_name_regex"`
	MaxFileSize                int        `json:"max_file_size"`
	CommitCommitterCheck       bool       `json:"commit_committer_check"`
	CommitCommitterNameCheck   bool       `json:"commit_committer_name_check"`
	RejectUnsignedCommits      bool       `json:"reject_unsigned_commits"`
	RejectNonDCOCommits        bool       `json:"reject_non_dco_commits"`
}

// GetGroupPushRules gets the push rules of a group.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/groups.html#get-group-push-rules
func (s *GroupsService) GetGroupPushRules(gid interface{}, options ...RequestOptionFunc) (*GroupPushRules, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/push_rule", PathEscape(group))

	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	gpr := new(GroupPushRules)
	resp, err := s.client.Do(req, gpr)
	if err != nil {
		return nil, resp, err
	}

	return gpr, resp, nil
}

// AddGroupPushRuleOptions represents the available AddGroupPushRule()
// options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/groups.html#add-group-push-rule
type AddGroupPushRuleOptions struct {
	AuthorEmailRegex           *string `url:"author_email_regex,omitempty" json:"author_email_regex,omitempty"`
	BranchNameRegex            *string `url:"branch_name_regex,omitempty" json:"branch_name_regex,omitempty"`
	CommitCommitterCheck       *bool   `url:"commit_committer_check,omitempty" json:"commit_committer_check,omitempty"`
	CommitCommitterNameCheck   *bool   `url:"commit_committer_name_check,omitempty" json:"commit_committer_name_check,omitempty"`
	CommitMessageNegativeRegex *string `url:"commit_message_negative_regex,omitempty" json:"commit_message_negative_regex,omitempty"`
	CommitMessageRegex         *string `url:"commit_message_regex,omitempty" json:"commit_message_regex,omitempty"`
	DenyDeleteTag              *bool   `url:"deny_delete_tag,omitempty" json:"deny_delete_tag,omitempty"`
	FileNameRegex              *string `url:"file_name_regex,omitempty" json:"file_name_regex,omitempty"`
	MaxFileSize                *int    `url:"max_file_size,omitempty" json:"max_file_size,omitempty"`
	MemberCheck                *bool   `url:"member_check,omitempty" json:"member_check,omitempty"`
	PreventSecrets             *bool   `url:"prevent_secrets,omitempty" json:"prevent_secrets,omitempty"`
	RejectUnsignedCommits      *bool   `url:"reject_unsigned_commits,omitempty" json:"reject_unsigned_commits,omitempty"`
	RejectNonDCOCommits        *bool   `url:"reject_non_dco_commits,omitempty" json:"reject_non_dco_commits,omitempty"`
}

// AddGroupPushRule adds push rules to the specified group.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/groups.html#add-group-push-rule
func (s *GroupsService) AddGroupPushRule(gid interface{}, opt *AddGroupPushRuleOptions, options ...RequestOptionFunc) (*GroupPushRules, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/push_rule", PathEscape(group))

	req, err := s.client.NewRequest(http.MethodPost, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	gpr := new(GroupPushRules)
	resp, err := s.client.Do(req, gpr)
	if err != nil {
		return nil, resp, err
	}

	return gpr, resp, nil
}

// EditGroupPushRuleOptions represents the available EditGroupPushRule()
// options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/groups.html#edit-group-push-rule
type EditGroupPushRuleOptions struct {
	AuthorEmailRegex           *string `url:"author_email_regex,omitempty" json:"author_email_regex,omitempty"`
	BranchNameRegex            *string `url:"branch_name_regex,omitempty" json:"branch_name_regex,omitempty"`
	CommitCommitterCheck       *bool   `url:"commit_committer_check,omitempty" json:"commit_committer_check,omitempty"`
	CommitCommitterNameCheck   *bool   `url:"commit_committer_name_check,omitempty" json:"commit_committer_name_check,omitempty"`
	CommitMessageNegativeRegex *string `url:"commit_message_negative_regex,omitempty" json:"commit_message_negative_regex,omitempty"`
	CommitMessageRegex         *string `url:"commit_message_regex,omitempty" json:"commit_message_regex,omitempty"`
	DenyDeleteTag              *bool   `url:"deny_delete_tag,omitempty" json:"deny_delete_tag,omitempty"`
	FileNameRegex              *string `url:"file_name_regex,omitempty" json:"file_name_regex,omitempty"`
	MaxFileSize                *int    `url:"max_file_size,omitempty" json:"max_file_size,omitempty"`
	MemberCheck                *bool   `url:"member_check,omitempty" json:"member_check,omitempty"`
	PreventSecrets             *bool   `url:"prevent_secrets,omitempty" json:"prevent_secrets,omitempty"`
	RejectUnsignedCommits      *bool   `url:"reject_unsigned_commits,omitempty" json:"reject_unsigned_commits,omitempty"`
	RejectNonDCOCommits        *bool   `url:"reject_non_dco_commits,omitempty" json:"reject_non_dco_commits,omitempty"`
}

// EditGroupPushRule edits a push rule for a specified group.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/groups.html#edit-group-push-rule
func (s *GroupsService) EditGroupPushRule(gid interface{}, opt *EditGroupPushRuleOptions, options ...RequestOptionFunc) (*GroupPushRules, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/push_rule", PathEscape(group))

	req, err := s.client.NewRequest(http.MethodPut, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	gpr := new(GroupPushRules)
	resp, err := s.client.Do(req, gpr)
	if err != nil {
		return nil, resp, err
	}

	return gpr, resp, nil
}

// DeleteGroupPushRule deletes the push rules of a group.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/groups.html#delete-group-push-rule
func (s *GroupsService) DeleteGroupPushRule(gid interface{}, options ...RequestOptionFunc) (*Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("groups/%s/push_rule", PathEscape(group))

	req, err := s.client.NewRequest(http.MethodDelete, u, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/groups_test.go000066400000000000000000001004211475761473200237620ustar00rootroot00000000000000package gitlab

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"reflect"
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestListGroups(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups",
		func(w http.ResponseWriter, r *http.Request) {
			testMethod(t, r, http.MethodGet)
			fmt.Fprint(w, `[{"id":1},{"id":2}]`)
		})

	groups, _, err := client.Groups.ListGroups(&ListGroupsOptions{})
	if err != nil {
		t.Errorf("Groups.ListGroups returned error: %v", err)
	}

	want := []*Group{{ID: 1}, {ID: 2}}
	if !reflect.DeepEqual(want, groups) {
		t.Errorf("Groups.ListGroups returned %+v, want %+v", groups, want)
	}
}

func TestGetGroup(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/g",
		func(w http.ResponseWriter, r *http.Request) {
			testMethod(t, r, http.MethodGet)
			fmt.Fprint(w, `
			{
			"id": 1, 
			"name": "g", 
			"default_branch": "branch",
			"shared_with_groups": [
				{
				"group_id": 1,
				"group_name": "whiskers delicious",
				"member_role_id": 2
				}
			]
			}
		`)
		})

	group, _, err := client.Groups.GetGroup("g", &GetGroupOptions{})
	if err != nil {
		t.Errorf("Groups.GetGroup returned error: %v", err)
	}

	// Create the group shares struct to test.
	// We need to re-declare the struct here since it's an anonymous struct in the upstream struct.
	shares := []struct {
		GroupID          int      `json:"group_id"`
		GroupName        string   `json:"group_name"`
		GroupFullPath    string   `json:"group_full_path"`
		GroupAccessLevel int      `json:"group_access_level"`
		ExpiresAt        *ISOTime `json:"expires_at"`
		MemberRoleID     int      `json:"member_role_id"`
	}{
		{
			GroupID:      1,
			GroupName:    "whiskers delicious",
			MemberRoleID: 2,
		},
	}
	want := &Group{ID: 1, Name: "g", DefaultBranch: "branch", SharedWithGroups: shares}

	if !reflect.DeepEqual(want, group) {
		t.Errorf("Groups.GetGroup returned %+v, want %+v", group, want)
	}
}

func TestGetGroupWithFileTemplateId(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/g",
		func(w http.ResponseWriter, r *http.Request) {
			testMethod(t, r, http.MethodGet)
			fmt.Fprint(w, `{"id": 1, "name": "g","file_template_project_id": 12345}`)
		})

	group, _, err := client.Groups.GetGroup("g", &GetGroupOptions{})
	if err != nil {
		t.Errorf("Groups.GetGroup returned error: %v", err)
	}

	want := &Group{ID: 1, Name: "g", FileTemplateProjectID: 12345}
	if !reflect.DeepEqual(want, group) {
		t.Errorf("Groups.GetGroup returned %+v, want %+v", group, want)
	}
}

func TestCreateGroup(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups",
		func(w http.ResponseWriter, r *http.Request) {
			testMethod(t, r, http.MethodPost)
			fmt.Fprint(w, `{"id": 1, "name": "g", "path": "g"}`)
		})

	opt := &CreateGroupOptions{
		Name: Ptr("g"),
		Path: Ptr("g"),
	}

	group, _, err := client.Groups.CreateGroup(opt, nil)
	if err != nil {
		t.Errorf("Groups.CreateGroup returned error: %v", err)
	}

	want := &Group{ID: 1, Name: "g", Path: "g"}
	if !reflect.DeepEqual(want, group) {
		t.Errorf("Groups.CreateGroup returned %+v, want %+v", group, want)
	}
}

func TestCreateGroupWithDefaultBranch(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups",
		func(w http.ResponseWriter, r *http.Request) {
			testMethod(t, r, http.MethodPost)
			fmt.Fprint(w, `{"id": 1, "name": "g", "path": "g", "default_branch": "branch"}`)
		})

	opt := &CreateGroupOptions{
		Name:          Ptr("g"),
		Path:          Ptr("g"),
		DefaultBranch: Ptr("branch"),
	}

	group, _, err := client.Groups.CreateGroup(opt, nil)
	if err != nil {
		t.Errorf("Groups.CreateGroup returned error: %v", err)
	}

	want := &Group{ID: 1, Name: "g", Path: "g", DefaultBranch: "branch"}
	if !reflect.DeepEqual(want, group) {
		t.Errorf("Groups.CreateGroup returned %+v, want %+v", group, want)
	}
}

func TestCreateGroupDefaultBranchSettings(t *testing.T) {
	mux, client := setup(t)

	var jsonRequestBody CreateGroupOptions
	mux.HandleFunc("/api/v4/groups",
		func(w http.ResponseWriter, r *http.Request) {
			testMethod(t, r, http.MethodPost)

			testData, _ := io.ReadAll(r.Body)
			err := json.Unmarshal(testData, &jsonRequestBody)
			if err != nil {
				t.Fatal("Failed to unmarshal request body into an interface.")
			}

			fmt.Fprint(w, `
			{
				"id": 1,
				"name": "g",
				"path": "g",
				"default_branch_protection_defaults": {
					"allowed_to_push": [
						{
							"access_level": 40
						}
					],
					"allow_force_push": false,
					"allowed_to_merge": [
						{
							"access_level": 40
						}
					]
				}
			}
			`)
		})

	opt := &CreateGroupOptions{
		Name: Ptr("g"),
		Path: Ptr("g"),
		DefaultBranchProtectionDefaults: &DefaultBranchProtectionDefaultsOptions{
			AllowedToPush: &[]*GroupAccessLevel{
				{
					AccessLevel: Ptr(AccessLevelValue(40)),
				},
			},
			AllowedToMerge: &[]*GroupAccessLevel{
				{
					AccessLevel: Ptr(AccessLevelValue(40)),
				},
			},
		},
	}

	group, _, err := client.Groups.CreateGroup(opt, nil)
	if err != nil {
		t.Errorf("Groups.CreateGroup returned error: %v", err)
	}

	// Create the group that we want to get back
	want := &Group{
		ID:   1,
		Name: "g",
		Path: "g",
		DefaultBranchProtectionDefaults: &BranchProtectionDefaults{
			AllowedToMerge: []*GroupAccessLevel{
				{
					AccessLevel: Ptr(MaintainerPermissions),
				},
			},
			AllowedToPush: []*GroupAccessLevel{
				{
					AccessLevel: Ptr(MaintainerPermissions),
				},
			},
		},
	}

	if !reflect.DeepEqual(want, group) {
		t.Errorf("Groups.CreateGroup returned %+v, want %+v", group, want)
	}

	// Validate the request does what we want it to
	allowedToMerge := *jsonRequestBody.DefaultBranchProtectionDefaults.AllowedToMerge
	allowedToPush := *jsonRequestBody.DefaultBranchProtectionDefaults.AllowedToPush
	assert.Equal(t, Ptr(MaintainerPermissions), allowedToMerge[0].AccessLevel)
	assert.Equal(t, Ptr(MaintainerPermissions), allowedToPush[0].AccessLevel)
}

func TestTransferGroup(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1/projects/2",
		func(w http.ResponseWriter, r *http.Request) {
			testMethod(t, r, http.MethodPost)
			fmt.Fprintf(w, `{"id": 1}`)
		})

	group, _, err := client.Groups.TransferGroup(1, 2)
	if err != nil {
		t.Errorf("Groups.TransferGroup returned error: %v", err)
	}

	want := &Group{ID: 1}
	if !reflect.DeepEqual(group, want) {
		t.Errorf("Groups.TransferGroup returned %+v, want %+v", group, want)
	}
}

func TestTransferSubGroup(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1/transfer",
		func(w http.ResponseWriter, r *http.Request) {
			testMethod(t, r, http.MethodPost)
			fmt.Fprintf(w, `{"id": 1, "parent_id": 2}`)
		})

	opt := &TransferSubGroupOptions{
		GroupID: Ptr(2),
	}

	group, _, err := client.Groups.TransferSubGroup(1, opt)
	if err != nil {
		t.Errorf("Groups.TransferSubGroup returned error: %v", err)
	}

	want := &Group{ID: 1, ParentID: 2}
	if !reflect.DeepEqual(group, want) {
		t.Errorf("Groups.TransferSubGroup returned %+v, want %+v", group, want)
	}
}

func TestDeleteGroup(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1",
		func(w http.ResponseWriter, r *http.Request) {
			testMethod(t, r, http.MethodDelete)
			w.WriteHeader(http.StatusAccepted)
		})

	resp, err := client.Groups.DeleteGroup(1, nil)
	if err != nil {
		t.Errorf("Groups.DeleteGroup returned error: %v", err)
	}

	want := http.StatusAccepted
	got := resp.StatusCode
	if got != want {
		t.Errorf("Groups.DeleteGroup returned %d, want %d", got, want)
	}
}

func TestDeleteGroup_WithPermanentDelete(t *testing.T) {
	mux, client := setup(t)
	var params url.Values

	mux.HandleFunc("/api/v4/groups/1",
		func(w http.ResponseWriter, r *http.Request) {
			testMethod(t, r, http.MethodDelete)
			w.WriteHeader(http.StatusAccepted)

			// Get the request parameters
			parsedParams, err := url.ParseQuery(r.URL.RawQuery)
			if err != nil {
				t.Errorf("Groups.DeleteGroup returned error when parsing test parameters: %v", err)
			}
			params = parsedParams
		})

	resp, err := client.Groups.DeleteGroup(1, &DeleteGroupOptions{
		PermanentlyRemove: Ptr(true),
		FullPath:          Ptr("testPath"),
	})
	if err != nil {
		t.Errorf("Groups.DeleteGroup returned error: %v", err)
	}

	// Test that our status code matches
	if resp.StatusCode != http.StatusAccepted {
		t.Errorf("Groups.DeleteGroup returned %d, want %d", resp.StatusCode, http.StatusAccepted)
	}

	// Test that "permanently_remove" is set to true
	if params.Get("permanently_remove") != "true" {
		t.Errorf("Groups.DeleteGroup returned %v, want %v", params.Get("permanently_remove"), true)
	}

	// Test that "full_path" is set to "testPath"
	if params.Get("full_path") != "testPath" {
		t.Errorf("Groups.DeleteGroup returned %v, want %v", params.Get("full_path"), "testPath")
	}
}

func TestSearchGroup(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups",
		func(w http.ResponseWriter, r *http.Request) {
			testMethod(t, r, http.MethodGet)
			fmt.Fprint(w, `[{"id": 1, "name": "Foobar Group"}]`)
		})

	groups, _, err := client.Groups.SearchGroup("foobar")
	if err != nil {
		t.Errorf("Groups.SearchGroup returned error: %v", err)
	}

	want := []*Group{{ID: 1, Name: "Foobar Group"}}
	if !reflect.DeepEqual(want, groups) {
		t.Errorf("Groups.SearchGroup returned +%v, want %+v", groups, want)
	}
}

func TestUpdateGroup(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1",
		func(w http.ResponseWriter, r *http.Request) {
			testMethod(t, r, http.MethodPut)
			fmt.Fprint(w, `{"id": 1}`)
		})

	group, _, err := client.Groups.UpdateGroup(1, &UpdateGroupOptions{})
	if err != nil {
		t.Errorf("Groups.UpdateGroup returned error: %v", err)
	}

	want := &Group{ID: 1}
	if !reflect.DeepEqual(want, group) {
		t.Errorf("Groups.UpdatedGroup returned %+v, want %+v", group, want)
	}
}

func TestUpdateGroupWithDefaultBranch(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1",
		func(w http.ResponseWriter, r *http.Request) {
			testMethod(t, r, http.MethodPut)
			fmt.Fprint(w, `{"id": 1, "default_branch": "branch"}`)
		})

	opt := &UpdateGroupOptions{
		DefaultBranch: Ptr("branch"),
	}

	group, _, err := client.Groups.UpdateGroup(1, opt)
	if err != nil {
		t.Errorf("Groups.UpdateGroup returned error: %v", err)
	}

	want := &Group{ID: 1, DefaultBranch: "branch"}
	if !reflect.DeepEqual(want, group) {
		t.Errorf("Groups.UpdatedGroup returned %+v, want %+v", group, want)
	}
}

func TestListGroupProjects(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/22/projects",
		func(w http.ResponseWriter, r *http.Request) {
			testMethod(t, r, http.MethodGet)
			fmt.Fprint(w, `[{"id":1},{"id":2}]`)
		})

	projects, _, err := client.Groups.ListGroupProjects(22,
		&ListGroupProjectsOptions{})
	if err != nil {
		t.Errorf("Groups.ListGroupProjects returned error: %v", err)
	}

	want := []*Project{{ID: 1}, {ID: 2}}
	if !reflect.DeepEqual(want, projects) {
		t.Errorf("Groups.ListGroupProjects returned %+v, want %+v", projects, want)
	}
}

func TestListSubGroups(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1/subgroups",
		func(w http.ResponseWriter, r *http.Request) {
			testMethod(t, r, http.MethodGet)
			fmt.Fprint(w, `[{"id": 1}, {"id": 2}]`)
		})

	groups, _, err := client.Groups.ListSubGroups(1, &ListSubGroupsOptions{})
	if err != nil {
		t.Errorf("Groups.ListSubGroups returned error: %v", err)
	}

	want := []*Group{{ID: 1}, {ID: 2}}
	if !reflect.DeepEqual(want, groups) {
		t.Errorf("Groups.ListSubGroups returned %+v, want %+v", groups, want)
	}
}

func TestListGroupLDAPLinks(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1/ldap_group_links",
		func(w http.ResponseWriter, r *http.Request) {
			testMethod(t, r, http.MethodGet)
			fmt.Fprint(w, `[
	{
		"cn":"gitlab_group_example_30",
		"group_access":30,
		"provider":"example_ldap_provider"
	},
	{
		"cn":"gitlab_group_example_40",
		"group_access":40,
		"provider":"example_ldap_provider"
	}
]`)
		})

	links, _, err := client.Groups.ListGroupLDAPLinks(1)
	if err != nil {
		t.Errorf("Groups.ListGroupLDAPLinks returned error: %v", err)
	}

	want := []*LDAPGroupLink{
		{
			CN:          "gitlab_group_example_30",
			GroupAccess: 30,
			Provider:    "example_ldap_provider",
		},
		{
			CN:          "gitlab_group_example_40",
			GroupAccess: 40,
			Provider:    "example_ldap_provider",
		},
	}
	if !reflect.DeepEqual(want, links) {
		t.Errorf("Groups.ListGroupLDAPLinks returned %+v, want %+v", links, want)
	}
}

func TestAddGroupLDAPLink(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1/ldap_group_links",
		func(w http.ResponseWriter, r *http.Request) {
			testMethod(t, r, http.MethodPost)
			fmt.Fprint(w, `
{
	"cn":"gitlab_group_example_30",
	"group_access":30,
	"provider":"example_ldap_provider"
}`)
		})

	opt := &AddGroupLDAPLinkOptions{
		CN:          Ptr("gitlab_group_example_30"),
		GroupAccess: Ptr(AccessLevelValue(30)),
		Provider:    Ptr("example_ldap_provider"),
	}

	link, _, err := client.Groups.AddGroupLDAPLink(1, opt)
	if err != nil {
		t.Errorf("Groups.AddGroupLDAPLink returned error: %v", err)
	}

	want := &LDAPGroupLink{
		CN:          "gitlab_group_example_30",
		GroupAccess: 30,
		Provider:    "example_ldap_provider",
	}
	if !reflect.DeepEqual(want, link) {
		t.Errorf("Groups.AddGroupLDAPLink returned %+v, want %+v", link, want)
	}
}

func TestAddGroupLDAPLinkFilter(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1/ldap_group_links",
		func(w http.ResponseWriter, r *http.Request) {
			testMethod(t, r, http.MethodPost)
			fmt.Fprint(w, `
{
	"filter":"(memberOf=example_group_dn)",
	"group_access":30,
	"provider":"example_ldap_provider"
}`)
		})

	opt := &AddGroupLDAPLinkOptions{
		Filter:      Ptr("(memberOf=example_group_dn)"),
		GroupAccess: Ptr(AccessLevelValue(30)),
		Provider:    Ptr("example_ldap_provider"),
	}

	link, _, err := client.Groups.AddGroupLDAPLink(1, opt)
	if err != nil {
		t.Errorf("Groups.AddGroupLDAPLink returned error: %v", err)
	}

	want := &LDAPGroupLink{
		Filter:      "(memberOf=example_group_dn)",
		GroupAccess: 30,
		Provider:    "example_ldap_provider",
	}
	if !reflect.DeepEqual(want, link) {
		t.Errorf("Groups.AddGroupLDAPLink returned %+v, want %+v", link, want)
	}
}

func TestListGroupSAMLLinks(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1/saml_group_links",
		func(w http.ResponseWriter, r *http.Request) {
			testMethod(t, r, http.MethodGet)
			fmt.Fprint(w, `[
	{
		"access_level":30,
		"name":"gitlab_group_example_developer"
	},
	{
		"access_level":40,
		"name":"gitlab_group_example_maintainer"
	}
]`)
		})

	links, _, err := client.Groups.ListGroupSAMLLinks(1)
	if err != nil {
		t.Errorf("Groups.ListGroupSAMLLinks returned error: %v", err)
	}

	want := []*SAMLGroupLink{
		{
			AccessLevel: DeveloperPermissions,
			Name:        "gitlab_group_example_developer",
		},
		{
			AccessLevel: MaintainerPermissions,
			Name:        "gitlab_group_example_maintainer",
		},
	}
	if !reflect.DeepEqual(want, links) {
		t.Errorf("Groups.ListGroupSAMLLinks returned %+v, want %+v", links, want)
	}
}

func TestListGroupSAMLLinksCustomRole(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1/saml_group_links",
		func(w http.ResponseWriter, r *http.Request) {
			testMethod(t, r, http.MethodGet)
			fmt.Fprint(w, `[
	{
		"access_level":30,
		"name":"gitlab_group_example_developer",
		"member_role_id":123
	}
]`)
		})

	links, _, err := client.Groups.ListGroupSAMLLinks(1)
	if err != nil {
		t.Errorf("Groups.ListGroupSAMLLinks returned error: %v", err)
	}

	want := []*SAMLGroupLink{
		{
			AccessLevel:  DeveloperPermissions,
			Name:         "gitlab_group_example_developer",
			MemberRoleID: 123,
		},
	}
	if !reflect.DeepEqual(want, links) {
		t.Errorf("Groups.ListGroupSAMLLinks returned %+v, want %+v", links, want)
	}
}

func TestGetGroupSAMLLink(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1/saml_group_links/gitlab_group_example_developer",
		func(w http.ResponseWriter, r *http.Request) {
			testMethod(t, r, http.MethodGet)
			fmt.Fprint(w, `
{
	"access_level":30,
	"name":"gitlab_group_example_developer"
}`)
		})

	links, _, err := client.Groups.GetGroupSAMLLink(1, "gitlab_group_example_developer")
	if err != nil {
		t.Errorf("Groups.GetGroupSAMLLinks returned error: %v", err)
	}

	want := &SAMLGroupLink{
		AccessLevel: DeveloperPermissions,
		Name:        "gitlab_group_example_developer",
	}
	if !reflect.DeepEqual(want, links) {
		t.Errorf("Groups.GetGroupSAMLLink returned %+v, want %+v", links, want)
	}
}

func TestGetGroupSAMLLinkCustomRole(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1/saml_group_links/gitlab_group_example_developer",
		func(w http.ResponseWriter, r *http.Request) {
			testMethod(t, r, http.MethodGet)
			fmt.Fprint(w, `
{
	"access_level":30,
	"name":"gitlab_group_example_developer",
	"member_role_id":123
}`)
		})

	links, _, err := client.Groups.GetGroupSAMLLink(1, "gitlab_group_example_developer")
	if err != nil {
		t.Errorf("Groups.GetGroupSAMLLinks returned error: %v", err)
	}

	want := &SAMLGroupLink{
		AccessLevel:  DeveloperPermissions,
		Name:         "gitlab_group_example_developer",
		MemberRoleID: 123,
	}
	if !reflect.DeepEqual(want, links) {
		t.Errorf("Groups.GetGroupSAMLLink returned %+v, want %+v", links, want)
	}
}

func TestAddGroupSAMLLink(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1/saml_group_links",
		func(w http.ResponseWriter, r *http.Request) {
			testMethod(t, r, http.MethodPost)
			fmt.Fprint(w, `
{
	"access_level":30,
	"name":"gitlab_group_example_developer"
}`)
		})

	opt := &AddGroupSAMLLinkOptions{
		SAMLGroupName: Ptr("gitlab_group_example_developer"),
		AccessLevel:   Ptr(DeveloperPermissions),
	}

	link, _, err := client.Groups.AddGroupSAMLLink(1, opt)
	if err != nil {
		t.Errorf("Groups.AddGroupSAMLLink returned error: %v", err)
	}

	want := &SAMLGroupLink{
		AccessLevel: DeveloperPermissions,
		Name:        "gitlab_group_example_developer",
	}
	if !reflect.DeepEqual(want, link) {
		t.Errorf("Groups.AddGroupSAMLLink returned %+v, want %+v", link, want)
	}
}

func TestAddGroupSAMLLinkCustomRole(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1/saml_group_links",
		func(w http.ResponseWriter, r *http.Request) {
			testMethod(t, r, http.MethodPost)
			fmt.Fprint(w, `
{
	"access_level":30,
	"name":"gitlab_group_example_developer",
	"member_role_id":123
}`)
		})

	opt := &AddGroupSAMLLinkOptions{
		SAMLGroupName: Ptr("gitlab_group_example_developer"),
		AccessLevel:   Ptr(DeveloperPermissions),
		MemberRoleID:  Ptr(123),
	}

	link, _, err := client.Groups.AddGroupSAMLLink(1, opt)
	if err != nil {
		t.Errorf("Groups.AddGroupSAMLLink returned error: %v", err)
	}

	want := &SAMLGroupLink{
		AccessLevel:  DeveloperPermissions,
		Name:         "gitlab_group_example_developer",
		MemberRoleID: 123,
	}
	if !reflect.DeepEqual(want, link) {
		t.Errorf("Groups.AddGroupSAMLLink returned %+v, want %+v", link, want)
	}
}

func TestRestoreGroup(t *testing.T) {
	mux, client := setup(t)
	mux.HandleFunc("/api/v4/groups/1/restore",
		func(w http.ResponseWriter, r *http.Request) {
			testMethod(t, r, http.MethodPost)
			fmt.Fprint(w, `{"id": 1, "name": "g"}`)
		})

	group, _, err := client.Groups.RestoreGroup(1)
	if err != nil {
		t.Errorf("Groups.RestoreGroup returned error: %v", err)
	}
	want := &Group{ID: 1, Name: "g"}
	if !reflect.DeepEqual(want, group) {
		t.Errorf("Groups.RestoreGroup returned %+v, want %+v", group, want)
	}
}

func TestShareGroupWithGroup(t *testing.T) {
	mux, client := setup(t)

	var input ShareGroupWithGroupOptions
	mux.HandleFunc("/api/v4/groups/1/share",
		func(w http.ResponseWriter, r *http.Request) {
			// Parse the options so we can test them
			body, err := io.ReadAll(r.Body)
			if err != nil {
				t.Fatalf("Unable to read request body. Err: %v", err)
			}
			err = json.Unmarshal(body, &input)
			if err != nil {
				t.Fatalf("Unable to unmarshal request body. Err: %v", err)
			}

			// Return group details
			testMethod(t, r, http.MethodPost)
			fmt.Fprint(w, `{"id": 1, "name": "g"}`)
		})

	group, _, err := client.Groups.ShareGroupWithGroup(1, &ShareGroupWithGroupOptions{
		GroupID:      Ptr(1),
		GroupAccess:  Ptr(DeveloperPermissions),
		MemberRoleID: Ptr(1),
	})
	if err != nil {
		t.Errorf("Groups.ShareGroupWithGroup returned error: %v", err)
	}

	want := &Group{ID: 1, Name: "g"}
	if !reflect.DeepEqual(want, group) {
		t.Errorf("Groups.ShareGroupWithGroup returned %+v, want %+v", group, want)
	}

	assert.Equal(t, 1, *input.MemberRoleID)
}

func TestUnshareGroupFromGroup(t *testing.T) {
	mux, client := setup(t)
	mux.HandleFunc("/api/v4/groups/1/share/2",
		func(w http.ResponseWriter, r *http.Request) {
			testMethod(t, r, http.MethodDelete)
			w.WriteHeader(204)
		})

	r, err := client.Groups.UnshareGroupFromGroup(1, 2)
	if err != nil {
		t.Errorf("Groups.UnshareGroupFromGroup returned error: %v", err)
	}
	if r.StatusCode != 204 {
		t.Errorf("Groups.UnshareGroupFromGroup returned status code %d", r.StatusCode)
	}
}

func TestUpdateGroupWithIPRestrictionRanges(t *testing.T) {
	mux, client := setup(t)
	const ipRange = "192.168.0.0/24"

	mux.HandleFunc("/api/v4/groups/1",
		func(w http.ResponseWriter, r *http.Request) {
			testMethod(t, r, http.MethodPut)

			body, err := io.ReadAll(r.Body)
			if err != nil {
				t.Fatalf("Failed to read the request body. Error: %v", err)
			}

			var bodyJson map[string]interface{}
			err = json.Unmarshal(body, &bodyJson)
			if err != nil {
				t.Fatalf("Failed to parse the request body into JSON. Error: %v", err)
			}

			if bodyJson["ip_restriction_ranges"] != ipRange {
				t.Fatalf("Test failed. `ip_restriction_ranges` expected to be '%v', got %v", ipRange, bodyJson["ip_restriction_ranges"])
			}

			fmt.Fprintf(w, `{"id": 1, "ip_restriction_ranges" : "%v"}`, ipRange)
		})

	group, _, err := client.Groups.UpdateGroup(1, &UpdateGroupOptions{
		IPRestrictionRanges: Ptr(ipRange),
	})
	if err != nil {
		t.Errorf("Groups.UpdateGroup returned error: %v", err)
	}

	want := &Group{ID: 1, IPRestrictionRanges: ipRange}
	if !reflect.DeepEqual(want, group) {
		t.Errorf("Groups.UpdatedGroup returned %+v, want %+v", group, want)
	}
}

func TestGetGroupWithEmailsEnabled(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1",
		func(w http.ResponseWriter, r *http.Request) {
			testMethod(t, r, http.MethodGet)

			// Modified from https://docs.gitlab.com/ee/api/groups.html#details-of-a-group
			fmt.Fprint(w, `
			{
				"id": 1,
				"name": "test",
				"path": "test",
				"emails_enabled": true,
				"description": "Aliquid qui quis dignissimos distinctio ut commodi voluptas est.",
				"visibility": "public",
				"avatar_url": null,
				"web_url": "https://gitlab.example.com/groups/test",
				"request_access_enabled": false,
				"repository_storage": "default",
				"full_name": "test",
				"full_path": "test",
				"runners_token": "ba324ca7b1c77fc20bb9",
				"file_template_project_id": 1,
				"parent_id": null,
				"enabled_git_access_protocol": "all",
				"created_at": "2020-01-15T12:36:29.590Z",
				"prevent_sharing_groups_outside_hierarchy": false,
				"ip_restriction_ranges": null,
				"math_rendering_limits_enabled": true,
				"lock_math_rendering_limits_enabled": false
			  }`)
		})

	group, _, err := client.Groups.GetGroup(1, &GetGroupOptions{})
	if err != nil {
		t.Errorf("Groups.UpdateGroup returned error: %v", err)
	}

	if !group.EmailsEnabled {
		t.Fatalf("Failed to parse `emails_enabled`. Wanted true, got %v", group.EmailsEnabled)
	}
}

func TestCreateGroupWithEmailsEnabled(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups",
		func(w http.ResponseWriter, r *http.Request) {
			testMethod(t, r, http.MethodPost)

			body, err := io.ReadAll(r.Body)
			if err != nil {
				t.Fatalf("Failed to read the request body. Error: %v", err)
			}

			// unmarshal into generic JSON since we don't want to test CreateGroupOptions using itself to validate.
			var bodyJson map[string]interface{}
			err = json.Unmarshal(body, &bodyJson)
			if err != nil {
				t.Fatalf("Failed to parse the request body into JSON. Error: %v", err)
			}

			if bodyJson["emails_enabled"] != true {
				t.Fatalf("Test failed. `emails_enabled` expected to be true, got %v", bodyJson["emails_enabled"])
			}

			// Response is tested via the "GET" test, only test the actual request here.
			fmt.Fprint(w, `
			{}`)
		})

	_, _, err := client.Groups.CreateGroup(&CreateGroupOptions{EmailsEnabled: Ptr(true)})
	if err != nil {
		t.Errorf("Groups.CreateGroup returned error: %v", err)
	}
}

func TestUpdateGroupWithEmailsEnabled(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1",
		func(w http.ResponseWriter, r *http.Request) {
			testMethod(t, r, http.MethodPut)

			body, err := io.ReadAll(r.Body)
			if err != nil {
				t.Fatalf("Failed to read the request body. Error: %v", err)
			}

			// unmarshal into generic JSON since we don't want to test UpdateGroupOptions using itself to validate.
			var bodyJson map[string]interface{}
			err = json.Unmarshal(body, &bodyJson)
			if err != nil {
				t.Fatalf("Failed to parse the request body into JSON. Error: %v", err)
			}

			if bodyJson["emails_enabled"] != true {
				t.Fatalf("Test failed. `emails_enabled` expected to be true, got %v", bodyJson["emails_enabled"])
			}

			// Response is tested via the "GET" test, only test the actual request here.
			fmt.Fprint(w, `
			{}`)
		})

	_, _, err := client.Groups.UpdateGroup(1, &UpdateGroupOptions{EmailsEnabled: Ptr(true)})
	if err != nil {
		t.Errorf("Groups.UpdateGroup returned error: %v", err)
	}
}

func TestGetGroupPushRules(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1/push_rule", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprint(w, `{
			"id": 1,
			"commit_message_regex": "Fixes \\d+\\..*",
			"commit_message_negative_regex": "ssh\\:\\/\\/",
			"branch_name_regex": "(feat|fix)\\/*",
			"deny_delete_tag": false,
			"member_check": false,
			"prevent_secrets": false,
			"author_email_regex": "@company.com$",
			"file_name_regex": "(jar|exe)$",
			"max_file_size": 5,
			"commit_committer_check": false,
			"commit_committer_name_check": false,
			"reject_unsigned_commits": false,
			"reject_non_dco_commits": false
		  }`)
	})

	rule, _, err := client.Groups.GetGroupPushRules(1)
	if err != nil {
		t.Errorf("Groups.GetGroupPushRules returned error: %v", err)
	}

	want := &GroupPushRules{
		ID:                         1,
		CommitMessageRegex:         "Fixes \\d+\\..*",
		CommitMessageNegativeRegex: "ssh\\:\\/\\/",
		BranchNameRegex:            "(feat|fix)\\/*",
		DenyDeleteTag:              false,
		MemberCheck:                false,
		PreventSecrets:             false,
		AuthorEmailRegex:           "@company.com$",
		FileNameRegex:              "(jar|exe)$",
		MaxFileSize:                5,
		CommitCommitterCheck:       false,
		CommitCommitterNameCheck:   false,
		RejectUnsignedCommits:      false,
		RejectNonDCOCommits:        false,
	}

	if !reflect.DeepEqual(want, rule) {
		t.Errorf("Groups.GetGroupPushRules returned %+v, want %+v", rule, want)
	}
}

func TestAddGroupPushRules(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1/push_rule", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		fmt.Fprint(w, `{
			"id": 1,
			"commit_message_regex": "Fixes \\d+\\..*",
			"commit_message_negative_regex": "ssh\\:\\/\\/",
			"branch_name_regex": "(feat|fix)\\/*",
			"deny_delete_tag": false,
			"member_check": false,
			"prevent_secrets": false,
			"author_email_regex": "@company.com$",
			"file_name_regex": "(jar|exe)$",
			"max_file_size": 5,
			"commit_committer_check": false,
			"commit_committer_name_check": false,
			"reject_unsigned_commits": false,
			"reject_non_dco_commits": false
		  }`)
	})

	opt := &AddGroupPushRuleOptions{
		CommitMessageRegex:         Ptr("Fixes \\d+\\..*"),
		CommitMessageNegativeRegex: Ptr("ssh\\:\\/\\/"),
		BranchNameRegex:            Ptr("(feat|fix)\\/*"),
		DenyDeleteTag:              Ptr(false),
		MemberCheck:                Ptr(false),
		PreventSecrets:             Ptr(false),
		AuthorEmailRegex:           Ptr("@company.com$"),
		FileNameRegex:              Ptr("(jar|exe)$"),
		MaxFileSize:                Ptr(5),
		CommitCommitterCheck:       Ptr(false),
		CommitCommitterNameCheck:   Ptr(false),
		RejectUnsignedCommits:      Ptr(false),
		RejectNonDCOCommits:        Ptr(false),
	}

	rule, _, err := client.Groups.AddGroupPushRule(1, opt)
	if err != nil {
		t.Errorf("Groups.AddGroupPushRule returned error: %v", err)
	}

	want := &GroupPushRules{
		ID:                         1,
		CommitMessageRegex:         "Fixes \\d+\\..*",
		CommitMessageNegativeRegex: "ssh\\:\\/\\/",
		BranchNameRegex:            "(feat|fix)\\/*",
		DenyDeleteTag:              false,
		MemberCheck:                false,
		PreventSecrets:             false,
		AuthorEmailRegex:           "@company.com$",
		FileNameRegex:              "(jar|exe)$",
		MaxFileSize:                5,
		CommitCommitterCheck:       false,
		CommitCommitterNameCheck:   false,
		RejectUnsignedCommits:      false,
		RejectNonDCOCommits:        false,
	}

	if !reflect.DeepEqual(want, rule) {
		t.Errorf("Groups.AddGroupPushRule returned %+v, want %+v", rule, want)
	}
}

func TestEditGroupPushRules(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1/push_rule", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPut)
		fmt.Fprint(w, `{
			"id": 1,
			"commit_message_regex": "Fixes \\d+\\..*",
			"commit_message_negative_regex": "ssh\\:\\/\\/",
			"branch_name_regex": "(feat|fix)\\/*",
			"deny_delete_tag": false,
			"member_check": false,
			"prevent_secrets": false,
			"author_email_regex": "@company.com$",
			"file_name_regex": "(jar|exe)$",
			"max_file_size": 5,
			"commit_committer_check": false,
			"commit_committer_name_check": false,
			"reject_unsigned_commits": false,
			"reject_non_dco_commits": false
		  }`)
	})

	opt := &EditGroupPushRuleOptions{
		CommitMessageRegex:         Ptr("Fixes \\d+\\..*"),
		CommitMessageNegativeRegex: Ptr("ssh\\:\\/\\/"),
		BranchNameRegex:            Ptr("(feat|fix)\\/*"),
		DenyDeleteTag:              Ptr(false),
		MemberCheck:                Ptr(false),
		PreventSecrets:             Ptr(false),
		AuthorEmailRegex:           Ptr("@company.com$"),
		FileNameRegex:              Ptr("(jar|exe)$"),
		MaxFileSize:                Ptr(5),
		CommitCommitterCheck:       Ptr(false),
		CommitCommitterNameCheck:   Ptr(false),
		RejectUnsignedCommits:      Ptr(false),
		RejectNonDCOCommits:        Ptr(false),
	}

	rule, _, err := client.Groups.EditGroupPushRule(1, opt)
	if err != nil {
		t.Errorf("Groups.EditGroupPushRule returned error: %v", err)
	}

	want := &GroupPushRules{
		ID:                         1,
		CommitMessageRegex:         "Fixes \\d+\\..*",
		CommitMessageNegativeRegex: "ssh\\:\\/\\/",
		BranchNameRegex:            "(feat|fix)\\/*",
		DenyDeleteTag:              false,
		MemberCheck:                false,
		PreventSecrets:             false,
		AuthorEmailRegex:           "@company.com$",
		FileNameRegex:              "(jar|exe)$",
		MaxFileSize:                5,
		CommitCommitterCheck:       false,
		CommitCommitterNameCheck:   false,
		RejectUnsignedCommits:      false,
		RejectNonDCOCommits:        false,
	}

	if !reflect.DeepEqual(want, rule) {
		t.Errorf("Groups.EditGroupPushRule returned %+v, want %+v", rule, want)
	}
}

func TestUpdateGroupWithAllowedEmailDomainsList(t *testing.T) {
	mux, client := setup(t)
	const domain = "example.com"

	mux.HandleFunc("/api/v4/groups/1",
		func(w http.ResponseWriter, r *http.Request) {
			testMethod(t, r, http.MethodPut)

			body, err := io.ReadAll(r.Body)
			if err != nil {
				t.Fatalf("Failed to read the request body. Error: %v", err)
			}

			var bodyJson map[string]interface{}
			err = json.Unmarshal(body, &bodyJson)
			if err != nil {
				t.Fatalf("Failed to parse the request body into JSON. Error: %v", err)
			}

			if bodyJson["allowed_email_domains_list"] != domain {
				t.Fatalf("Test failed. `allowed_email_domains_list` expected to be '%v', got %v", domain, bodyJson["allowed_email_domains_list"])
			}

			fmt.Fprintf(w, `{"id": 1, "allowed_email_domains_list" : "%v"}`, domain)
		})

	group, _, err := client.Groups.UpdateGroup(1, &UpdateGroupOptions{
		AllowedEmailDomainsList: Ptr(domain),
	})
	if err != nil {
		t.Errorf("Groups.UpdateGroup returned error: %v", err)
	}

	want := &Group{ID: 1, AllowedEmailDomainsList: domain}
	if !reflect.DeepEqual(want, group) {
		t.Errorf("Groups.UpdatedGroup returned %+v, want %+v", group, want)
	}
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/import.go000066400000000000000000000243321475761473200227240ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"net/http"
)

// ImportService handles communication with the import
// related methods of the GitLab API.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/import.html
type ImportService struct {
	client *Client
}

// GitHubImport represents the response from an import from GitHub.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/import.html#import-repository-from-github
type GitHubImport struct {
	ID                    int    `json:"id"`
	Name                  string `json:"name"`
	FullPath              string `json:"full_path"`
	FullName              string `json:"full_name"`
	RefsUrl               string `json:"refs_url"`
	ImportSource          string `json:"import_source"`
	ImportStatus          string `json:"import_status"`
	HumanImportStatusName string `json:"human_import_status_name"`
	ProviderLink          string `json:"provider_link"`
	RelationType          string `json:"relation_type"`
	ImportWarning         string `json:"import_warning"`
}

func (s GitHubImport) String() string {
	return Stringify(s)
}

// ImportRepositoryFromGitHubOptions represents the available
// ImportRepositoryFromGitHub() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/import.html#import-repository-from-github
type ImportRepositoryFromGitHubOptions struct {
	PersonalAccessToken *string `url:"personal_access_token,omitempty" json:"personal_access_token,omitempty"`
	RepoID              *int    `url:"repo_id,omitempty" json:"repo_id,omitempty"`
	NewName             *string `url:"new_name,omitempty" json:"new_name,omitempty"`
	TargetNamespace     *string `url:"target_namespace,omitempty" json:"target_namespace,omitempty"`
	GitHubHostname      *string `url:"github_hostname,omitempty" json:"github_hostname,omitempty"`
	OptionalStages      struct {
		SingleEndpointNotesImport *bool `url:"single_endpoint_notes_import,omitempty" json:"single_endpoint_notes_import,omitempty"`
		AttachmentsImport         *bool `url:"attachments_import,omitempty" json:"attachments_import,omitempty"`
		CollaboratorsImport       *bool `url:"collaborators_import,omitempty" json:"collaborators_import,omitempty"`
	} `url:"optional_stages,omitempty" json:"optional_stages,omitempty"`
	TimeoutStrategy *string `url:"timeout_strategy,omitempty" json:"timeout_strategy,omitempty"`
}

// Import a repository from GitHub.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/import.html#import-repository-from-github
func (s *ImportService) ImportRepositoryFromGitHub(opt *ImportRepositoryFromGitHubOptions, options ...RequestOptionFunc) (*GitHubImport, *Response, error) {
	req, err := s.client.NewRequest(http.MethodPost, "import/github", opt, options)
	if err != nil {
		return nil, nil, err
	}

	gi := new(GitHubImport)
	resp, err := s.client.Do(req, gi)
	if err != nil {
		return nil, resp, err
	}

	return gi, resp, nil
}

// CancelledGitHubImport represents the response when canceling
// an import from GitHub.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/import.html#cancel-github-project-import
type CancelledGitHubImport struct {
	ID                    int    `json:"id"`
	Name                  string `json:"name"`
	FullPath              string `json:"full_path"`
	FullName              string `json:"full_name"`
	ImportSource          string `json:"import_source"`
	ImportStatus          string `json:"import_status"`
	HumanImportStatusName string `json:"human_import_status_name"`
	ProviderLink          string `json:"provider_link"`
}

func (s CancelledGitHubImport) String() string {
	return Stringify(s)
}

// CancelGitHubProjectImportOptions represents the available
// CancelGitHubProjectImport() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/import.html#cancel-github-project-import
type CancelGitHubProjectImportOptions struct {
	ProjectID *int `url:"project_id,omitempty" json:"project_id,omitempty"`
}

// Cancel an import of a repository from GitHub.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/import.html#cancel-github-project-import
func (s *ImportService) CancelGitHubProjectImport(opt *CancelGitHubProjectImportOptions, options ...RequestOptionFunc) (*CancelledGitHubImport, *Response, error) {
	req, err := s.client.NewRequest(http.MethodPost, "import/github/cancel", opt, options)
	if err != nil {
		return nil, nil, err
	}

	cgi := new(CancelledGitHubImport)
	resp, err := s.client.Do(req, cgi)
	if err != nil {
		return nil, resp, err
	}

	return cgi, resp, nil
}

// ImportGitHubGistsIntoGitLabSnippetsOptions represents the available
// ImportGitHubGistsIntoGitLabSnippets() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/import.html#import-github-gists-into-gitlab-snippets
type ImportGitHubGistsIntoGitLabSnippetsOptions struct {
	PersonalAccessToken *string `url:"personal_access_token,omitempty" json:"personal_access_token,omitempty"`
}

// Import personal GitHub Gists into personal GitLab Snippets.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/import.html#import-github-gists-into-gitlab-snippets
func (s *ImportService) ImportGitHubGistsIntoGitLabSnippets(opt *ImportGitHubGistsIntoGitLabSnippetsOptions, options ...RequestOptionFunc) (*Response, error) {
	req, err := s.client.NewRequest(http.MethodPost, "import/github/gists", opt, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}

// BitbucketServerImport represents the response from an import from Bitbucket
// Server.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/import.html#import-repository-from-bitbucket-server
type BitbucketServerImport struct {
	ID       int    `json:"id"`
	Name     string `json:"name"`
	FullPath string `json:"full_path"`
	FullName string `json:"full_name"`
	RefsUrl  string `json:"refs_url"`
}

func (s BitbucketServerImport) String() string {
	return Stringify(s)
}

// ImportRepositoryFromBitbucketServerOptions represents the available ImportRepositoryFromBitbucketServer() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/import.html#import-repository-from-bitbucket-server
type ImportRepositoryFromBitbucketServerOptions struct {
	BitbucketServerUrl      *string `url:"bitbucket_server_url,omitempty" json:"bitbucket_server_url,omitempty"`
	BitbucketServerUsername *string `url:"bitbucket_server_username,omitempty" json:"bitbucket_server_username,omitempty"`
	PersonalAccessToken     *string `url:"personal_access_token,omitempty" json:"personal_access_token,omitempty"`
	BitbucketServerProject  *string `url:"bitbucket_server_project,omitempty" json:"bitbucket_server_project,omitempty"`
	BitbucketServerRepo     *string `url:"bitbucket_server_repo,omitempty" json:"bitbucket_server_repo,omitempty"`
	NewName                 *string `url:"new_name,omitempty" json:"new_name,omitempty"`
	NewNamespace            *string `url:"new_namespace,omitempty" json:"new_namespace,omitempty"`
	TimeoutStrategy         *string `url:"timeout_strategy,omitempty" json:"timeout_strategy,omitempty"`
}

// Import a repository from Bitbucket Server.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/import.html#import-repository-from-bitbucket-server
func (s *ImportService) ImportRepositoryFromBitbucketServer(opt *ImportRepositoryFromBitbucketServerOptions, options ...RequestOptionFunc) (*BitbucketServerImport, *Response, error) {
	req, err := s.client.NewRequest(http.MethodPost, "import/bitbucket_server", opt, options)
	if err != nil {
		return nil, nil, err
	}

	bsi := new(BitbucketServerImport)
	resp, err := s.client.Do(req, bsi)
	if err != nil {
		return nil, resp, err
	}

	return bsi, resp, nil
}

// BitbucketCloudImport represents the response from an import from Bitbucket
// Cloud.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/import.html#import-repository-from-bitbucket-cloud
type BitbucketCloudImport struct {
	ID                    int    `json:"id"`
	Name                  string `json:"name"`
	FullPath              string `json:"full_path"`
	FullName              string `json:"full_name"`
	RefsUrl               string `json:"refs_url"`
	ImportSource          string `json:"import_source"`
	ImportStatus          string `json:"import_status"`
	HumanImportStatusName string `json:"human_import_status_name"`
	ProviderLink          string `json:"provider_link"`
	RelationType          string `json:"relation_type"`
	ImportWarning         string `json:"import_warning"`
}

func (s BitbucketCloudImport) String() string {
	return Stringify(s)
}

// ImportRepositoryFromBitbucketCloudOptions represents the available
// ImportRepositoryFromBitbucketCloud() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/import.html#import-repository-from-bitbucket-cloud
type ImportRepositoryFromBitbucketCloudOptions struct {
	BitbucketUsername    *string `url:"bitbucket_username,omitempty" json:"bitbucket_username,omitempty"`
	BitbucketAppPassword *string `url:"bitbucket_app_password,omitempty" json:"bitbucket_app_password,omitempty"`
	RepoPath             *string `url:"repo_path,omitempty" json:"repo_path,omitempty"`
	TargetNamespace      *string `url:"target_namespace,omitempty" json:"target_namespace,omitempty"`
	NewName              *string `url:"new_name,omitempty" json:"new_name,omitempty"`
}

// Import a repository from Bitbucket Cloud.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/import.html#import-repository-from-bitbucket-cloud
func (s *ImportService) ImportRepositoryFromBitbucketCloud(opt *ImportRepositoryFromBitbucketCloudOptions, options ...RequestOptionFunc) (*BitbucketCloudImport, *Response, error) {
	req, err := s.client.NewRequest(http.MethodPost, "import/bitbucket", opt, options)
	if err != nil {
		return nil, nil, err
	}

	bci := new(BitbucketCloudImport)
	resp, err := s.client.Do(req, bci)
	if err != nil {
		return nil, resp, err
	}

	return bci, resp, nil
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/import_test.go000066400000000000000000000146171475761473200237700ustar00rootroot00000000000000package gitlab

import (
	"fmt"
	"net/http"
	"testing"

	"github.com/stretchr/testify/require"
)

func TestImportService_ImportRepositoryFromGitHub(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/import/github", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		fmt.Fprintf(w, `
			{
				"id": 27,
				"name": "my-repo",
				"full_path": "/root/my-repo",
				"full_name": "Administrator / my-repo",
				"refs_url": "/root/my-repo/refs",
				"import_source": "my-github/repo",
				"import_status": "scheduled",
				"human_import_status_name": "scheduled",
				"provider_link": "/my-github/repo",
				"relation_type": null,
				"import_warning": null
			}
		`)
	})

	want := &GitHubImport{
		ID:                    27,
		Name:                  "my-repo",
		FullPath:              "/root/my-repo",
		FullName:              "Administrator / my-repo",
		RefsUrl:               "/root/my-repo/refs",
		ImportSource:          "my-github/repo",
		ImportStatus:          "scheduled",
		HumanImportStatusName: "scheduled",
		ProviderLink:          "/my-github/repo",
	}

	opt := &ImportRepositoryFromGitHubOptions{
		PersonalAccessToken: Ptr("token"),
		RepoID:              Ptr(34),
		TargetNamespace:     Ptr("root"),
	}

	gi, resp, err := client.Import.ImportRepositoryFromGitHub(opt)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, gi)

	gi, resp, err = client.Import.ImportRepositoryFromGitHub(opt, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, gi)
}

func TestImportService_CancelGitHubProjectImport(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/import/github/cancel", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		fmt.Fprintf(w, `
			{
				"id": 27,
				"name": "my-repo",
				"full_path": "/root/my-repo",
				"full_name": "Administrator / my-repo",
				"import_source": "my-github/repo",
				"import_status": "scheduled",
				"human_import_status_name": "scheduled",
				"provider_link": "/my-github/repo"
			}
		`)
	})

	want := &CancelledGitHubImport{
		ID:                    27,
		Name:                  "my-repo",
		FullPath:              "/root/my-repo",
		FullName:              "Administrator / my-repo",
		ImportSource:          "my-github/repo",
		ImportStatus:          "scheduled",
		HumanImportStatusName: "scheduled",
		ProviderLink:          "/my-github/repo",
	}

	opt := &CancelGitHubProjectImportOptions{
		ProjectID: Ptr(27),
	}

	cgi, resp, err := client.Import.CancelGitHubProjectImport(opt)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, cgi)

	cgi, resp, err = client.Import.CancelGitHubProjectImport(opt, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, cgi)
}

func TestImportService_ImportGitHubGistsIntoGitLabSnippets(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/import/github/gists", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
	})

	opt := &ImportGitHubGistsIntoGitLabSnippetsOptions{PersonalAccessToken: Ptr("token")}

	resp, err := client.Import.ImportGitHubGistsIntoGitLabSnippets(opt)
	require.NoError(t, err)
	require.NotNil(t, resp)

	resp, err = client.Import.ImportGitHubGistsIntoGitLabSnippets(opt, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
}

func TestImportService_ImportRepositoryFromBitbucketServer(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/import/bitbucket_server", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		fmt.Fprintf(w, `
			{
				"id": 27,
				"name": "my-repo",
				"full_path": "/root/my-repo",
				"full_name": "Administrator / my-repo",
				"refs_url": "/root/my-repo/refs"
			}
		`)
	})

	want := &BitbucketServerImport{
		ID:       27,
		Name:     "my-repo",
		FullPath: "/root/my-repo",
		FullName: "Administrator / my-repo",
		RefsUrl:  "/root/my-repo/refs",
	}

	opt := &ImportRepositoryFromBitbucketServerOptions{
		BitbucketServerUrl:      Ptr("https://bitbucket.example.com"),
		BitbucketServerUsername: Ptr("username"),
		PersonalAccessToken:     Ptr("token"),
		BitbucketServerProject:  Ptr("root"),
		BitbucketServerRepo:     Ptr("my-repo"),
		NewName:                 Ptr("my-repo"),
		NewNamespace:            Ptr("root"),
		TimeoutStrategy:         Ptr("pessimistic"),
	}

	bsi, resp, err := client.Import.ImportRepositoryFromBitbucketServer(opt)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, bsi)

	bsi, resp, err = client.Import.ImportRepositoryFromBitbucketServer(opt, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, bsi)
}

func TestImportService_ImportRepositoryFromBitbucketCloud(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/import/bitbucket", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		fmt.Fprintf(w, `
			{
				"id": 27,
				"name": "my-repo",
				"full_path": "/root/my-repo",
				"full_name": "Administrator / my-repo",
				"refs_url": "/root/my-repo/refs",
				"import_source": "my-bitbucket/repo",
				"import_status": "scheduled",
				"human_import_status_name": "scheduled",
				"provider_link": "/my-bitbucket/repo",
				"relation_type": null,
				"import_warning": null
			}
		`)
	})

	want := &BitbucketCloudImport{
		ID:                    27,
		Name:                  "my-repo",
		FullPath:              "/root/my-repo",
		FullName:              "Administrator / my-repo",
		RefsUrl:               "/root/my-repo/refs",
		ImportSource:          "my-bitbucket/repo",
		ImportStatus:          "scheduled",
		HumanImportStatusName: "scheduled",
		ProviderLink:          "/my-bitbucket/repo",
	}

	opt := &ImportRepositoryFromBitbucketCloudOptions{
		BitbucketUsername:    Ptr("username"),
		BitbucketAppPassword: Ptr("password"),
		RepoPath:             Ptr("/root/my-repo"),
		TargetNamespace:      Ptr("/root/"),
		NewName:              Ptr("my-repo"),
	}

	bci, resp, err := client.Import.ImportRepositoryFromBitbucketCloud(opt)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, bci)

	bci, resp, err = client.Import.ImportRepositoryFromBitbucketCloud(opt, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, bci)
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/instance_clusters.go000066400000000000000000000110041475761473200251320ustar00rootroot00000000000000//
// Copyright 2021, Serena Fang
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"fmt"
	"net/http"
	"time"
)

// InstanceClustersService handles communication with the
// instance clusters related methods of the GitLab API.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/instance_clusters.html
type InstanceClustersService struct {
	client *Client
}

// InstanceCluster represents a GitLab Instance Cluster.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/instance_clusters.html
type InstanceCluster struct {
	ID                 int                 `json:"id"`
	Name               string              `json:"name"`
	Domain             string              `json:"domain"`
	Managed            bool                `json:"managed"`
	CreatedAt          *time.Time          `json:"created_at"`
	ProviderType       string              `json:"provider_type"`
	PlatformType       string              `json:"platform_type"`
	EnvironmentScope   string              `json:"environment_scope"`
	ClusterType        string              `json:"cluster_type"`
	User               *User               `json:"user"`
	PlatformKubernetes *PlatformKubernetes `json:"platform_kubernetes"`
	ManagementProject  *ManagementProject  `json:"management_project"`
}

func (v InstanceCluster) String() string {
	return Stringify(v)
}

// ListClusters gets a list of all instance clusters.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/instance_clusters.html#list-instance-clusters
func (s *InstanceClustersService) ListClusters(options ...RequestOptionFunc) ([]*InstanceCluster, *Response, error) {
	u := "admin/clusters"

	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	var ics []*InstanceCluster
	resp, err := s.client.Do(req, &ics)
	if err != nil {
		return nil, resp, err
	}

	return ics, resp, nil
}

// GetCluster gets an instance cluster.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/instance_clusters.html#get-a-single-instance-cluster
func (s *InstanceClustersService) GetCluster(cluster int, options ...RequestOptionFunc) (*InstanceCluster, *Response, error) {
	u := fmt.Sprintf("admin/clusters/%d", cluster)

	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	ic := new(InstanceCluster)
	resp, err := s.client.Do(req, &ic)
	if err != nil {
		return nil, resp, err
	}

	return ic, resp, nil
}

// AddCluster adds an existing cluster to the instance.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/instance_clusters.html#add-existing-instance-cluster
func (s *InstanceClustersService) AddCluster(opt *AddClusterOptions, options ...RequestOptionFunc) (*InstanceCluster, *Response, error) {
	u := "admin/clusters/add"

	req, err := s.client.NewRequest(http.MethodPost, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	ic := new(InstanceCluster)
	resp, err := s.client.Do(req, ic)
	if err != nil {
		return nil, resp, err
	}

	return ic, resp, nil
}

// EditCluster updates an existing instance cluster.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/instance_clusters.html#edit-instance-cluster
func (s *InstanceClustersService) EditCluster(cluster int, opt *EditClusterOptions, options ...RequestOptionFunc) (*InstanceCluster, *Response, error) {
	u := fmt.Sprintf("admin/clusters/%d", cluster)

	req, err := s.client.NewRequest(http.MethodPut, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	ic := new(InstanceCluster)
	resp, err := s.client.Do(req, ic)
	if err != nil {
		return nil, resp, err
	}

	return ic, resp, nil
}

// DeleteCluster deletes an existing instance cluster.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/instance_clusters.html#delete-instance-cluster
func (s *InstanceClustersService) DeleteCluster(cluster int, options ...RequestOptionFunc) (*Response, error) {
	u := fmt.Sprintf("admin/clusters/%d", cluster)

	req, err := s.client.NewRequest(http.MethodDelete, u, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/instance_clusters_test.go000066400000000000000000000250131475761473200261760ustar00rootroot00000000000000package gitlab

import (
	"fmt"
	"net/http"
	"testing"

	"github.com/stretchr/testify/require"
)

func TestInstanceClustersService_ListClusters(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/admin/clusters", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
			[
			  {
				"id": 9,
				"name": "cluster-1",
				"managed": true,
				"enabled": true,
				"domain": null,
				"provider_type": "user",
				"platform_type": "kubernetes",
				"environment_scope": "*",
				"cluster_type": "instance_type",
				"user": {
				  "id": 1,
				  "name": "Administrator",
				  "username": "root",
				  "state": "active",
				  "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
				  "web_url": "https://gitlab.example.com/root"
				},
				"platform_kubernetes": {
				  "api_url": "https://example.com",
				  "namespace": null,
				  "authorization_type": "rbac",
				  "ca_cert":"-----BEGIN CERTIFICATE-----IxMDM1MV0ZDJkZjM...-----END CERTIFICATE-----"
				},
				"provider_gcp": null,
				"management_project": null
			  }
			]
		`)
	})

	want := []*InstanceCluster{{
		ID:               9,
		Name:             "cluster-1",
		Domain:           "",
		Managed:          true,
		ProviderType:     "user",
		PlatformType:     "kubernetes",
		EnvironmentScope: "*",
		ClusterType:      "instance_type",
		User: &User{
			ID:        1,
			Username:  "root",
			Email:     "",
			Name:      "Administrator",
			State:     "active",
			WebURL:    "https://gitlab.example.com/root",
			AvatarURL: "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
		},
		PlatformKubernetes: &PlatformKubernetes{
			APIURL:            "https://example.com",
			Token:             "",
			CaCert:            "-----BEGIN CERTIFICATE-----IxMDM1MV0ZDJkZjM...-----END CERTIFICATE-----",
			Namespace:         "",
			AuthorizationType: "rbac",
		},
		ManagementProject: nil,
	}}

	ics, resp, err := client.InstanceCluster.ListClusters(nil, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, ics)

	ics, resp, err = client.InstanceCluster.ListClusters(nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, ics)
}

func TestInstanceClustersService_ListClusters_StatusNotFound(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/admin/clusters", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		w.WriteHeader(http.StatusNotFound)
	})

	ics, resp, err := client.InstanceCluster.ListClusters(nil, nil)
	require.Error(t, err)
	require.Nil(t, ics)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestInstanceClustersService_GetCluster(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/admin/clusters/9", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
		  {
			"id": 9,
			"name": "cluster-1",
			"managed": true,
			"enabled": true,
			"domain": null,
			"provider_type": "user",
			"platform_type": "kubernetes",
			"environment_scope": "*",
			"cluster_type": "instance_type",
			"user": {
			  "id": 1,
			  "name": "Administrator",
			  "username": "root",
			  "state": "active",
			  "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
			  "web_url": "https://gitlab.example.com/root"
			},
			"platform_kubernetes": {
			  "api_url": "https://example.com",
			  "namespace": null,
			  "authorization_type": "rbac",
			  "ca_cert":"-----BEGIN CERTIFICATE-----IxMDM1MV0ZDJkZjM...-----END CERTIFICATE-----"
			},
			"provider_gcp": null,
			"management_project": null
		  }
		`)
	})

	want := &InstanceCluster{
		ID:               9,
		Name:             "cluster-1",
		Domain:           "",
		Managed:          true,
		ProviderType:     "user",
		PlatformType:     "kubernetes",
		EnvironmentScope: "*",
		ClusterType:      "instance_type",
		User: &User{
			ID:        1,
			Username:  "root",
			Email:     "",
			Name:      "Administrator",
			State:     "active",
			WebURL:    "https://gitlab.example.com/root",
			AvatarURL: "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
		},
		PlatformKubernetes: &PlatformKubernetes{
			APIURL:            "https://example.com",
			Token:             "",
			CaCert:            "-----BEGIN CERTIFICATE-----IxMDM1MV0ZDJkZjM...-----END CERTIFICATE-----",
			Namespace:         "",
			AuthorizationType: "rbac",
		},
		ManagementProject: nil,
	}

	ic, resp, err := client.InstanceCluster.GetCluster(9, nil, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, ic)

	ic, resp, err = client.InstanceCluster.GetCluster(9, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, ic)

	ic, resp, err = client.InstanceCluster.GetCluster(10, nil, nil)
	require.Error(t, err)
	require.Nil(t, ic)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestInstanceClustersService_AddCluster(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/admin/clusters/add", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		fmt.Fprintf(w, `
		  {
			"id": 11,
			"name": "cluster-1",
			"managed": true,
			"enabled": true,
			"domain": null,
			"provider_type": "user",
			"platform_type": "kubernetes",
			"environment_scope": "*",
			"cluster_type": "instance_type",
			"user": {
			  "id": 1,
			  "name": "Administrator",
			  "username": "root",
			  "state": "active",
			  "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
			  "web_url": "https://gitlab.example.com/root"
			},
			"platform_kubernetes": {
			  "api_url": "https://example.com",
			  "namespace": null,
			  "authorization_type": "rbac",
			  "ca_cert":"-----BEGIN CERTIFICATE-----IxMDM1MV0ZDJkZjM...-----END CERTIFICATE-----"
			},
			"provider_gcp": null,
			"management_project": null
		  }
		`)
	})

	want := &InstanceCluster{
		ID:               11,
		Name:             "cluster-1",
		Domain:           "",
		Managed:          true,
		ProviderType:     "user",
		PlatformType:     "kubernetes",
		EnvironmentScope: "*",
		ClusterType:      "instance_type",
		User: &User{
			ID:        1,
			Username:  "root",
			Email:     "",
			Name:      "Administrator",
			State:     "active",
			WebURL:    "https://gitlab.example.com/root",
			AvatarURL: "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
		},
		PlatformKubernetes: &PlatformKubernetes{
			APIURL:            "https://example.com",
			Token:             "",
			CaCert:            "-----BEGIN CERTIFICATE-----IxMDM1MV0ZDJkZjM...-----END CERTIFICATE-----",
			Namespace:         "",
			AuthorizationType: "rbac",
		},
		ManagementProject: nil,
	}

	ic, resp, err := client.InstanceCluster.AddCluster(nil, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, ic)

	ic, resp, err = client.InstanceCluster.AddCluster(nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, ic)
}

func TestInstanceClustersService_AddCluster_StatusInternalServerError(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/admin/clusters/add", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		w.WriteHeader(http.StatusInternalServerError)
	})

	ic, resp, err := client.InstanceCluster.AddCluster(nil, nil)
	require.Error(t, err)
	require.Nil(t, ic)
	require.Equal(t, http.StatusInternalServerError, resp.StatusCode)
}

func TestInstanceClustersService_EditCluster(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/admin/clusters/11", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPut)
		fmt.Fprintf(w, `
		  {
			"id": 11,
			"name": "cluster-1",
			"managed": true,
			"enabled": true,
			"domain": null,
			"provider_type": "user",
			"platform_type": "kubernetes",
			"environment_scope": "*",
			"cluster_type": "instance_type",
			"user": {
			  "id": 1,
			  "name": "Administrator",
			  "username": "root",
			  "state": "active",
			  "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
			  "web_url": "https://gitlab.example.com/root"
			},
			"platform_kubernetes": {
			  "api_url": "https://example.com",
			  "namespace": null,
			  "authorization_type": "rbac",
			  "ca_cert":"-----BEGIN CERTIFICATE-----IxMDM1MV0ZDJkZjM...-----END CERTIFICATE-----"
			},
			"provider_gcp": null,
			"management_project": null
		  }
		`)
	})

	want := &InstanceCluster{
		ID:               11,
		Name:             "cluster-1",
		Domain:           "",
		Managed:          true,
		ProviderType:     "user",
		PlatformType:     "kubernetes",
		EnvironmentScope: "*",
		ClusterType:      "instance_type",
		User: &User{
			ID:        1,
			Username:  "root",
			Email:     "",
			Name:      "Administrator",
			State:     "active",
			WebURL:    "https://gitlab.example.com/root",
			AvatarURL: "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
		},
		PlatformKubernetes: &PlatformKubernetes{
			APIURL:            "https://example.com",
			Token:             "",
			CaCert:            "-----BEGIN CERTIFICATE-----IxMDM1MV0ZDJkZjM...-----END CERTIFICATE-----",
			Namespace:         "",
			AuthorizationType: "rbac",
		},
		ManagementProject: nil,
	}

	ic, resp, err := client.InstanceCluster.EditCluster(11, nil, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, ic)

	ic, resp, err = client.InstanceCluster.EditCluster(11, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, ic)

	ic, resp, err = client.InstanceCluster.EditCluster(12, nil, nil)
	require.Error(t, err)
	require.Nil(t, ic)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestInstanceClustersService_DeleteCluster(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/admin/clusters/11", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodDelete)
	})

	resp, err := client.InstanceCluster.DeleteCluster(11, nil, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)

	resp, err = client.InstanceCluster.DeleteCluster(11, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)

	resp, err = client.InstanceCluster.DeleteCluster(12, nil, nil)
	require.Error(t, err)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/instance_variables.go000066400000000000000000000143431475761473200252470ustar00rootroot00000000000000//
// Copyright 2021, Patrick Webster
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"fmt"
	"net/http"
	"net/url"
)

// InstanceVariablesService handles communication with the
// instance level CI variables related methods of the GitLab API.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/instance_level_ci_variables.html
type InstanceVariablesService struct {
	client *Client
}

// InstanceVariable represents a GitLab instance level CI Variable.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/instance_level_ci_variables.html
type InstanceVariable struct {
	Key          string            `json:"key"`
	Value        string            `json:"value"`
	VariableType VariableTypeValue `json:"variable_type"`
	Protected    bool              `json:"protected"`
	Masked       bool              `json:"masked"`
	Raw          bool              `json:"raw"`
	Description  string            `json:"description"`
}

func (v InstanceVariable) String() string {
	return Stringify(v)
}

// ListInstanceVariablesOptions represents the available options for listing variables
// for an instance.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/instance_level_ci_variables.html#list-all-instance-variables
type ListInstanceVariablesOptions ListOptions

// ListVariables gets a list of all variables for an instance.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/instance_level_ci_variables.html#list-all-instance-variables
func (s *InstanceVariablesService) ListVariables(opt *ListInstanceVariablesOptions, options ...RequestOptionFunc) ([]*InstanceVariable, *Response, error) {
	u := "admin/ci/variables"

	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var vs []*InstanceVariable
	resp, err := s.client.Do(req, &vs)
	if err != nil {
		return nil, resp, err
	}

	return vs, resp, nil
}

// GetVariable gets a variable.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/instance_level_ci_variables.html#show-instance-variable-details
func (s *InstanceVariablesService) GetVariable(key string, options ...RequestOptionFunc) (*InstanceVariable, *Response, error) {
	u := fmt.Sprintf("admin/ci/variables/%s", url.PathEscape(key))

	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	v := new(InstanceVariable)
	resp, err := s.client.Do(req, v)
	if err != nil {
		return nil, resp, err
	}

	return v, resp, nil
}

// CreateInstanceVariableOptions represents the available CreateVariable()
// options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/instance_level_ci_variables.html#create-instance-variable
type CreateInstanceVariableOptions struct {
	Key          *string            `url:"key,omitempty" json:"key,omitempty"`
	Value        *string            `url:"value,omitempty" json:"value,omitempty"`
	Description  *string            `url:"description,omitempty" json:"description,omitempty"`
	Masked       *bool              `url:"masked,omitempty" json:"masked,omitempty"`
	Protected    *bool              `url:"protected,omitempty" json:"protected,omitempty"`
	Raw          *bool              `url:"raw,omitempty" json:"raw,omitempty"`
	VariableType *VariableTypeValue `url:"variable_type,omitempty" json:"variable_type,omitempty"`
}

// CreateVariable creates a new instance level CI variable.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/instance_level_ci_variables.html#create-instance-variable
func (s *InstanceVariablesService) CreateVariable(opt *CreateInstanceVariableOptions, options ...RequestOptionFunc) (*InstanceVariable, *Response, error) {
	u := "admin/ci/variables"

	req, err := s.client.NewRequest(http.MethodPost, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	v := new(InstanceVariable)
	resp, err := s.client.Do(req, v)
	if err != nil {
		return nil, resp, err
	}

	return v, resp, nil
}

// UpdateInstanceVariableOptions represents the available UpdateVariable()
// options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/instance_level_ci_variables.html#update-instance-variable
type UpdateInstanceVariableOptions struct {
	Value        *string            `url:"value,omitempty" json:"value,omitempty"`
	Description  *string            `url:"description,omitempty" json:"description,omitempty"`
	Masked       *bool              `url:"masked,omitempty" json:"masked,omitempty"`
	Protected    *bool              `url:"protected,omitempty" json:"protected,omitempty"`
	Raw          *bool              `url:"raw,omitempty" json:"raw,omitempty"`
	VariableType *VariableTypeValue `url:"variable_type,omitempty" json:"variable_type,omitempty"`
}

// UpdateVariable updates the position of an existing
// instance level CI variable.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/instance_level_ci_variables.html#update-instance-variable
func (s *InstanceVariablesService) UpdateVariable(key string, opt *UpdateInstanceVariableOptions, options ...RequestOptionFunc) (*InstanceVariable, *Response, error) {
	u := fmt.Sprintf("admin/ci/variables/%s", url.PathEscape(key))

	req, err := s.client.NewRequest(http.MethodPut, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	v := new(InstanceVariable)
	resp, err := s.client.Do(req, v)
	if err != nil {
		return nil, resp, err
	}

	return v, resp, nil
}

// RemoveVariable removes an instance level CI variable.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/instance_level_ci_variables.html#remove-instance-variable
func (s *InstanceVariablesService) RemoveVariable(key string, options ...RequestOptionFunc) (*Response, error) {
	u := fmt.Sprintf("admin/ci/variables/%s", url.PathEscape(key))

	req, err := s.client.NewRequest(http.MethodDelete, u, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/instance_variables_test.go000066400000000000000000000133421475761473200263040ustar00rootroot00000000000000package gitlab

import (
	"fmt"
	"net/http"
	"testing"

	"github.com/stretchr/testify/require"
)

func TestInstanceVariablesService_ListVariables(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/admin/ci/variables", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
			[
				{
					"key": "TEST_VARIABLE_1",
					"variable_type": "env_var",
					"value": "TEST_1",
					"protected": false,
					"masked": false,
					"raw": true
				}
			]
		`)
	})

	want := []*InstanceVariable{{
		Key:          "TEST_VARIABLE_1",
		Value:        "TEST_1",
		VariableType: "env_var",
		Protected:    false,
		Masked:       false,
		Raw:          true,
	}}

	ivs, resp, err := client.InstanceVariables.ListVariables(nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, ivs)

	ivs, resp, err = client.InstanceVariables.ListVariables(nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, ivs)
}

func TestInstanceVariablesService_ListVariables_StatusNotFound(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/admin/ci/variables", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		w.WriteHeader(http.StatusNotFound)
	})

	ivs, resp, err := client.InstanceVariables.ListVariables(nil)
	require.Error(t, err)
	require.Nil(t, ivs)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestInstanceVariablesService_GetVariable(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/admin/ci/variables/TEST_VARIABLE_1", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
			{
				"key": "TEST_VARIABLE_1",
				"variable_type": "env_var",
				"value": "TEST_1",
				"protected": false,
				"masked": false,
				"raw": true
			}
		`)
	})

	want := &InstanceVariable{
		Key:          "TEST_VARIABLE_1",
		Value:        "TEST_1",
		VariableType: "env_var",
		Protected:    false,
		Masked:       false,
		Raw:          true,
	}

	iv, resp, err := client.InstanceVariables.GetVariable("TEST_VARIABLE_1", nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, iv)

	iv, resp, err = client.InstanceVariables.GetVariable("TEST_VARIABLE_1", nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, iv)

	iv, resp, err = client.InstanceVariables.GetVariable("TEST_VARIABLE_2", nil)
	require.Error(t, err)
	require.Nil(t, iv)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestInstanceVariablesService_CreateVariable(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/admin/ci/variables", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		fmt.Fprintf(w, `
			{
				"key": "NEW_VARIABLE",
				"value": "new value",
				"variable_type": "env_var",
				"protected": false,
				"masked": false,
				"raw": true
			}
		`)
	})

	want := &InstanceVariable{
		Key:          "NEW_VARIABLE",
		Value:        "new value",
		VariableType: "env_var",
		Protected:    false,
		Masked:       false,
		Raw:          true,
	}

	iv, resp, err := client.InstanceVariables.CreateVariable(nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, iv)

	iv, resp, err = client.InstanceVariables.CreateVariable(nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, iv)
}

func TestInstanceVariablesService_StatusInternalServerError(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/admin/ci/variables", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		w.WriteHeader(http.StatusInternalServerError)
	})

	iv, resp, err := client.InstanceVariables.CreateVariable(nil)
	require.Error(t, err)
	require.Nil(t, iv)
	require.Equal(t, http.StatusInternalServerError, resp.StatusCode)
}

func TestInstanceVariablesService_UpdateVariable(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/admin/ci/variables/NEW_VARIABLE", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPut)
		fmt.Fprintf(w, `
			{
				"key": "NEW_VARIABLE",
				"value": "updated value",
				"variable_type": "env_var",
				"protected": false,
				"masked": false,
				"raw": true
			}
		`)
	})

	want := &InstanceVariable{
		Key:          "NEW_VARIABLE",
		Value:        "updated value",
		VariableType: "env_var",
		Protected:    false,
		Masked:       false,
		Raw:          true,
	}

	iv, resp, err := client.InstanceVariables.UpdateVariable("NEW_VARIABLE", nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, iv)

	iv, resp, err = client.InstanceVariables.UpdateVariable("NEW_VARIABLE", nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, iv)

	iv, resp, err = client.InstanceVariables.UpdateVariable("NEW_VARIABLE_1", nil)
	require.Error(t, err)
	require.Nil(t, iv)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestInstanceVariablesService_RemoveVariable(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/admin/ci/variables/NEW_VARIABLE", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodDelete)
	})

	resp, err := client.InstanceVariables.RemoveVariable("NEW_VARIABLE", nil)
	require.NoError(t, err)
	require.NotNil(t, resp)

	resp, err = client.InstanceVariables.RemoveVariable("NEW_VARIABLE", nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)

	resp, err = client.InstanceVariables.RemoveVariable("NEW_VARIABLE_1", nil)
	require.Error(t, err)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/invites.go000066400000000000000000000126661475761473200231020ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"fmt"
	"net/http"
	"time"
)

// InvitesService handles communication with the invitation related
// methods of the GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/invitations.html
type InvitesService struct {
	client *Client
}

// PendingInvite represents a pending invite.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/invitations.html
type PendingInvite struct {
	ID            int              `json:"id"`
	InviteEmail   string           `json:"invite_email"`
	CreatedAt     *time.Time       `json:"created_at"`
	AccessLevel   AccessLevelValue `json:"access_level"`
	ExpiresAt     *time.Time       `json:"expires_at"`
	UserName      string           `json:"user_name"`
	CreatedByName string           `json:"created_by_name"`
}

// ListPendingInvitationsOptions represents the available
// ListPendingInvitations() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/invitations.html#list-all-invitations-pending-for-a-group-or-project
type ListPendingInvitationsOptions struct {
	ListOptions
	Query *string `url:"query,omitempty" json:"query,omitempty"`
}

// ListPendingGroupInvitations gets a list of invited group members.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/invitations.html#list-all-invitations-pending-for-a-group-or-project
func (s *InvitesService) ListPendingGroupInvitations(gid interface{}, opt *ListPendingInvitationsOptions, options ...RequestOptionFunc) ([]*PendingInvite, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/invitations", PathEscape(group))

	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var pis []*PendingInvite
	resp, err := s.client.Do(req, &pis)
	if err != nil {
		return nil, resp, err
	}

	return pis, resp, nil
}

// ListPendingProjectInvitations gets a list of invited project members.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/invitations.html#list-all-invitations-pending-for-a-group-or-project
func (s *InvitesService) ListPendingProjectInvitations(pid interface{}, opt *ListPendingInvitationsOptions, options ...RequestOptionFunc) ([]*PendingInvite, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/invitations", PathEscape(project))

	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var pis []*PendingInvite
	resp, err := s.client.Do(req, &pis)
	if err != nil {
		return nil, resp, err
	}

	return pis, resp, nil
}

// InvitesOptions represents the available GroupInvites() and ProjectInvites()
// options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/invitations.html#add-a-member-to-a-group-or-project
type InvitesOptions struct {
	ID          interface{}       `url:"id,omitempty" json:"id,omitempty"`
	Email       *string           `url:"email,omitempty" json:"email,omitempty"`
	UserID      interface{}       `url:"user_id,omitempty" json:"user_id,omitempty"`
	AccessLevel *AccessLevelValue `url:"access_level,omitempty" json:"access_level,omitempty"`
	ExpiresAt   *ISOTime          `url:"expires_at,omitempty" json:"expires_at,omitempty"`
}

// InvitesResult represents an invitations result.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/invitations.html#add-a-member-to-a-group-or-project
type InvitesResult struct {
	Status  string            `json:"status"`
	Message map[string]string `json:"message,omitempty"`
}

// GroupInvites invites new users by email to join a group.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/invitations.html#add-a-member-to-a-group-or-project
func (s *InvitesService) GroupInvites(gid interface{}, opt *InvitesOptions, options ...RequestOptionFunc) (*InvitesResult, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/invitations", PathEscape(group))

	req, err := s.client.NewRequest(http.MethodPost, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	ir := new(InvitesResult)
	resp, err := s.client.Do(req, ir)
	if err != nil {
		return nil, resp, err
	}

	return ir, resp, nil
}

// ProjectInvites invites new users by email to join a project.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/invitations.html#add-a-member-to-a-group-or-project
func (s *InvitesService) ProjectInvites(pid interface{}, opt *InvitesOptions, options ...RequestOptionFunc) (*InvitesResult, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/invitations", PathEscape(project))

	req, err := s.client.NewRequest(http.MethodPost, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	ir := new(InvitesResult)
	resp, err := s.client.Do(req, ir)
	if err != nil {
		return nil, resp, err
	}

	return ir, resp, nil
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/invites_test.go000066400000000000000000000101461475761473200241300ustar00rootroot00000000000000package gitlab

import (
	"fmt"
	"net/http"
	"reflect"
	"testing"
)

func TestListGroupPendingInvites(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/test/invitations", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprint(w, `[{"id":1},{"id":2}]`)
	})

	opt := &ListPendingInvitationsOptions{
		ListOptions: ListOptions{Page: 2, PerPage: 3},
	}

	projects, _, err := client.Invites.ListPendingGroupInvitations("test", opt)
	if err != nil {
		t.Errorf("Invites.ListPendingGroupInvitations returned error: %v", err)
	}

	want := []*PendingInvite{{ID: 1}, {ID: 2}}
	if !reflect.DeepEqual(want, projects) {
		t.Errorf("Invites.ListPendingGroupInvitations returned %+v, want %+v", projects, want)
	}
}

func TestGroupInvites(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/test/invitations", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		fmt.Fprint(w, `{"status": "success"}`)
	})

	opt := &InvitesOptions{
		Email: Ptr("example@member.org"),
	}

	projects, _, err := client.Invites.GroupInvites("test", opt)
	if err != nil {
		t.Errorf("Invites.GroupInvites returned error: %v", err)
	}

	want := &InvitesResult{Status: "success"}
	if !reflect.DeepEqual(want, projects) {
		t.Errorf("Invites.GroupInvites returned %+v, want %+v", projects, want)
	}
}

func TestGroupInvitesError(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/test/invitations", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		fmt.Fprint(w, `{"status": "error","message": {"example@member.org": "Already invited"}}`)
	})

	opt := &InvitesOptions{
		Email: Ptr("example@member.org"),
	}

	projects, _, err := client.Invites.GroupInvites("test", opt)
	if err != nil {
		t.Errorf("Invites.GroupInvites returned error: %v", err)
	}

	want := &InvitesResult{Status: "error", Message: map[string]string{"example@member.org": "Already invited"}}
	if !reflect.DeepEqual(want, projects) {
		t.Errorf("Invites.GroupInvites returned %+v, want %+v", projects, want)
	}
}

func TestListProjectPendingInvites(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/test/invitations", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprint(w, `[{"id":1},{"id":2}]`)
	})

	opt := &ListPendingInvitationsOptions{
		ListOptions: ListOptions{Page: 2, PerPage: 3},
	}

	projects, _, err := client.Invites.ListPendingProjectInvitations("test", opt)
	if err != nil {
		t.Errorf("Invites.ListPendingProjectInvitations returned error: %v", err)
	}

	want := []*PendingInvite{{ID: 1}, {ID: 2}}
	if !reflect.DeepEqual(want, projects) {
		t.Errorf("Invites.ListPendingProjectInvitations returned %+v, want %+v", projects, want)
	}
}

func TestProjectInvites(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/test/invitations", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		fmt.Fprint(w, `{"status": "success"}`)
	})

	opt := &InvitesOptions{
		Email: Ptr("example@member.org"),
	}

	projects, _, err := client.Invites.ProjectInvites("test", opt)
	if err != nil {
		t.Errorf("Invites.ProjectInvites returned error: %v", err)
	}

	want := &InvitesResult{Status: "success"}
	if !reflect.DeepEqual(want, projects) {
		t.Errorf("Invites.ProjectInvites returned %+v, want %+v", projects, want)
	}
}

func TestProjectInvitesError(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/test/invitations", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		fmt.Fprint(w, `{"status": "error","message": {"example@member.org": "Already invited"}}`)
	})

	opt := &InvitesOptions{
		Email: Ptr("example@member.org"),
	}

	projects, _, err := client.Invites.ProjectInvites("test", opt)
	if err != nil {
		t.Errorf("Invites.ProjectInvites returned error: %v", err)
	}

	want := &InvitesResult{Status: "error", Message: map[string]string{"example@member.org": "Already invited"}}
	if !reflect.DeepEqual(want, projects) {
		t.Errorf("Invites.ProjectInvites returned %+v, want %+v", projects, want)
	}
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/issue_links.go000066400000000000000000000134311475761473200237400ustar00rootroot00000000000000//
// Copyright 2021, Arkbriar
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"fmt"
	"net/http"
	"time"
)

// IssueLinksService handles communication with the issue relations related methods
// of the GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/issue_links.html
type IssueLinksService struct {
	client *Client
}

// IssueLink represents a two-way relation between two issues.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/issue_links.html
type IssueLink struct {
	SourceIssue *Issue `json:"source_issue"`
	TargetIssue *Issue `json:"target_issue"`
	LinkType    string `json:"link_type"`
}

// IssueRelation gets a relation between two issues.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/issue_links.html#list-issue-relations
type IssueRelation struct {
	ID             int              `json:"id"`
	IID            int              `json:"iid"`
	State          string           `json:"state"`
	Description    string           `json:"description"`
	Confidential   bool             `json:"confidential"`
	Author         *IssueAuthor     `json:"author"`
	Milestone      *Milestone       `json:"milestone"`
	ProjectID      int              `json:"project_id"`
	Assignees      []*IssueAssignee `json:"assignees"`
	Assignee       *IssueAssignee   `json:"assignee"`
	UpdatedAt      *time.Time       `json:"updated_at"`
	Title          string           `json:"title"`
	CreatedAt      *time.Time       `json:"created_at"`
	Labels         Labels           `json:"labels"`
	DueDate        *ISOTime         `json:"due_date"`
	WebURL         string           `json:"web_url"`
	References     *IssueReferences `json:"references"`
	Weight         int              `json:"weight"`
	UserNotesCount int              `json:"user_notes_count"`
	IssueLinkID    int              `json:"issue_link_id"`
	LinkType       string           `json:"link_type"`
	LinkCreatedAt  *time.Time       `json:"link_created_at"`
	LinkUpdatedAt  *time.Time       `json:"link_updated_at"`
}

// ListIssueRelations gets a list of related issues of a given issue,
// sorted by the relationship creation datetime (ascending).
//
// Issues will be filtered according to the user authorizations.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/issue_links.html#list-issue-relations
func (s *IssueLinksService) ListIssueRelations(pid interface{}, issue int, options ...RequestOptionFunc) ([]*IssueRelation, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/issues/%d/links", PathEscape(project), issue)

	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	var is []*IssueRelation
	resp, err := s.client.Do(req, &is)
	if err != nil {
		return nil, resp, err
	}

	return is, resp, nil
}

// GetIssueLink gets a specific issue link.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/issue_links.html#get-an-issue-link
func (s *IssueLinksService) GetIssueLink(pid interface{}, issue, issueLink int, options ...RequestOptionFunc) (*IssueLink, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/issues/%d/links/%d", PathEscape(project), issue, issueLink)

	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	il := new(IssueLink)
	resp, err := s.client.Do(req, il)
	if err != nil {
		return nil, resp, err
	}

	return il, resp, nil
}

// CreateIssueLinkOptions represents the available CreateIssueLink() options.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/issue_links.html#create-an-issue-link
type CreateIssueLinkOptions struct {
	TargetProjectID *string `json:"target_project_id"`
	TargetIssueIID  *string `json:"target_issue_iid"`
	LinkType        *string `json:"link_type"`
}

// CreateIssueLink creates a two-way relation between two issues.
// User must be allowed to update both issues in order to succeed.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/issue_links.html#create-an-issue-link
func (s *IssueLinksService) CreateIssueLink(pid interface{}, issue int, opt *CreateIssueLinkOptions, options ...RequestOptionFunc) (*IssueLink, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/issues/%d/links", PathEscape(project), issue)

	req, err := s.client.NewRequest(http.MethodPost, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	i := new(IssueLink)
	resp, err := s.client.Do(req, &i)
	if err != nil {
		return nil, resp, err
	}

	return i, resp, nil
}

// DeleteIssueLink deletes an issue link, thus removes the two-way relationship.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/issue_links.html#delete-an-issue-link
func (s *IssueLinksService) DeleteIssueLink(pid interface{}, issue, issueLink int, options ...RequestOptionFunc) (*IssueLink, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/issues/%d/links/%d",
		PathEscape(project),
		issue,
		issueLink)

	req, err := s.client.NewRequest(http.MethodDelete, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	il := new(IssueLink)
	resp, err := s.client.Do(req, &il)
	if err != nil {
		return nil, resp, err
	}

	return il, resp, nil
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/issue_links_test.go000066400000000000000000000246621475761473200250070ustar00rootroot00000000000000package gitlab

import (
	"fmt"
	"net/http"
	"testing"

	"github.com/stretchr/testify/require"
)

func TestIssueLinksService_ListIssueRelations(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/4/issues/14/links", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
			[
			  {
				"id" : 84,
				"iid" : 14,
				"confidential": false,
				"issue_link_id": 1,
				"project_id" : 4,
				"title" : "Issues with auth",
				"state" : "opened",
				"assignees" : [],
				"assignee" : null,
				"labels" : [
				  "bug"
				],
				"author" : {
				  "name" : "Venkatesh Thalluri",
				  "avatar_url" : null,
				  "state" : "active",
				  "web_url" : "https://gitlab.example.com/eileen.lowe",
				  "id" : 18,
				  "username" : "venkatesh.thalluri"
				},
				"description" : null,
				"milestone" : null,
				"user_notes_count": 0,
				"due_date": null,
				"web_url": "http://example.com/example/example/issues/14",
				"confidential": false,
				"weight": null,
				"link_type": "relates_to"
			  }
			]
		`)
	})

	want := []*IssueRelation{{
		ID:           84,
		IID:          14,
		State:        "opened",
		Description:  "",
		Confidential: false,
		Author: &IssueAuthor{
			ID:        18,
			State:     "active",
			WebURL:    "https://gitlab.example.com/eileen.lowe",
			Name:      "Venkatesh Thalluri",
			AvatarURL: "",
			Username:  "venkatesh.thalluri",
		},
		Milestone:   nil,
		ProjectID:   4,
		Assignees:   []*IssueAssignee{},
		Assignee:    nil,
		Title:       "Issues with auth",
		Labels:      []string{"bug"},
		WebURL:      "http://example.com/example/example/issues/14",
		Weight:      0,
		IssueLinkID: 1,
		LinkType:    "relates_to",
	}}

	is, resp, err := client.IssueLinks.ListIssueRelations(4, 14, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, is)

	is, resp, err = client.IssueLinks.ListIssueRelations(4.01, 14, nil)
	require.EqualError(t, err, "invalid ID type 4.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, is)

	is, resp, err = client.IssueLinks.ListIssueRelations(4, 14, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, is)

	is, resp, err = client.IssueLinks.ListIssueRelations(8, 14, nil)
	require.Error(t, err)
	require.Nil(t, is)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestIssueLinksService_CreateIssueLink(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/4/issues/1/links", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		fmt.Fprintf(w, `
			{
			  "source_issue" : {
				"id" : 83,
				"iid" : 11,
				"project_id" : 4,
				"title" : "Issues with auth",
				"state" : "opened",
				"assignees" : [],
				"assignee" : null,
				"labels" : [
				  "bug"
				],
				"author" : {
				  "name" : "Venkatesh Thalluri",
				  "avatar_url" : null,
				  "state" : "active",
				  "web_url" : "https://gitlab.example.com/eileen.lowe",
				  "id" : 18,
				  "username" : "venkatesh.thalluri"
				},
				"description" : null,
				"milestone" : null,
				"subscribed" : true,
				"user_notes_count": 0,
				"due_date": null,
				"web_url": "http://example.com/example/example/issues/11",
				"confidential": false,
				"weight": null
			  },
			  "target_issue" : {
				"id" : 84,
				"iid" : 14,
				"project_id" : 4,
				"title" : "Issues with auth",
				"state" : "opened",
				"assignees" : [],
				"assignee" : null,
				"labels" : [
				  "bug"
				],
				"author" : {
				  "name" : "Alexandra Bashirian",
				  "avatar_url" : null,
				  "state" : "active",
				  "web_url" : "https://gitlab.example.com/eileen.lowe",
				  "id" : 18,
				  "username" : "eileen.lowe"
				},
				"description" : null,
				"milestone" : null,
				"subscribed" : true,
				"user_notes_count": 0,
				"due_date": null,
				"web_url": "http://example.com/example/example/issues/14",
				"confidential": false,
				"weight": null
			  },
			  "link_type": "relates_to"
			}
		`)
	})

	want := &IssueLink{
		SourceIssue: &Issue{
			ID:          83,
			IID:         11,
			ExternalID:  "",
			State:       "opened",
			Description: "",
			Author: &IssueAuthor{
				ID:        18,
				State:     "active",
				WebURL:    "https://gitlab.example.com/eileen.lowe",
				Name:      "Venkatesh Thalluri",
				AvatarURL: "",
				Username:  "venkatesh.thalluri",
			},
			ProjectID:         4,
			Assignees:         []*IssueAssignee{},
			Title:             "Issues with auth",
			MovedToID:         0,
			Labels:            []string{"bug"},
			Upvotes:           0,
			Downvotes:         0,
			WebURL:            "http://example.com/example/example/issues/11",
			Confidential:      false,
			Weight:            0,
			DiscussionLocked:  false,
			Subscribed:        true,
			UserNotesCount:    0,
			IssueLinkID:       0,
			MergeRequestCount: 0,
			EpicIssueID:       0,
		},
		TargetIssue: &Issue{
			ID:          84,
			IID:         14,
			ExternalID:  "",
			State:       "opened",
			Description: "",
			Author: &IssueAuthor{
				ID:        18,
				State:     "active",
				WebURL:    "https://gitlab.example.com/eileen.lowe",
				Name:      "Alexandra Bashirian",
				AvatarURL: "",
				Username:  "eileen.lowe",
			},
			ProjectID:         4,
			Assignees:         []*IssueAssignee{},
			Title:             "Issues with auth",
			MovedToID:         0,
			Labels:            []string{"bug"},
			Upvotes:           0,
			Downvotes:         0,
			WebURL:            "http://example.com/example/example/issues/14",
			Confidential:      false,
			Weight:            0,
			DiscussionLocked:  false,
			Subscribed:        true,
			UserNotesCount:    0,
			IssueLinkID:       0,
			MergeRequestCount: 0,
			EpicIssueID:       0,
		},
		LinkType: "relates_to",
	}

	i, resp, err := client.IssueLinks.CreateIssueLink(4, 1, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, i)

	i, resp, err = client.IssueLinks.CreateIssueLink(4.01, 1, nil)
	require.EqualError(t, err, "invalid ID type 4.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, i)

	i, resp, err = client.IssueLinks.CreateIssueLink(4, 1, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, i)

	i, resp, err = client.IssueLinks.CreateIssueLink(8, 1, nil)
	require.Error(t, err)
	require.Nil(t, i)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestIssueLinksService_DeleteIssueLink(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/4/issues/1/links/83", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodDelete)
		fmt.Fprintf(w, `
			{
			  "source_issue" : {
				"id" : 83,
				"iid" : 11,
				"project_id" : 4,
				"title" : "Issues with auth",
				"state" : "opened",
				"assignees" : [],
				"assignee" : null,
				"labels" : [
				  "bug"
				],
				"author" : {
				  "name" : "Venkatesh Thalluri",
				  "avatar_url" : null,
				  "state" : "active",
				  "web_url" : "https://gitlab.example.com/eileen.lowe",
				  "id" : 18,
				  "username" : "venkatesh.thalluri"
				},
				"description" : null,
				"milestone" : null,
				"subscribed" : true,
				"user_notes_count": 0,
				"due_date": null,
				"web_url": "http://example.com/example/example/issues/11",
				"confidential": false,
				"weight": null
			  },
			  "target_issue" : {
				"id" : 84,
				"iid" : 14,
				"project_id" : 4,
				"title" : "Issues with auth",
				"state" : "opened",
				"assignees" : [],
				"assignee" : null,
				"labels" : [
				  "bug"
				],
				"author" : {
				  "name" : "Alexandra Bashirian",
				  "avatar_url" : null,
				  "state" : "active",
				  "web_url" : "https://gitlab.example.com/eileen.lowe",
				  "id" : 18,
				  "username" : "eileen.lowe"
				},
				"description" : null,
				"milestone" : null,
				"subscribed" : true,
				"user_notes_count": 0,
				"due_date": null,
				"web_url": "http://example.com/example/example/issues/14",
				"confidential": false,
				"weight": null
			  },
			  "link_type": "relates_to"
			}
		`)
	})

	want := &IssueLink{
		SourceIssue: &Issue{
			ID:          83,
			IID:         11,
			ExternalID:  "",
			State:       "opened",
			Description: "",
			Author: &IssueAuthor{
				ID:        18,
				State:     "active",
				WebURL:    "https://gitlab.example.com/eileen.lowe",
				Name:      "Venkatesh Thalluri",
				AvatarURL: "",
				Username:  "venkatesh.thalluri",
			},
			ProjectID:         4,
			Assignees:         []*IssueAssignee{},
			Title:             "Issues with auth",
			MovedToID:         0,
			Labels:            []string{"bug"},
			Upvotes:           0,
			Downvotes:         0,
			WebURL:            "http://example.com/example/example/issues/11",
			Confidential:      false,
			Weight:            0,
			DiscussionLocked:  false,
			Subscribed:        true,
			UserNotesCount:    0,
			IssueLinkID:       0,
			MergeRequestCount: 0,
			EpicIssueID:       0,
		},
		TargetIssue: &Issue{
			ID:          84,
			IID:         14,
			ExternalID:  "",
			State:       "opened",
			Description: "",
			Author: &IssueAuthor{
				ID:        18,
				State:     "active",
				WebURL:    "https://gitlab.example.com/eileen.lowe",
				Name:      "Alexandra Bashirian",
				AvatarURL: "",
				Username:  "eileen.lowe",
			},
			ProjectID:         4,
			Assignees:         []*IssueAssignee{},
			Title:             "Issues with auth",
			MovedToID:         0,
			Labels:            []string{"bug"},
			Upvotes:           0,
			Downvotes:         0,
			WebURL:            "http://example.com/example/example/issues/14",
			Confidential:      false,
			Weight:            0,
			DiscussionLocked:  false,
			Subscribed:        true,
			UserNotesCount:    0,
			IssueLinkID:       0,
			MergeRequestCount: 0,
			EpicIssueID:       0,
		},
		LinkType: "relates_to",
	}

	i, resp, err := client.IssueLinks.DeleteIssueLink(4, 1, 83, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, i)

	i, resp, err = client.IssueLinks.DeleteIssueLink(4.01, 1, 83, nil)
	require.EqualError(t, err, "invalid ID type 4.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, i)

	i, resp, err = client.IssueLinks.DeleteIssueLink(4, 1, 83, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, i)

	i, resp, err = client.IssueLinks.DeleteIssueLink(8, 1, 83, nil)
	require.Error(t, err)
	require.Nil(t, i)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/issues.go000066400000000000000000001007371475761473200227310ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"encoding/json"
	"fmt"
	"net/http"
	"reflect"
	"time"
)

// IssuesService handles communication with the issue related methods
// of the GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/issues.html
type IssuesService struct {
	client    *Client
	timeStats *timeStatsService
}

// IssueAuthor represents a author of the issue.
type IssueAuthor struct {
	ID        int    `json:"id"`
	State     string `json:"state"`
	WebURL    string `json:"web_url"`
	Name      string `json:"name"`
	AvatarURL string `json:"avatar_url"`
	Username  string `json:"username"`
}

// IssueAssignee represents a assignee of the issue.
type IssueAssignee struct {
	ID        int    `json:"id"`
	State     string `json:"state"`
	WebURL    string `json:"web_url"`
	Name      string `json:"name"`
	AvatarURL string `json:"avatar_url"`
	Username  string `json:"username"`
}

// IssueReferences represents references of the issue.
type IssueReferences struct {
	Short    string `json:"short"`
	Relative string `json:"relative"`
	Full     string `json:"full"`
}

// IssueCloser represents a closer of the issue.
type IssueCloser struct {
	ID        int    `json:"id"`
	State     string `json:"state"`
	WebURL    string `json:"web_url"`
	Name      string `json:"name"`
	AvatarURL string `json:"avatar_url"`
	Username  string `json:"username"`
}

// IssueLinks represents links of the issue.
type IssueLinks struct {
	Self       string `json:"self"`
	Notes      string `json:"notes"`
	AwardEmoji string `json:"award_emoji"`
	Project    string `json:"project"`
}

// Issue represents a GitLab issue.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/issues.html
type Issue struct {
	ID                   int                    `json:"id"`
	IID                  int                    `json:"iid"`
	ExternalID           string                 `json:"external_id"`
	State                string                 `json:"state"`
	Description          string                 `json:"description"`
	HealthStatus         string                 `json:"health_status"`
	Author               *IssueAuthor           `json:"author"`
	Milestone            *Milestone             `json:"milestone"`
	ProjectID            int                    `json:"project_id"`
	Assignees            []*IssueAssignee       `json:"assignees"`
	Assignee             *IssueAssignee         `json:"assignee"`
	UpdatedAt            *time.Time             `json:"updated_at"`
	ClosedAt             *time.Time             `json:"closed_at"`
	ClosedBy             *IssueCloser           `json:"closed_by"`
	Title                string                 `json:"title"`
	CreatedAt            *time.Time             `json:"created_at"`
	MovedToID            int                    `json:"moved_to_id"`
	Labels               Labels                 `json:"labels"`
	LabelDetails         []*LabelDetails        `json:"label_details"`
	Upvotes              int                    `json:"upvotes"`
	Downvotes            int                    `json:"downvotes"`
	DueDate              *ISOTime               `json:"due_date"`
	WebURL               string                 `json:"web_url"`
	References           *IssueReferences       `json:"references"`
	TimeStats            *TimeStats             `json:"time_stats"`
	Confidential         bool                   `json:"confidential"`
	Weight               int                    `json:"weight"`
	DiscussionLocked     bool                   `json:"discussion_locked"`
	IssueType            *string                `json:"issue_type,omitempty"`
	Subscribed           bool                   `json:"subscribed"`
	UserNotesCount       int                    `json:"user_notes_count"`
	Links                *IssueLinks            `json:"_links"`
	IssueLinkID          int                    `json:"issue_link_id"`
	MergeRequestCount    int                    `json:"merge_requests_count"`
	EpicIssueID          int                    `json:"epic_issue_id"`
	Epic                 *Epic                  `json:"epic"`
	Iteration            *GroupIteration        `json:"iteration"`
	TaskCompletionStatus *TasksCompletionStatus `json:"task_completion_status"`
	ServiceDeskReplyTo   string                 `json:"service_desk_reply_to"`
}

func (i Issue) String() string {
	return Stringify(i)
}

// UnmarshalJSON implements the json.Unmarshaler interface.
func (i *Issue) UnmarshalJSON(data []byte) error {
	type alias Issue

	raw := make(map[string]interface{})
	err := json.Unmarshal(data, &raw)
	if err != nil {
		return err
	}

	if reflect.TypeOf(raw["id"]).Kind() == reflect.String {
		raw["external_id"] = raw["id"]
		delete(raw, "id")
	}

	labelDetails, ok := raw["labels"].([]interface{})
	if ok && len(labelDetails) > 0 {
		// We only want to change anything if we got label details.
		if _, ok := labelDetails[0].(map[string]interface{}); ok {
			labels := make([]interface{}, len(labelDetails))
			for i, details := range labelDetails {
				labels[i] = details.(map[string]interface{})["name"]
			}

			// Set the correct values
			raw["labels"] = labels
			raw["label_details"] = labelDetails
		}
	}

	data, err = json.Marshal(raw)
	if err != nil {
		return err
	}

	return json.Unmarshal(data, (*alias)(i))
}

// LabelDetails represents detailed label information.
type LabelDetails struct {
	ID              int    `json:"id"`
	Name            string `json:"name"`
	Color           string `json:"color"`
	Description     string `json:"description"`
	DescriptionHTML string `json:"description_html"`
	TextColor       string `json:"text_color"`
}

// ListIssuesOptions represents the available ListIssues() options.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/issues.html#list-issues
type ListIssuesOptions struct {
	ListOptions
	State               *string          `url:"state,omitempty" json:"state,omitempty"`
	Labels              *LabelOptions    `url:"labels,comma,omitempty" json:"labels,omitempty"`
	NotLabels           *LabelOptions    `url:"not[labels],comma,omitempty" json:"not[labels],omitempty"`
	WithLabelDetails    *bool            `url:"with_labels_details,omitempty" json:"with_labels_details,omitempty"`
	Milestone           *string          `url:"milestone,omitempty" json:"milestone,omitempty"`
	NotMilestone        *string          `url:"not[milestone],omitempty" json:"not[milestone],omitempty"`
	Scope               *string          `url:"scope,omitempty" json:"scope,omitempty"`
	AuthorID            *int             `url:"author_id,omitempty" json:"author_id,omitempty"`
	AuthorUsername      *string          `url:"author_username,omitempty" json:"author_username,omitempty"`
	NotAuthorUsername   *string          `url:"not[author_username],omitempty" json:"not[author_username],omitempty"`
	NotAuthorID         *[]int           `url:"not[author_id],omitempty" json:"not[author_id],omitempty"`
	AssigneeID          *AssigneeIDValue `url:"assignee_id,omitempty" json:"assignee_id,omitempty"`
	NotAssigneeID       *[]int           `url:"not[assignee_id],omitempty" json:"not[assignee_id],omitempty"`
	AssigneeUsername    *string          `url:"assignee_username,omitempty" json:"assignee_username,omitempty"`
	NotAssigneeUsername *string          `url:"not[assignee_username],omitempty" json:"not[assignee_username],omitempty"`
	MyReactionEmoji     *string          `url:"my_reaction_emoji,omitempty" json:"my_reaction_emoji,omitempty"`
	NotMyReactionEmoji  *[]string        `url:"not[my_reaction_emoji],omitempty" json:"not[my_reaction_emoji],omitempty"`
	IIDs                *[]int           `url:"iids[],omitempty" json:"iids,omitempty"`
	In                  *string          `url:"in,omitempty" json:"in,omitempty"`
	NotIn               *string          `url:"not[in],omitempty" json:"not[in],omitempty"`
	OrderBy             *string          `url:"order_by,omitempty" json:"order_by,omitempty"`
	Sort                *string          `url:"sort,omitempty" json:"sort,omitempty"`
	Search              *string          `url:"search,omitempty" json:"search,omitempty"`
	NotSearch           *string          `url:"not[search],omitempty" json:"not[search],omitempty"`
	CreatedAfter        *time.Time       `url:"created_after,omitempty" json:"created_after,omitempty"`
	CreatedBefore       *time.Time       `url:"created_before,omitempty" json:"created_before,omitempty"`
	DueDate             *string          `url:"due_date,omitempty" json:"due_date,omitempty"`
	UpdatedAfter        *time.Time       `url:"updated_after,omitempty" json:"updated_after,omitempty"`
	UpdatedBefore       *time.Time       `url:"updated_before,omitempty" json:"updated_before,omitempty"`
	Confidential        *bool            `url:"confidential,omitempty" json:"confidential,omitempty"`
	IssueType           *string          `url:"issue_type,omitempty" json:"issue_type,omitempty"`
	IterationID         *int             `url:"iteration_id,omitempty" json:"iteration_id,omitempty"`
}

// ListIssues gets all issues created by authenticated user. This function
// takes pagination parameters page and per_page to restrict the list of issues.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/issues.html#list-issues
func (s *IssuesService) ListIssues(opt *ListIssuesOptions, options ...RequestOptionFunc) ([]*Issue, *Response, error) {
	req, err := s.client.NewRequest(http.MethodGet, "issues", opt, options)
	if err != nil {
		return nil, nil, err
	}

	var i []*Issue
	resp, err := s.client.Do(req, &i)
	if err != nil {
		return nil, resp, err
	}

	return i, resp, nil
}

// ListGroupIssuesOptions represents the available ListGroupIssues() options.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/issues.html#list-group-issues
type ListGroupIssuesOptions struct {
	ListOptions
	State             *string       `url:"state,omitempty" json:"state,omitempty"`
	Labels            *LabelOptions `url:"labels,comma,omitempty" json:"labels,omitempty"`
	NotLabels         *LabelOptions `url:"not[labels],comma,omitempty" json:"not[labels],omitempty"`
	WithLabelDetails  *bool         `url:"with_labels_details,omitempty" json:"with_labels_details,omitempty"`
	IIDs              *[]int        `url:"iids[],omitempty" json:"iids,omitempty"`
	Milestone         *string       `url:"milestone,omitempty" json:"milestone,omitempty"`
	NotMilestone      *string       `url:"not[milestone],omitempty" json:"not[milestone],omitempty"`
	Scope             *string       `url:"scope,omitempty" json:"scope,omitempty"`
	AuthorID          *int          `url:"author_id,omitempty" json:"author_id,omitempty"`
	NotAuthorID       *int          `url:"not[author_id],omitempty" json:"not[author_id],omitempty"`
	AuthorUsername    *string       `url:"author_username,omitempty" json:"author_username,omitempty"`
	NotAuthorUsername *string       `url:"not[author_username],omitempty" json:"not[author_username],omitempty"`

	AssigneeID          *int       `url:"assignee_id,omitempty" json:"assignee_id,omitempty"`
	NotAssigneeID       *int       `url:"not[assignee_id],omitempty" json:"not[assignee_id],omitempty"`
	AssigneeUsername    *string    `url:"assignee_username,omitempty" json:"assignee_username,omitempty"`
	NotAssigneeUsername *string    `url:"not[assignee_username],omitempty" json:"not[assignee_username],omitempty"`
	MyReactionEmoji     *string    `url:"my_reaction_emoji,omitempty" json:"my_reaction_emoji,omitempty"`
	NotMyReactionEmoji  *string    `url:"not[my_reaction_emoji],omitempty" json:"not[my_reaction_emoji],omitempty"`
	OrderBy             *string    `url:"order_by,omitempty" json:"order_by,omitempty"`
	Sort                *string    `url:"sort,omitempty" json:"sort,omitempty"`
	Search              *string    `url:"search,omitempty" json:"search,omitempty"`
	NotSearch           *string    `url:"not[search],omitempty" json:"not[search],omitempty"`
	In                  *string    `url:"in,omitempty" json:"in,omitempty"`
	NotIn               *string    `url:"not[in],omitempty" json:"not[in],omitempty"`
	CreatedAfter        *time.Time `url:"created_after,omitempty" json:"created_after,omitempty"`
	CreatedBefore       *time.Time `url:"created_before,omitempty" json:"created_before,omitempty"`
	DueDate             *string    `url:"due_date,omitempty" json:"due_date,omitempty"`
	UpdatedAfter        *time.Time `url:"updated_after,omitempty" json:"updated_after,omitempty"`
	UpdatedBefore       *time.Time `url:"updated_before,omitempty" json:"updated_before,omitempty"`
	IssueType           *string    `url:"issue_type,omitempty" json:"issue_type,omitempty"`
	IterationID         *int       `url:"iteration_id,omitempty" json:"iteration_id,omitempty"`
}

// ListGroupIssues gets a list of group issues. This function accepts
// pagination parameters page and per_page to return the list of group issues.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/issues.html#list-group-issues
func (s *IssuesService) ListGroupIssues(pid interface{}, opt *ListGroupIssuesOptions, options ...RequestOptionFunc) ([]*Issue, *Response, error) {
	group, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/issues", PathEscape(group))

	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var i []*Issue
	resp, err := s.client.Do(req, &i)
	if err != nil {
		return nil, resp, err
	}

	return i, resp, nil
}

// ListProjectIssuesOptions represents the available ListProjectIssues() options.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/issues.html#list-project-issues
type ListProjectIssuesOptions struct {
	ListOptions
	IIDs                *[]int        `url:"iids[],omitempty" json:"iids,omitempty"`
	State               *string       `url:"state,omitempty" json:"state,omitempty"`
	Labels              *LabelOptions `url:"labels,comma,omitempty" json:"labels,omitempty"`
	NotLabels           *LabelOptions `url:"not[labels],comma,omitempty" json:"not[labels],omitempty"`
	WithLabelDetails    *bool         `url:"with_labels_details,omitempty" json:"with_labels_details,omitempty"`
	Milestone           *string       `url:"milestone,omitempty" json:"milestone,omitempty"`
	NotMilestone        *string       `url:"not[milestone],omitempty" json:"not[milestone],omitempty"`
	Scope               *string       `url:"scope,omitempty" json:"scope,omitempty"`
	AuthorID            *int          `url:"author_id,omitempty" json:"author_id,omitempty"`
	AuthorUsername      *string       `url:"author_username,omitempty" json:"author_username,omitempty"`
	NotAuthorUsername   *string       `url:"not[author_username],omitempty" json:"not[author_username],omitempty"`
	NotAuthorID         *int          `url:"not[author_id],omitempty" json:"not[author_id],omitempty"`
	AssigneeID          *int          `url:"assignee_id,omitempty" json:"assignee_id,omitempty"`
	NotAssigneeID       *int          `url:"not[assignee_id],omitempty" json:"not[assignee_id],omitempty"`
	AssigneeUsername    *string       `url:"assignee_username,omitempty" json:"assignee_username,omitempty"`
	NotAssigneeUsername *string       `url:"not[assignee_username],omitempty" json:"not[assignee_username],omitempty"`
	MyReactionEmoji     *string       `url:"my_reaction_emoji,omitempty" json:"my_reaction_emoji,omitempty"`
	NotMyReactionEmoji  *string       `url:"not[my_reaction_emoji],omitempty" json:"not[my_reaction_emoji],omitempty"`
	OrderBy             *string       `url:"order_by,omitempty" json:"order_by,omitempty"`
	Sort                *string       `url:"sort,omitempty" json:"sort,omitempty"`
	Search              *string       `url:"search,omitempty" json:"search,omitempty"`
	In                  *string       `url:"in,omitempty" json:"in,omitempty"`
	NotIn               *string       `url:"not[in],omitempty" json:"not[in],omitempty"`
	CreatedAfter        *time.Time    `url:"created_after,omitempty" json:"created_after,omitempty"`
	CreatedBefore       *time.Time    `url:"created_before,omitempty" json:"created_before,omitempty"`
	DueDate             *string       `url:"due_date,omitempty" json:"due_date,omitempty"`
	UpdatedAfter        *time.Time    `url:"updated_after,omitempty" json:"updated_after,omitempty"`
	UpdatedBefore       *time.Time    `url:"updated_before,omitempty" json:"updated_before,omitempty"`
	Confidential        *bool         `url:"confidential,omitempty" json:"confidential,omitempty"`
	IssueType           *string       `url:"issue_type,omitempty" json:"issue_type,omitempty"`
	IterationID         *int          `url:"iteration_id,omitempty" json:"iteration_id,omitempty"`
}

// ListProjectIssues gets a list of project issues. This function accepts
// pagination parameters page and per_page to return the list of project issues.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/issues.html#list-project-issues
func (s *IssuesService) ListProjectIssues(pid interface{}, opt *ListProjectIssuesOptions, options ...RequestOptionFunc) ([]*Issue, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/issues", PathEscape(project))

	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var i []*Issue
	resp, err := s.client.Do(req, &i)
	if err != nil {
		return nil, resp, err
	}

	return i, resp, nil
}

// GetIssueByID gets a single issue.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/issues.html#single-issue
func (s *IssuesService) GetIssueByID(issue int, options ...RequestOptionFunc) (*Issue, *Response, error) {
	u := fmt.Sprintf("issues/%d", issue)

	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	i := new(Issue)
	resp, err := s.client.Do(req, i)
	if err != nil {
		return nil, resp, err
	}

	return i, resp, nil
}

// GetIssue gets a single project issue.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/issues.html#single-project-issue
func (s *IssuesService) GetIssue(pid interface{}, issue int, options ...RequestOptionFunc) (*Issue, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/issues/%d", PathEscape(project), issue)

	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	i := new(Issue)
	resp, err := s.client.Do(req, i)
	if err != nil {
		return nil, resp, err
	}

	return i, resp, nil
}

// CreateIssueOptions represents the available CreateIssue() options.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/issues.html#new-issue
type CreateIssueOptions struct {
	IID                                *int          `url:"iid,omitempty" json:"iid,omitempty"`
	Title                              *string       `url:"title,omitempty" json:"title,omitempty"`
	Description                        *string       `url:"description,omitempty" json:"description,omitempty"`
	Confidential                       *bool         `url:"confidential,omitempty" json:"confidential,omitempty"`
	AssigneeIDs                        *[]int        `url:"assignee_ids,omitempty" json:"assignee_ids,omitempty"`
	MilestoneID                        *int          `url:"milestone_id,omitempty" json:"milestone_id,omitempty"`
	Labels                             *LabelOptions `url:"labels,comma,omitempty" json:"labels,omitempty"`
	CreatedAt                          *time.Time    `url:"created_at,omitempty" json:"created_at,omitempty"`
	DueDate                            *ISOTime      `url:"due_date,omitempty" json:"due_date,omitempty"`
	EpicID                             *int          `url:"epic_id,omitempty" json:"epic_id,omitempty"`
	MergeRequestToResolveDiscussionsOf *int          `url:"merge_request_to_resolve_discussions_of,omitempty" json:"merge_request_to_resolve_discussions_of,omitempty"`
	DiscussionToResolve                *string       `url:"discussion_to_resolve,omitempty" json:"discussion_to_resolve,omitempty"`
	Weight                             *int          `url:"weight,omitempty" json:"weight,omitempty"`
	IssueType                          *string       `url:"issue_type,omitempty" json:"issue_type,omitempty"`
}

// CreateIssue creates a new project issue.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/issues.html#new-issue
func (s *IssuesService) CreateIssue(pid interface{}, opt *CreateIssueOptions, options ...RequestOptionFunc) (*Issue, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/issues", PathEscape(project))

	req, err := s.client.NewRequest(http.MethodPost, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	i := new(Issue)
	resp, err := s.client.Do(req, i)
	if err != nil {
		return nil, resp, err
	}

	return i, resp, nil
}

// UpdateIssueOptions represents the available UpdateIssue() options.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/issues.html#edit-issue
type UpdateIssueOptions struct {
	Title            *string       `url:"title,omitempty" json:"title,omitempty"`
	Description      *string       `url:"description,omitempty" json:"description,omitempty"`
	Confidential     *bool         `url:"confidential,omitempty" json:"confidential,omitempty"`
	AssigneeIDs      *[]int        `url:"assignee_ids,omitempty" json:"assignee_ids,omitempty"`
	MilestoneID      *int          `url:"milestone_id,omitempty" json:"milestone_id,omitempty"`
	Labels           *LabelOptions `url:"labels,comma,omitempty" json:"labels,omitempty"`
	AddLabels        *LabelOptions `url:"add_labels,comma,omitempty" json:"add_labels,omitempty"`
	RemoveLabels     *LabelOptions `url:"remove_labels,comma,omitempty" json:"remove_labels,omitempty"`
	StateEvent       *string       `url:"state_event,omitempty" json:"state_event,omitempty"`
	UpdatedAt        *time.Time    `url:"updated_at,omitempty" json:"updated_at,omitempty"`
	DueDate          *ISOTime      `url:"due_date,omitempty" json:"due_date,omitempty"`
	EpicID           *int          `url:"epic_id,omitempty" json:"epic_id,omitempty"`
	Weight           *int          `url:"weight,omitempty" json:"weight,omitempty"`
	DiscussionLocked *bool         `url:"discussion_locked,omitempty" json:"discussion_locked,omitempty"`
	IssueType        *string       `url:"issue_type,omitempty" json:"issue_type,omitempty"`
}

// UpdateIssue updates an existing project issue. This function is also used
// to mark an issue as closed.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/issues.html#edit-issues
func (s *IssuesService) UpdateIssue(pid interface{}, issue int, opt *UpdateIssueOptions, options ...RequestOptionFunc) (*Issue, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/issues/%d", PathEscape(project), issue)

	req, err := s.client.NewRequest(http.MethodPut, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	i := new(Issue)
	resp, err := s.client.Do(req, i)
	if err != nil {
		return nil, resp, err
	}

	return i, resp, nil
}

// DeleteIssue deletes a single project issue.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/issues.html#delete-an-issue
func (s *IssuesService) DeleteIssue(pid interface{}, issue int, options ...RequestOptionFunc) (*Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("projects/%s/issues/%d", PathEscape(project), issue)

	req, err := s.client.NewRequest(http.MethodDelete, u, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}

// ReorderIssueOptions represents the available ReorderIssue() options.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/issues.html#reorder-an-issue
type ReorderIssueOptions struct {
	MoveAfterID  *int `url:"move_after_id,omitempty" json:"move_after_id,omitempty"`
	MoveBeforeID *int `url:"move_before_id,omitempty" json:"move_before_id,omitempty"`
}

// ReorderIssue reorders an issue.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/issues.html#reorder-an-issue
func (s *IssuesService) ReorderIssue(pid interface{}, issue int, opt *ReorderIssueOptions, options ...RequestOptionFunc) (*Issue, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/issues/%d/reorder", PathEscape(project), issue)

	req, err := s.client.NewRequest(http.MethodPut, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	i := new(Issue)
	resp, err := s.client.Do(req, i)
	if err != nil {
		return nil, resp, err
	}

	return i, resp, nil
}

// MoveIssueOptions represents the available MoveIssue() options.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/issues.html#move-an-issue
type MoveIssueOptions struct {
	ToProjectID *int `url:"to_project_id,omitempty" json:"to_project_id,omitempty"`
}

// MoveIssue updates an existing project issue. This function is also used
// to mark an issue as closed.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/issues.html#move-an-issue
func (s *IssuesService) MoveIssue(pid interface{}, issue int, opt *MoveIssueOptions, options ...RequestOptionFunc) (*Issue, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/issues/%d/move", PathEscape(project), issue)

	req, err := s.client.NewRequest(http.MethodPost, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	i := new(Issue)
	resp, err := s.client.Do(req, i)
	if err != nil {
		return nil, resp, err
	}

	return i, resp, nil
}

// SubscribeToIssue subscribes the authenticated user to the given issue to
// receive notifications. If the user is already subscribed to the issue, the
// status code 304 is returned.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/issues.html#subscribe-to-an-issue
func (s *IssuesService) SubscribeToIssue(pid interface{}, issue int, options ...RequestOptionFunc) (*Issue, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/issues/%d/subscribe", PathEscape(project), issue)

	req, err := s.client.NewRequest(http.MethodPost, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	i := new(Issue)
	resp, err := s.client.Do(req, i)
	if err != nil {
		return nil, resp, err
	}

	return i, resp, nil
}

// UnsubscribeFromIssue unsubscribes the authenticated user from the given
// issue to not receive notifications from that merge request. If the user
// is not subscribed to the issue, status code 304 is returned.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/issues.html#unsubscribe-from-an-issue
func (s *IssuesService) UnsubscribeFromIssue(pid interface{}, issue int, options ...RequestOptionFunc) (*Issue, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/issues/%d/unsubscribe", PathEscape(project), issue)

	req, err := s.client.NewRequest(http.MethodPost, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	i := new(Issue)
	resp, err := s.client.Do(req, i)
	if err != nil {
		return nil, resp, err
	}

	return i, resp, nil
}

// CreateTodo creates a todo for the current user for an issue.
// If there already exists a todo for the user on that issue, status code
// 304 is returned.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/issues.html#create-a-to-do-item
func (s *IssuesService) CreateTodo(pid interface{}, issue int, options ...RequestOptionFunc) (*Todo, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/issues/%d/todo", PathEscape(project), issue)

	req, err := s.client.NewRequest(http.MethodPost, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	t := new(Todo)
	resp, err := s.client.Do(req, t)
	if err != nil {
		return nil, resp, err
	}

	return t, resp, nil
}

// ListMergeRequestsClosingIssueOptions represents the available
// ListMergeRequestsClosingIssue() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/issues.html#list-merge-requests-that-close-a-particular-issue-on-merge
type ListMergeRequestsClosingIssueOptions ListOptions

// ListMergeRequestsClosingIssue gets all the merge requests that will close
// issue when merged.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/issues.html#list-merge-requests-that-close-a-particular-issue-on-merge
func (s *IssuesService) ListMergeRequestsClosingIssue(pid interface{}, issue int, opt *ListMergeRequestsClosingIssueOptions, options ...RequestOptionFunc) ([]*MergeRequest, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/issues/%d/closed_by", PathEscape(project), issue)

	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var m []*MergeRequest
	resp, err := s.client.Do(req, &m)
	if err != nil {
		return nil, resp, err
	}

	return m, resp, nil
}

// ListMergeRequestsRelatedToIssueOptions represents the available
// ListMergeRequestsRelatedToIssue() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/issues.html#list-merge-requests-related-to-issue
type ListMergeRequestsRelatedToIssueOptions ListOptions

// ListMergeRequestsRelatedToIssue gets all the merge requests that are
// related to the issue
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/issues.html#list-merge-requests-related-to-issue
func (s *IssuesService) ListMergeRequestsRelatedToIssue(pid interface{}, issue int, opt *ListMergeRequestsRelatedToIssueOptions, options ...RequestOptionFunc) ([]*MergeRequest, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/issues/%d/related_merge_requests",
		PathEscape(project),
		issue,
	)

	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var m []*MergeRequest
	resp, err := s.client.Do(req, &m)
	if err != nil {
		return nil, resp, err
	}

	return m, resp, nil
}

// SetTimeEstimate sets the time estimate for a single project issue.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/issues.html#set-a-time-estimate-for-an-issue
func (s *IssuesService) SetTimeEstimate(pid interface{}, issue int, opt *SetTimeEstimateOptions, options ...RequestOptionFunc) (*TimeStats, *Response, error) {
	return s.timeStats.setTimeEstimate(pid, "issues", issue, opt, options...)
}

// ResetTimeEstimate resets the time estimate for a single project issue.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/issues.html#reset-the-time-estimate-for-an-issue
func (s *IssuesService) ResetTimeEstimate(pid interface{}, issue int, options ...RequestOptionFunc) (*TimeStats, *Response, error) {
	return s.timeStats.resetTimeEstimate(pid, "issues", issue, options...)
}

// AddSpentTime adds spent time for a single project issue.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/issues.html#add-spent-time-for-an-issue
func (s *IssuesService) AddSpentTime(pid interface{}, issue int, opt *AddSpentTimeOptions, options ...RequestOptionFunc) (*TimeStats, *Response, error) {
	return s.timeStats.addSpentTime(pid, "issues", issue, opt, options...)
}

// ResetSpentTime resets the spent time for a single project issue.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/issues.html#reset-spent-time-for-an-issue
func (s *IssuesService) ResetSpentTime(pid interface{}, issue int, options ...RequestOptionFunc) (*TimeStats, *Response, error) {
	return s.timeStats.resetSpentTime(pid, "issues", issue, options...)
}

// GetTimeSpent gets the spent time for a single project issue.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/issues.html#get-time-tracking-stats
func (s *IssuesService) GetTimeSpent(pid interface{}, issue int, options ...RequestOptionFunc) (*TimeStats, *Response, error) {
	return s.timeStats.getTimeSpent(pid, "issues", issue, options...)
}

// GetParticipants gets a list of issue participants.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/issues.html#participants-on-issues
func (s *IssuesService) GetParticipants(pid interface{}, issue int, options ...RequestOptionFunc) ([]*BasicUser, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/issues/%d/participants", PathEscape(project), issue)

	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	var bu []*BasicUser
	resp, err := s.client.Do(req, &bu)
	if err != nil {
		return nil, resp, err
	}

	return bu, resp, nil
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/issues_statistics.go000066400000000000000000000202701475761473200251740ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"fmt"
	"net/http"
	"time"
)

// IssuesStatisticsService handles communication with the issues statistics
// related methods of the GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/issues_statistics.html
type IssuesStatisticsService struct {
	client *Client
}

// IssuesStatistics represents a GitLab issues statistic.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/issues_statistics.html
type IssuesStatistics struct {
	Statistics struct {
		Counts struct {
			All    int `json:"all"`
			Closed int `json:"closed"`
			Opened int `json:"opened"`
		} `json:"counts"`
	} `json:"statistics"`
}

func (n IssuesStatistics) String() string {
	return Stringify(n)
}

// GetIssuesStatisticsOptions represents the available GetIssuesStatistics() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/issues_statistics.html#get-issues-statistics
type GetIssuesStatisticsOptions struct {
	Labels           *LabelOptions `url:"labels,omitempty" json:"labels,omitempty"`
	Milestone        *string       `url:"milestone,omitempty" json:"milestone,omitempty"`
	Scope            *string       `url:"scope,omitempty" json:"scope,omitempty"`
	AuthorID         *int          `url:"author_id,omitempty" json:"author_id,omitempty"`
	AuthorUsername   *string       `url:"author_username,omitempty" json:"author_username,omitempty"`
	AssigneeID       *int          `url:"assignee_id,omitempty" json:"assignee_id,omitempty"`
	AssigneeUsername *[]string     `url:"assignee_username,omitempty" json:"assignee_username,omitempty"`
	MyReactionEmoji  *string       `url:"my_reaction_emoji,omitempty" json:"my_reaction_emoji,omitempty"`
	IIDs             *[]int        `url:"iids[],omitempty" json:"iids,omitempty"`
	Search           *string       `url:"search,omitempty" json:"search,omitempty"`
	In               *string       `url:"in,omitempty" json:"in,omitempty"`
	CreatedAfter     *time.Time    `url:"created_after,omitempty" json:"created_after,omitempty"`
	CreatedBefore    *time.Time    `url:"created_before,omitempty" json:"created_before,omitempty"`
	UpdatedAfter     *time.Time    `url:"updated_after,omitempty" json:"updated_after,omitempty"`
	UpdatedBefore    *time.Time    `url:"updated_before,omitempty" json:"updated_before,omitempty"`
	Confidential     *bool         `url:"confidential,omitempty" json:"confidential,omitempty"`
}

// GetIssuesStatistics gets issues statistics on all issues the authenticated
// user has access to.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/issues_statistics.html#get-issues-statistics
func (s *IssuesStatisticsService) GetIssuesStatistics(opt *GetIssuesStatisticsOptions, options ...RequestOptionFunc) (*IssuesStatistics, *Response, error) {
	req, err := s.client.NewRequest(http.MethodGet, "issues_statistics", opt, options)
	if err != nil {
		return nil, nil, err
	}

	is := new(IssuesStatistics)
	resp, err := s.client.Do(req, is)
	if err != nil {
		return nil, resp, err
	}

	return is, resp, nil
}

// GetGroupIssuesStatisticsOptions represents the available GetGroupIssuesStatistics()
// options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/issues_statistics.html#get-group-issues-statistics
type GetGroupIssuesStatisticsOptions struct {
	Labels           *LabelOptions `url:"labels,omitempty" json:"labels,omitempty"`
	IIDs             *[]int        `url:"iids[],omitempty" json:"iids,omitempty"`
	Milestone        *string       `url:"milestone,omitempty" json:"milestone,omitempty"`
	Scope            *string       `url:"scope,omitempty" json:"scope,omitempty"`
	AuthorID         *int          `url:"author_id,omitempty" json:"author_id,omitempty"`
	AuthorUsername   *string       `url:"author_username,omitempty" json:"author_username,omitempty"`
	AssigneeID       *int          `url:"assignee_id,omitempty" json:"assignee_id,omitempty"`
	AssigneeUsername *[]string     `url:"assignee_username,omitempty" json:"assignee_username,omitempty"`
	MyReactionEmoji  *string       `url:"my_reaction_emoji,omitempty" json:"my_reaction_emoji,omitempty"`
	Search           *string       `url:"search,omitempty" json:"search,omitempty"`
	CreatedAfter     *time.Time    `url:"created_after,omitempty" json:"created_after,omitempty"`
	CreatedBefore    *time.Time    `url:"created_before,omitempty" json:"created_before,omitempty"`
	UpdatedAfter     *time.Time    `url:"updated_after,omitempty" json:"updated_after,omitempty"`
	UpdatedBefore    *time.Time    `url:"updated_before,omitempty" json:"updated_before,omitempty"`
	Confidential     *bool         `url:"confidential,omitempty" json:"confidential,omitempty"`
}

// GetGroupIssuesStatistics gets issues count statistics for given group.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/issues_statistics.html#get-group-issues-statistics
func (s *IssuesStatisticsService) GetGroupIssuesStatistics(gid interface{}, opt *GetGroupIssuesStatisticsOptions, options ...RequestOptionFunc) (*IssuesStatistics, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/issues_statistics", PathEscape(group))

	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	is := new(IssuesStatistics)
	resp, err := s.client.Do(req, is)
	if err != nil {
		return nil, resp, err
	}

	return is, resp, nil
}

// GetProjectIssuesStatisticsOptions represents the available
// GetProjectIssuesStatistics() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/issues_statistics.html#get-project-issues-statistics
type GetProjectIssuesStatisticsOptions struct {
	IIDs             *[]int        `url:"iids[],omitempty" json:"iids,omitempty"`
	Labels           *LabelOptions `url:"labels,omitempty" json:"labels,omitempty"`
	Milestone        *string       `url:"milestone,omitempty" json:"milestone,omitempty"`
	Scope            *string       `url:"scope,omitempty" json:"scope,omitempty"`
	AuthorID         *int          `url:"author_id,omitempty" json:"author_id,omitempty"`
	AuthorUsername   *string       `url:"author_username,omitempty" json:"author_username,omitempty"`
	AssigneeID       *int          `url:"assignee_id,omitempty" json:"assignee_id,omitempty"`
	AssigneeUsername *[]string     `url:"assignee_username,omitempty" json:"assignee_username,omitempty"`
	MyReactionEmoji  *string       `url:"my_reaction_emoji,omitempty" json:"my_reaction_emoji,omitempty"`
	Search           *string       `url:"search,omitempty" json:"search,omitempty"`
	CreatedAfter     *time.Time    `url:"created_after,omitempty" json:"created_after,omitempty"`
	CreatedBefore    *time.Time    `url:"created_before,omitempty" json:"created_before,omitempty"`
	UpdatedAfter     *time.Time    `url:"updated_after,omitempty" json:"updated_after,omitempty"`
	UpdatedBefore    *time.Time    `url:"updated_before,omitempty" json:"updated_before,omitempty"`
	Confidential     *bool         `url:"confidential,omitempty" json:"confidential,omitempty"`
}

// GetProjectIssuesStatistics gets issues count statistics for given project.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/issues_statistics.html#get-project-issues-statistics
func (s *IssuesStatisticsService) GetProjectIssuesStatistics(pid interface{}, opt *GetProjectIssuesStatisticsOptions, options ...RequestOptionFunc) (*IssuesStatistics, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/issues_statistics", PathEscape(project))

	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	is := new(IssuesStatistics)
	resp, err := s.client.Do(req, is)
	if err != nil {
		return nil, resp, err
	}

	return is, resp, nil
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/issues_statistics_test.go000066400000000000000000000073211475761473200262350ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"fmt"
	"net/http"
	"reflect"
	"testing"
)

func TestGetIssuesStatistics(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/issues_statistics", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		testURL(t, r, "/api/v4/issues_statistics?assignee_id=1&author_id=1")
		fmt.Fprint(w, `{"statistics": {"counts": {"all": 20,"closed": 5,"opened": 15}}}`)
	})

	opt := &GetIssuesStatisticsOptions{
		AssigneeID: Ptr(1),
		AuthorID:   Ptr(1),
	}

	issue, _, err := client.IssuesStatistics.GetIssuesStatistics(opt)
	if err != nil {
		t.Fatal(err)
	}

	want := &IssuesStatistics{
		Statistics: struct {
			Counts struct {
				All    int `json:"all"`
				Closed int `json:"closed"`
				Opened int `json:"opened"`
			} `json:"counts"`
		}{
			Counts: struct {
				All    int `json:"all"`
				Closed int `json:"closed"`
				Opened int `json:"opened"`
			}{
				20, 5, 15,
			},
		},
	}

	if !reflect.DeepEqual(want, issue) {
		t.Errorf("IssuesStatistics.GetIssuesStatistics returned %+v, want %+v", issue, want)
	}
}

func TestGetGroupIssuesStatistics(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1/issues_statistics", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		testURL(t, r, "/api/v4/groups/1/issues_statistics?assignee_id=1&author_id=1")
		fmt.Fprint(w, `{"statistics": {"counts": {"all": 20,"closed": 5,"opened": 15}}}`)
	})

	opt := &GetGroupIssuesStatisticsOptions{
		AssigneeID: Ptr(1),
		AuthorID:   Ptr(1),
	}

	issue, _, err := client.IssuesStatistics.GetGroupIssuesStatistics(1, opt)
	if err != nil {
		t.Fatal(err)
	}

	want := &IssuesStatistics{
		Statistics: struct {
			Counts struct {
				All    int `json:"all"`
				Closed int `json:"closed"`
				Opened int `json:"opened"`
			} `json:"counts"`
		}{
			Counts: struct {
				All    int `json:"all"`
				Closed int `json:"closed"`
				Opened int `json:"opened"`
			}{
				20, 5, 15,
			},
		},
	}

	if !reflect.DeepEqual(want, issue) {
		t.Errorf("IssuesStatistics.GetGroupIssuesStatistics returned %+v, want %+v", issue, want)
	}
}

func TestGetProjectIssuesStatistics(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/issues_statistics", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		testURL(t, r, "/api/v4/projects/1/issues_statistics?assignee_id=1&author_id=1")
		fmt.Fprint(w, `{"statistics": {"counts": {"all": 20,"closed": 5,"opened": 15}}}`)
	})

	opt := &GetProjectIssuesStatisticsOptions{
		AssigneeID: Ptr(1),
		AuthorID:   Ptr(1),
	}

	issue, _, err := client.IssuesStatistics.GetProjectIssuesStatistics(1, opt)
	if err != nil {
		t.Fatal(err)
	}

	want := &IssuesStatistics{
		Statistics: struct {
			Counts struct {
				All    int `json:"all"`
				Closed int `json:"closed"`
				Opened int `json:"opened"`
			} `json:"counts"`
		}{
			Counts: struct {
				All    int `json:"all"`
				Closed int `json:"closed"`
				Opened int `json:"opened"`
			}{
				20, 5, 15,
			},
		},
	}

	if !reflect.DeepEqual(want, issue) {
		t.Errorf("IssuesStatistics.GetProjectIssuesStatistics returned %+v, want %+v", issue, want)
	}
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/issues_test.go000066400000000000000000000663771475761473200240030ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"fmt"
	"net/http"
	"reflect"
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestGetIssue(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/issues/5", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprint(w, `{"id":1, "description": "This is test project", "author" : {"id" : 1, "name": "snehal"}, "assignees":[{"id":1}],"merge_requests_count": 1}`)
	})

	issue, _, err := client.Issues.GetIssue("1", 5)
	if err != nil {
		t.Fatal(err)
	}

	want := &Issue{
		ID:                1,
		Description:       "This is test project",
		Author:            &IssueAuthor{ID: 1, Name: "snehal"},
		Assignees:         []*IssueAssignee{{ID: 1}},
		MergeRequestCount: 1,
	}

	if !reflect.DeepEqual(want, issue) {
		t.Errorf("Issues.GetIssue returned %+v, want %+v", issue, want)
	}
}

func TestGetIssueByID(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/issues/5", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprint(w, `{"id":5, "description": "This is test project", "author" : {"id" : 1, "name": "snehal"}, "assignees":[{"id":1}],"merge_requests_count": 1}`)
	})

	issue, _, err := client.Issues.GetIssueByID(5)
	if err != nil {
		t.Fatal(err)
	}

	want := &Issue{
		ID:                5,
		Description:       "This is test project",
		Author:            &IssueAuthor{ID: 1, Name: "snehal"},
		Assignees:         []*IssueAssignee{{ID: 1}},
		MergeRequestCount: 1,
	}

	if !reflect.DeepEqual(want, issue) {
		t.Errorf("Issues.GetIssueByID returned %+v, want %+v", issue, want)
	}
}

func TestDeleteIssue(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/issues/5", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodDelete)
		fmt.Fprint(w, `{"id":1, "description": "This is test project", "author" : {"id" : 1, "name": "snehal"}, "assignees":[{"id":1}]}`)
	})

	_, err := client.Issues.DeleteIssue("1", 5)
	if err != nil {
		t.Fatal(err)
	}
}

func TestReorderIssue(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/issues/5/reorder", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPut)
		fmt.Fprint(w, `{"id":1, "title" : "Reordered issue", "description": "This is the description of a reordered issue", "author" : {"id" : 1, "name": "corrie"}, "assignees":[{"id":1}]}`)
	})

	afterID := 100
	opt := ReorderIssueOptions{MoveAfterID: &afterID}

	issue, _, err := client.Issues.ReorderIssue("1", 5, &opt)
	if err != nil {
		t.Fatal(err)
	}

	want := &Issue{
		ID:          1,
		Title:       "Reordered issue",
		Description: "This is the description of a reordered issue",
		Author:      &IssueAuthor{ID: 1, Name: "corrie"},
		Assignees:   []*IssueAssignee{{ID: 1}},
	}

	if !reflect.DeepEqual(want, issue) {
		t.Errorf("Issues.ReorderIssue returned %+v, want %+v", issue, want)
	}
}

func TestMoveIssue(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/issues/11/move", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		mustWriteHTTPResponse(t, w, "testdata/issue_move.json")
	})

	issue, _, err := client.Issues.MoveIssue("1", 11, &MoveIssueOptions{ToProjectID: Ptr(5)})
	if err != nil {
		t.Fatal(err)
	}

	want := &Issue{
		ID:        92,
		IID:       11,
		ProjectID: 5,
		MovedToID: 0,
	}

	assert.Equal(t, want.IID, issue.IID)
	assert.Equal(t, want.ProjectID, issue.ProjectID)

	mux.HandleFunc("/api/v4/projects/1/issues/11", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprint(w, `
		{
				"id": 1,
				"iid": 11,
				"project_id": 1,
				"moved_to_id": 92
		}`,
		)
	})
	movedIssue, _, err := client.Issues.GetIssue("1", 11)
	if err != nil {
		t.Fatal(err)
	}

	wantedMovedIssue := &Issue{
		ID:        1,
		IID:       11,
		ProjectID: 1,
		MovedToID: 92,
	}

	if !reflect.DeepEqual(wantedMovedIssue, movedIssue) {
		t.Errorf("Issues.GetIssue returned %+v, want %+v", issue, want)
	}
}

func TestListIssues(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/issues", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		testURL(t, r, "/api/v4/issues?assignee_id=2&author_id=1")
		fmt.Fprint(w, `
			[
				{
					"id": 1,
					"description": "This is test project",
					"author": {
						"id": 1,
						"name": "snehal"
					},
					"assignees": [
						{
							"id": 1
						}
					],
					"labels": [
						"foo",
						"bar"
					]
			  }
			]`,
		)
	})

	listProjectIssue := &ListIssuesOptions{
		AuthorID:   Ptr(0o1),
		AssigneeID: AssigneeID(0o2),
	}

	issues, _, err := client.Issues.ListIssues(listProjectIssue)
	if err != nil {
		t.Fatal(err)
	}

	want := []*Issue{{
		ID:          1,
		Description: "This is test project",
		Author:      &IssueAuthor{ID: 1, Name: "snehal"},
		Assignees:   []*IssueAssignee{{ID: 1}},
		Labels:      []string{"foo", "bar"},
	}}

	if !reflect.DeepEqual(want, issues) {
		t.Errorf("Issues.ListIssues returned %+v, want %+v", issues, want)
	}
}

func TestListIssuesWithLabelDetails(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/issues", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		testURL(t, r, "/api/v4/issues?assignee_id=2&author_id=1")
		fmt.Fprint(w, `
			[
				{
					"id": 1,
					"description": "This is test project",
					"author": {
						"id": 1,
						"name": "snehal"
					},
					"assignees": [
						{
							"id": 1
						}
					],
					"labels": [
						{
							"id": 1,
							"name": "foo",
							"color": "green",
							"description": "Issue",
							"description_html": "Issue Label",
							"text_color": "black"
						},
						{
							"id": 2,
							"name": "bar",
							"color": "red",
							"description": "Bug",
							"description_html": "Bug Label",
							"text_color": "black"
						}
			    ]
			  }
			]`,
		)
	})

	listProjectIssue := &ListIssuesOptions{
		AuthorID:   Ptr(0o1),
		AssigneeID: AssigneeID(0o2),
	}

	issues, _, err := client.Issues.ListIssues(listProjectIssue)
	if err != nil {
		t.Fatal(err)
	}

	want := []*Issue{{
		ID:          1,
		Description: "This is test project",
		Author:      &IssueAuthor{ID: 1, Name: "snehal"},
		Assignees:   []*IssueAssignee{{ID: 1}},
		Labels:      []string{"foo", "bar"},
		LabelDetails: []*LabelDetails{
			{ID: 1, Name: "foo", Color: "green", Description: "Issue", DescriptionHTML: "Issue Label", TextColor: "black"},
			{ID: 2, Name: "bar", Color: "red", Description: "Bug", DescriptionHTML: "Bug Label", TextColor: "black"},
		},
	}}

	if !reflect.DeepEqual(want, issues) {
		t.Errorf("Issues.ListIssues returned %+v, want %+v", issues, want)
	}
}

func TestListIssuesSearchInTitle(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/issues", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		testURL(t, r, "/api/v4/issues?in=title&search=Title")
		fmt.Fprint(w, `
			[
				{
					"id": 1,
					"title": "A Test Issue Title",
					"description": "This is the description for the issue"
			  }
			]`,
		)
	})

	listProjectIssue := &ListIssuesOptions{
		Search: Ptr("Title"),
		In:     Ptr("title"),
	}

	issues, _, err := client.Issues.ListIssues(listProjectIssue)
	if err != nil {
		t.Fatal(err)
	}

	want := []*Issue{{
		ID:          1,
		Title:       "A Test Issue Title",
		Description: "This is the description for the issue",
	}}

	if !reflect.DeepEqual(want, issues) {
		t.Errorf("Issues.ListIssues returned %+v, want %+v", issues, want)
	}
}

func TestListIssuesSearchInDescription(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/issues", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		testURL(t, r, "/api/v4/issues?in=description&search=description")
		fmt.Fprint(w, `
			[
				{
					"id": 1,
					"title": "A Test Issue Title",
					"description": "This is the description for the issue"
			  }
			]`,
		)
	})

	listProjectIssue := &ListIssuesOptions{
		Search: Ptr("description"),
		In:     Ptr("description"),
	}

	issues, _, err := client.Issues.ListIssues(listProjectIssue)
	if err != nil {
		t.Fatal(err)
	}

	want := []*Issue{{
		ID:          1,
		Title:       "A Test Issue Title",
		Description: "This is the description for the issue",
	}}

	if !reflect.DeepEqual(want, issues) {
		t.Errorf("Issues.ListIssues returned %+v, want %+v", issues, want)
	}
}

func TestListIssuesSearchByIterationID(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/issues", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		testURL(t, r, "/api/v4/issues?iteration_id=90")
		fmt.Fprint(w, `
			[
				{
					"id": 1,
					"title": "A Test Issue Title",
					"description": "This is the description for the issue",
					"iteration": {
						"id":90,
						"iid":4,
						"sequence":2,
						"group_id":162,
						"state":2,
						"web_url":"https://gitlab.com/groups/my-group/-/iterations/90"
					}
				}
			]`,
		)
	})

	listProjectIssue := &ListIssuesOptions{
		IterationID: Ptr(90),
	}

	issues, _, err := client.Issues.ListIssues(listProjectIssue)
	if err != nil {
		t.Fatal(err)
	}

	want := []*Issue{{
		ID:          1,
		Title:       "A Test Issue Title",
		Description: "This is the description for the issue",
		Iteration: &GroupIteration{
			ID:       90,
			IID:      4,
			Sequence: 2,
			GroupID:  162,
			State:    2,
			WebURL:   "https://gitlab.com/groups/my-group/-/iterations/90",
		},
	}}

	if !reflect.DeepEqual(want, issues) {
		t.Errorf("Issues.ListIssues returned %+v, want %+v", issues, want)
	}
}

func TestListProjectIssues(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/issues", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		testURL(t, r, "/api/v4/projects/1/issues?assignee_id=2&author_id=1")
		fmt.Fprint(w, `[{"id":1, "description": "This is test project", "author" : {"id" : 1, "name": "snehal"}, "assignees":[{"id":1}]}]`)
	})

	listProjectIssue := &ListProjectIssuesOptions{
		AuthorID:   Ptr(0o1),
		AssigneeID: Ptr(0o2),
	}
	issues, _, err := client.Issues.ListProjectIssues("1", listProjectIssue)
	if err != nil {
		t.Fatal(err)
	}

	want := []*Issue{{
		ID:          1,
		Description: "This is test project",
		Author:      &IssueAuthor{ID: 1, Name: "snehal"},
		Assignees:   []*IssueAssignee{{ID: 1}},
	}}

	if !reflect.DeepEqual(want, issues) {
		t.Errorf("Issues.ListProjectIssues returned %+v, want %+v", issues, want)
	}
}

func TestListProjectIssuesSearchByIterationID(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/issues", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		testURL(t, r, "/api/v4/projects/1/issues?iteration_id=90")
		fmt.Fprint(w, `
			[
				{
					"id": 1,
					"title": "A Test Issue Title",
					"description": "This is the description for the issue",
					"iteration": {
						"id":90,
						"iid":4,
						"sequence":2,
						"group_id":162,
						"state":2,
						"web_url":"https://gitlab.com/groups/my-group/-/iterations/90"
					}
				}
			]`,
		)
	})

	listProjectIssue := &ListProjectIssuesOptions{
		IterationID: Ptr(90),
	}

	issues, _, err := client.Issues.ListProjectIssues(1, listProjectIssue)
	if err != nil {
		t.Fatal(err)
	}

	want := []*Issue{{
		ID:          1,
		Title:       "A Test Issue Title",
		Description: "This is the description for the issue",
		Iteration: &GroupIteration{
			ID:       90,
			IID:      4,
			Sequence: 2,
			GroupID:  162,
			State:    2,
			WebURL:   "https://gitlab.com/groups/my-group/-/iterations/90",
		},
	}}

	if !reflect.DeepEqual(want, issues) {
		t.Errorf("Issues.ListIssues returned %+v, want %+v", issues, want)
	}
}

func TestListGroupIssues(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1/issues", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		testURL(t, r, "/api/v4/groups/1/issues?assignee_id=2&author_id=1&state=Open")
		fmt.Fprint(w, `[{"id":1, "description": "This is test project", "author" : {"id" : 1, "name": "snehal"}, "assignees":[{"id":1}]}]`)
	})

	listGroupIssue := &ListGroupIssuesOptions{
		State:      Ptr("Open"),
		AuthorID:   Ptr(0o1),
		AssigneeID: Ptr(0o2),
	}

	issues, _, err := client.Issues.ListGroupIssues("1", listGroupIssue)
	if err != nil {
		t.Fatal(err)
	}

	want := []*Issue{{
		ID:          1,
		Description: "This is test project",
		Author:      &IssueAuthor{ID: 1, Name: "snehal"},
		Assignees:   []*IssueAssignee{{ID: 1}},
	}}

	if !reflect.DeepEqual(want, issues) {
		t.Errorf("Issues.ListGroupIssues returned %+v, want %+v", issues, want)
	}
}

func TestListGroupIssuesSearchByIterationID(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/groups/1/issues", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		testURL(t, r, "/api/v4/groups/1/issues?iteration_id=90")
		fmt.Fprint(w, `
			[
				{
					"id": 1,
					"title": "A Test Issue Title",
					"description": "This is the description for the issue",
					"iteration": {
						"id":90,
						"iid":4,
						"sequence":2,
						"group_id":162,
						"state":2,
						"web_url":"https://gitlab.com/groups/my-group/-/iterations/90"
					}
				}
			]`,
		)
	})

	listProjectIssue := &ListGroupIssuesOptions{
		IterationID: Ptr(90),
	}

	issues, _, err := client.Issues.ListGroupIssues(1, listProjectIssue)
	if err != nil {
		t.Fatal(err)
	}

	want := []*Issue{{
		ID:          1,
		Title:       "A Test Issue Title",
		Description: "This is the description for the issue",
		Iteration: &GroupIteration{
			ID:       90,
			IID:      4,
			Sequence: 2,
			GroupID:  162,
			State:    2,
			WebURL:   "https://gitlab.com/groups/my-group/-/iterations/90",
		},
	}}

	if !reflect.DeepEqual(want, issues) {
		t.Errorf("Issues.ListIssues returned %+v, want %+v", issues, want)
	}
}

func TestCreateIssue(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/issues", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		fmt.Fprint(w, `{"id":1, "title" : "Title of issue", "description": "This is description of an issue", "author" : {"id" : 1, "name": "snehal"}, "assignees":[{"id":1}]}`)
	})

	createIssueOptions := &CreateIssueOptions{
		Title:       Ptr("Title of issue"),
		Description: Ptr("This is description of an issue"),
	}

	issue, _, err := client.Issues.CreateIssue("1", createIssueOptions)
	if err != nil {
		t.Fatal(err)
	}

	want := &Issue{
		ID:          1,
		Title:       "Title of issue",
		Description: "This is description of an issue",
		Author:      &IssueAuthor{ID: 1, Name: "snehal"},
		Assignees:   []*IssueAssignee{{ID: 1}},
	}

	if !reflect.DeepEqual(want, issue) {
		t.Errorf("Issues.CreateIssue returned %+v, want %+v", issue, want)
	}
}

func TestUpdateIssue(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/issues/5", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPut)
		fmt.Fprint(w, `{"id":1, "title" : "Title of issue", "description": "This is description of an issue", "author" : {"id" : 1, "name": "snehal"}, "assignees":[{"id":1}]}`)
	})

	updateIssueOpt := &UpdateIssueOptions{
		Title:       Ptr("Title of issue"),
		Description: Ptr("This is description of an issue"),
	}
	issue, _, err := client.Issues.UpdateIssue(1, 5, updateIssueOpt)
	if err != nil {
		t.Fatal(err)
	}

	want := &Issue{
		ID:          1,
		Title:       "Title of issue",
		Description: "This is description of an issue",
		Author:      &IssueAuthor{ID: 1, Name: "snehal"},
		Assignees:   []*IssueAssignee{{ID: 1}},
	}

	if !reflect.DeepEqual(want, issue) {
		t.Errorf("Issues.UpdateIssue returned %+v, want %+v", issue, want)
	}
}

func TestSubscribeToIssue(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/issues/5/subscribe", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		fmt.Fprint(w, `{"id":1, "title" : "Title of issue", "description": "This is description of an issue", "author" : {"id" : 1, "name": "snehal"}, "assignees":[{"id":1}]}`)
	})

	issue, _, err := client.Issues.SubscribeToIssue("1", 5)
	if err != nil {
		t.Fatal(err)
	}

	want := &Issue{
		ID:          1,
		Title:       "Title of issue",
		Description: "This is description of an issue",
		Author:      &IssueAuthor{ID: 1, Name: "snehal"},
		Assignees:   []*IssueAssignee{{ID: 1}},
	}

	if !reflect.DeepEqual(want, issue) {
		t.Errorf("Issues.SubscribeToIssue returned %+v, want %+v", issue, want)
	}
}

func TestUnsubscribeFromIssue(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/issues/5/unsubscribe", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		fmt.Fprint(w, `{"id":1, "title" : "Title of issue", "description": "This is description of an issue", "author" : {"id" : 1, "name": "snehal"}, "assignees":[{"id":1}]}`)
	})

	issue, _, err := client.Issues.UnsubscribeFromIssue("1", 5)
	if err != nil {
		t.Fatal(err)
	}

	want := &Issue{
		ID:          1,
		Title:       "Title of issue",
		Description: "This is description of an issue",
		Author:      &IssueAuthor{ID: 1, Name: "snehal"},
		Assignees:   []*IssueAssignee{{ID: 1}},
	}

	if !reflect.DeepEqual(want, issue) {
		t.Errorf("Issues.UnsubscribeFromIssue returned %+v, want %+v", issue, want)
	}
}

func TestListMergeRequestsClosingIssue(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/issues/5/closed_by", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		testURL(t, r, "/api/v4/projects/1/issues/5/closed_by?page=1&per_page=10")

		fmt.Fprint(w, `[{"id":1, "title" : "test merge one"},{"id":2, "title" : "test merge two"}]`)
	})

	listMergeRequestsClosingIssueOpt := &ListMergeRequestsClosingIssueOptions{
		Page:    1,
		PerPage: 10,
	}
	mergeRequest, _, err := client.Issues.ListMergeRequestsClosingIssue("1", 5, listMergeRequestsClosingIssueOpt)
	if err != nil {
		t.Fatal(err)
	}

	want := []*MergeRequest{{ID: 1, Title: "test merge one"}, {ID: 2, Title: "test merge two"}}

	if !reflect.DeepEqual(want, mergeRequest) {
		t.Errorf("Issues.ListMergeRequestsClosingIssue returned %+v, want %+v", mergeRequest, want)
	}
}

func TestListMergeRequestsRelatedToIssue(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/issues/5/related_merge_requests", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		testURL(t, r, "/api/v4/projects/1/issues/5/related_merge_requests?page=1&per_page=10")

		fmt.Fprint(w, `[{"id":1, "title" : "test merge one"},{"id":2, "title" : "test merge two"}]`)
	})

	listMergeRequestsRelatedToIssueOpt := &ListMergeRequestsRelatedToIssueOptions{
		Page:    1,
		PerPage: 10,
	}
	mergeRequest, _, err := client.Issues.ListMergeRequestsRelatedToIssue("1", 5, listMergeRequestsRelatedToIssueOpt)
	if err != nil {
		t.Fatal(err)
	}

	want := []*MergeRequest{{ID: 1, Title: "test merge one"}, {ID: 2, Title: "test merge two"}}

	if !reflect.DeepEqual(want, mergeRequest) {
		t.Errorf("Issues.ListMergeRequestsClosingIssue returned %+v, want %+v", mergeRequest, want)
	}
}

func TestSetTimeEstimate(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/issues/5/time_estimate", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		fmt.Fprint(w, `{"human_time_estimate": "3h 30m", "human_total_time_spent": null, "time_estimate": 12600, "total_time_spent": 0}`)
	})

	setTimeEstiOpt := &SetTimeEstimateOptions{
		Duration: Ptr("3h 30m"),
	}

	timeState, _, err := client.Issues.SetTimeEstimate("1", 5, setTimeEstiOpt)
	if err != nil {
		t.Fatal(err)
	}
	want := &TimeStats{HumanTimeEstimate: "3h 30m", HumanTotalTimeSpent: "", TimeEstimate: 12600, TotalTimeSpent: 0}

	if !reflect.DeepEqual(want, timeState) {
		t.Errorf("Issues.SetTimeEstimate returned %+v, want %+v", timeState, want)
	}
}

func TestResetTimeEstimate(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/issues/5/reset_time_estimate", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		fmt.Fprint(w, `{"human_time_estimate": null, "human_total_time_spent": null, "time_estimate": 0, "total_time_spent": 0}`)
	})

	timeState, _, err := client.Issues.ResetTimeEstimate("1", 5)
	if err != nil {
		t.Fatal(err)
	}
	want := &TimeStats{HumanTimeEstimate: "", HumanTotalTimeSpent: "", TimeEstimate: 0, TotalTimeSpent: 0}

	if !reflect.DeepEqual(want, timeState) {
		t.Errorf("Issues.ResetTimeEstimate returned %+v, want %+v", timeState, want)
	}
}

func TestAddSpentTime(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/issues/5/add_spent_time", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		testURL(t, r, "/api/v4/projects/1/issues/5/add_spent_time")
		testBody(t, r, `{"duration":"1h","summary":"test"}`)
		fmt.Fprint(w, `{"human_time_estimate": null, "human_total_time_spent": "1h", "time_estimate": 0, "total_time_spent": 3600}`)
	})
	addSpentTimeOpt := &AddSpentTimeOptions{
		Duration: Ptr("1h"),
		Summary:  Ptr("test"),
	}

	timeState, _, err := client.Issues.AddSpentTime("1", 5, addSpentTimeOpt)
	if err != nil {
		t.Fatal(err)
	}
	want := &TimeStats{HumanTimeEstimate: "", HumanTotalTimeSpent: "1h", TimeEstimate: 0, TotalTimeSpent: 3600}

	if !reflect.DeepEqual(want, timeState) {
		t.Errorf("Issues.AddSpentTime returned %+v, want %+v", timeState, want)
	}
}

func TestResetSpentTime(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/issues/5/reset_spent_time", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		testURL(t, r, "/api/v4/projects/1/issues/5/reset_spent_time")
		fmt.Fprint(w, `{"human_time_estimate": null, "human_total_time_spent": "", "time_estimate": 0, "total_time_spent": 0}`)
	})

	timeState, _, err := client.Issues.ResetSpentTime("1", 5)
	if err != nil {
		t.Fatal(err)
	}

	want := &TimeStats{HumanTimeEstimate: "", HumanTotalTimeSpent: "", TimeEstimate: 0, TotalTimeSpent: 0}
	if !reflect.DeepEqual(want, timeState) {
		t.Errorf("Issues.ResetSpentTime returned %+v, want %+v", timeState, want)
	}
}

func TestGetTimeSpent(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/issues/5/time_stats", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		testURL(t, r, "/api/v4/projects/1/issues/5/time_stats")
		fmt.Fprint(w, `{"human_time_estimate": "2h", "human_total_time_spent": "1h", "time_estimate": 7200, "total_time_spent": 3600}`)
	})

	timeState, _, err := client.Issues.GetTimeSpent("1", 5)
	if err != nil {
		t.Fatal(err)
	}

	want := &TimeStats{HumanTimeEstimate: "2h", HumanTotalTimeSpent: "1h", TimeEstimate: 7200, TotalTimeSpent: 3600}
	if !reflect.DeepEqual(want, timeState) {
		t.Errorf("Issues.GetTimeSpent returned %+v, want %+v", timeState, want)
	}
}

func TestGetIssueParticipants(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/issues/5/participants", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		testURL(t, r, "/api/v4/projects/1/issues/5/participants")

		fmt.Fprint(w, `[{"id":1,"name":"User1","username":"User1","state":"active","avatar_url":"","web_url":"https://localhost/User1"},
		{"id":2,"name":"User2","username":"User2","state":"active","avatar_url":"https://localhost/uploads/-/system/user/avatar/2/avatar.png","web_url":"https://localhost/User2"}]`)
	})

	issueParticipants, _, err := client.Issues.GetParticipants("1", 5)
	if err != nil {
		t.Fatal(err)
	}

	want := []*BasicUser{
		{ID: 1, Name: "User1", Username: "User1", State: "active", AvatarURL: "", WebURL: "https://localhost/User1"},
		{ID: 2, Name: "User2", Username: "User2", State: "active", AvatarURL: "https://localhost/uploads/-/system/user/avatar/2/avatar.png", WebURL: "https://localhost/User2"},
	}

	if !reflect.DeepEqual(want, issueParticipants) {
		t.Errorf("Issues.GetIssueParticipants returned %+v, want %+v", issueParticipants, want)
	}
}

func TestGetIssueMilestone(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/issues/5", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprint(w, `{"id":1, "description": "This is test project", "author" : {"id" : 1, "name": "snehal"}, "assignees":[{"id":1}],"merge_requests_count": 1,
			"milestone": {"due_date": null, "project_id": 1, "state": "closed", "description": "test", "iid": 3, "id": 11, "title": "v3.0"}}`)
	})

	issue, _, err := client.Issues.GetIssue("1", 5)
	if err != nil {
		t.Fatal(err)
	}

	want := &Issue{
		ID:                1,
		Description:       "This is test project",
		Author:            &IssueAuthor{ID: 1, Name: "snehal"},
		Assignees:         []*IssueAssignee{{ID: 1}},
		MergeRequestCount: 1,
		Milestone: &Milestone{
			DueDate:     nil,
			ProjectID:   1,
			State:       "closed",
			Description: "test",
			IID:         3,
			ID:          11,
			Title:       "v3.0",
		},
	}

	if !reflect.DeepEqual(want, issue) {
		t.Errorf("Issues.GetIssue returned %+v, want %+v", issue, want)
	}
}

func TestGetIssueGroupMilestone(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/issues/5", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprint(w, `{"id":1, "description": "This is test project", "author" : {"id" : 1, "name": "snehal"}, "assignees":[{"id":1}],"merge_requests_count": 1,
			"milestone": {"due_date": null, "group_id": 13, "state": "closed", "description": "test", "iid": 3, "id": 11, "title": "v3.0"}}`)
	})

	issue, _, err := client.Issues.GetIssue("1", 5)
	if err != nil {
		t.Fatal(err)
	}

	want := &Issue{
		ID:                1,
		Description:       "This is test project",
		Author:            &IssueAuthor{ID: 1, Name: "snehal"},
		Assignees:         []*IssueAssignee{{ID: 1}},
		MergeRequestCount: 1,
		Milestone: &Milestone{
			DueDate:     nil,
			GroupID:     13,
			State:       "closed",
			Description: "test",
			IID:         3,
			ID:          11,
			Title:       "v3.0",
		},
	}

	if !reflect.DeepEqual(want, issue) {
		t.Errorf("Issues.GetIssue returned %+v, want %+v", issue, want)
	}
}

func TestGetIssueWithServiceDesk(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/issues/5", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprint(w, `{"id":1, "description": "This is test project", "author" : {"id" : 1, "name": "snehal"}, "assignees":[{"id":1}],"service_desk_reply_to": "snehal@test.com"}`)
	})

	issue, _, err := client.Issues.GetIssue("1", 5)
	if err != nil {
		t.Fatal(err)
	}

	want := &Issue{
		ID:                 1,
		Description:        "This is test project",
		Author:             &IssueAuthor{ID: 1, Name: "snehal"},
		Assignees:          []*IssueAssignee{{ID: 1}},
		ServiceDeskReplyTo: "snehal@test.com",
	}

	if !reflect.DeepEqual(want, issue) {
		t.Errorf("Issues.GetIssue returned %+v, want %+v", issue, want)
	}
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/job_token_scope.go000066400000000000000000000230571475761473200245600ustar00rootroot00000000000000// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//	http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package gitlab

import (
	"fmt"
	"net/http"
)

// JobTokenScopeService handles communication with project CI settings
// such as token permissions.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/project_job_token_scopes.html
type JobTokenScopeService struct {
	client *Client
}

// JobTokenAccessSettings represents job token access attributes for this project.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/project_job_token_scopes.html
type JobTokenAccessSettings struct {
	InboundEnabled  bool `json:"inbound_enabled"`
	OutboundEnabled bool `json:"outbound_enabled"`
}

// GetProjectJobTokenAccessSettings fetch the CI/CD job token access settings (job token scope) of a project.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/project_job_token_scopes.html#get-a-projects-cicd-job-token-access-settings
func (j *JobTokenScopeService) GetProjectJobTokenAccessSettings(pid interface{}, options ...RequestOptionFunc) (*JobTokenAccessSettings, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf(`projects/%s/job_token_scope`, PathEscape(project))

	req, err := j.client.NewRequest(http.MethodGet, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	jt := new(JobTokenAccessSettings)
	resp, err := j.client.Do(req, jt)
	if err != nil {
		return nil, resp, err
	}

	return jt, resp, err
}

// PatchProjectJobTokenAccessSettingsOptions represents the available
// PatchProjectJobTokenAccessSettings() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/project_job_token_scopes.html#patch-a-projects-cicd-job-token-access-settings
type PatchProjectJobTokenAccessSettingsOptions struct {
	Enabled bool `json:"enabled"`
}

// PatchProjectJobTokenAccessSettings patch the Limit access to this project setting (job token scope) of a project.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/project_job_token_scopes.html#patch-a-projects-cicd-job-token-access-settings
func (j *JobTokenScopeService) PatchProjectJobTokenAccessSettings(pid interface{}, opt *PatchProjectJobTokenAccessSettingsOptions, options ...RequestOptionFunc) (*Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf(`projects/%s/job_token_scope`, PathEscape(project))

	req, err := j.client.NewRequest(http.MethodPatch, u, opt, options)
	if err != nil {
		return nil, err
	}

	return j.client.Do(req, nil)
}

// JobTokenInboundAllowItem represents a single job token inbound allowlist item.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/project_job_token_scopes.html
type JobTokenInboundAllowItem struct {
	SourceProjectID int `json:"source_project_id"`
	TargetProjectID int `json:"target_project_id"`
}

// GetJobTokenInboundAllowListOptions represents the available
// GetJobTokenInboundAllowList() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/project_job_token_scopes.html#get-a-projects-cicd-job-token-inbound-allowlist
type GetJobTokenInboundAllowListOptions struct {
	ListOptions
}

// GetProjectJobTokenInboundAllowList fetches the CI/CD job token inbound
// allowlist (job token scope) of a project.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/project_job_token_scopes.html#get-a-projects-cicd-job-token-inbound-allowlist
func (j *JobTokenScopeService) GetProjectJobTokenInboundAllowList(pid interface{}, opt *GetJobTokenInboundAllowListOptions, options ...RequestOptionFunc) ([]*Project, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf(`projects/%s/job_token_scope/allowlist`, PathEscape(project))

	req, err := j.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var ps []*Project
	resp, err := j.client.Do(req, &ps)
	if err != nil {
		return nil, resp, err
	}

	return ps, resp, nil
}

// AddProjectToJobScopeAllowListOptions represents the available
// AddProjectToJobScopeAllowList() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/project_job_token_scopes.html#create-a-new-project-to-a-projects-cicd-job-token-inbound-allowlist
type JobTokenInboundAllowOptions struct {
	TargetProjectID *int `url:"target_project_id,omitempty" json:"target_project_id,omitempty"`
}

// AddProjectToJobScopeAllowList adds a new project to a project's job token
// inbound allow list.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/project_job_token_scopes.html#create-a-new-project-to-a-projects-cicd-job-token-inbound-allowlist
func (j *JobTokenScopeService) AddProjectToJobScopeAllowList(pid interface{}, opt *JobTokenInboundAllowOptions, options ...RequestOptionFunc) (*JobTokenInboundAllowItem, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf(`projects/%s/job_token_scope/allowlist`, PathEscape(project))

	req, err := j.client.NewRequest(http.MethodPost, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	jt := new(JobTokenInboundAllowItem)
	resp, err := j.client.Do(req, jt)
	if err != nil {
		return nil, resp, err
	}

	return jt, resp, nil
}

// RemoveProjectFromJobScopeAllowList removes a project from a project's job
// token inbound allow list.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/project_job_token_scopes.html#remove-a-project-from-a-projects-cicd-job-token-inbound-allowlist
func (j *JobTokenScopeService) RemoveProjectFromJobScopeAllowList(pid interface{}, targetProject int, options ...RequestOptionFunc) (*Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf(`projects/%s/job_token_scope/allowlist/%d`, PathEscape(project), targetProject)

	req, err := j.client.NewRequest(http.MethodDelete, u, nil, options)
	if err != nil {
		return nil, err
	}

	return j.client.Do(req, nil)
}

// JobTokenAllowlistItem represents a single job token allowlist item.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/project_job_token_scopes.html
type JobTokenAllowlistItem struct {
	SourceProjectID int `json:"source_project_id"`
	TargetGroupID   int `json:"target_group_id"`
}

// GetJobTokenAllowlistGroupsOptions represents the available
// GetJobTokenAllowlistGroups() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/project_job_token_scopes.html#get-a-projects-cicd-job-token-allowlist-of-groups
type GetJobTokenAllowlistGroupsOptions struct {
	ListOptions
}

// GetJobTokenAllowListGroups fetches the CI/CD job token allowlist groups
// (job token scopes) of a project.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/project_job_token_scopes.html#get-a-projects-cicd-job-token-allowlist-of-groups
func (j *JobTokenScopeService) GetJobTokenAllowlistGroups(pid interface{}, opt *GetJobTokenAllowlistGroupsOptions, options ...RequestOptionFunc) ([]*Group, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf(`projects/%s/job_token_scope/groups_allowlist`, PathEscape(project))

	req, err := j.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var ps []*Group
	resp, err := j.client.Do(req, &ps)
	if err != nil {
		return nil, resp, err
	}

	return ps, resp, nil
}

// AddGroupToJobTokenAllowlistOptions represents the available
// AddGroupToJobTokenAllowlist() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/project_job_token_scopes.html#add-a-group-to-a-cicd-job-token-allowlist
type AddGroupToJobTokenAllowlistOptions struct {
	TargetGroupID *int `url:"target_group_id,omitempty" json:"target_group_id,omitempty"`
}

// AddProjectToJobScopeGroupsAllowList adds a new group to a project's job token
// inbound groups allow list.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/project_job_token_scopes.html#add-a-group-to-a-cicd-job-token-allowlist
func (j *JobTokenScopeService) AddGroupToJobTokenAllowlist(pid interface{}, opt *AddGroupToJobTokenAllowlistOptions, options ...RequestOptionFunc) (*JobTokenAllowlistItem, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf(`projects/%s/job_token_scope/groups_allowlist`, PathEscape(project))

	req, err := j.client.NewRequest(http.MethodPost, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	jt := new(JobTokenAllowlistItem)
	resp, err := j.client.Do(req, jt)
	if err != nil {
		return nil, resp, err
	}

	return jt, resp, nil
}

// RemoveGroupFromJopTokenAllowlist removes a group from a project's job
// token inbound groups allow list.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/project_job_token_scopes.html#remove-a-group-from-a-cicd-job-token-allowlist
func (j *JobTokenScopeService) RemoveGroupFromJobTokenAllowlist(pid interface{}, targetGroup int, options ...RequestOptionFunc) (*Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf(`projects/%s/job_token_scope/groups_allowlist/%d`, PathEscape(project), targetGroup)

	req, err := j.client.NewRequest(http.MethodDelete, u, nil, options)
	if err != nil {
		return nil, err
	}

	return j.client.Do(req, nil)
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/job_token_scope_test.go000066400000000000000000000210021475761473200256030ustar00rootroot00000000000000// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//	http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package gitlab

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestGetProjectTokenAccessSettings(t *testing.T) {
	mux, client := setup(t)

	// Handle project ID 1, and print a result of access settings
	mux.HandleFunc("/api/v4/projects/1/job_token_scope", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)

		// Print on the response
		fmt.Fprint(w, `{"inbound_enabled":true,"outbound_enabled":false}`)
	})

	want := &JobTokenAccessSettings{
		InboundEnabled:  true,
		OutboundEnabled: false,
	}

	settings, _, err := client.JobTokenScope.GetProjectJobTokenAccessSettings(1)

	assert.NoError(t, err)
	assert.Equal(t, want, settings)
}

func TestPatchProjectJobTokenAccessSettings(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/job_token_scope", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPatch)

		// Read the request to determine which target project is passed in
		body, err := io.ReadAll(r.Body)
		if err != nil {
			t.Fatalf("JobTokenScope.PatchProjectJobTokenAccessSettings failed to read body")
		}

		// Parse to object to ensure it's sent on the request appropriately.
		var options PatchProjectJobTokenAccessSettingsOptions
		err = json.Unmarshal(body, &options)
		if err != nil {
			t.Fatalf("JobTokenScope.PatchProjectJobTokenAccessSettings failed to unmarshal body: %v", err)
		}

		// Ensure we provide the proper response
		w.WriteHeader(http.StatusNoContent)

		// Print an empty body, since that's what the API provides.
		fmt.Fprint(w, "")
	})

	resp, err := client.JobTokenScope.PatchProjectJobTokenAccessSettings(
		1,
		&PatchProjectJobTokenAccessSettingsOptions{
			Enabled: false,
		},
	)
	assert.NoError(t, err)
	assert.Equal(t, 204, resp.StatusCode)
}

// This tests that when calling the GetProjectJobTokenInboundAllowList, we get a
// list of projects back properly. There isn't a "deep" test with every attribute
// specifieid, because the object returned is a *Project object, which is already
// tested in project.go.
func TestGetProjectJobTokenInboundAllowList(t *testing.T) {
	mux, client := setup(t)

	// Handle project ID 1, and print a result of two projects
	mux.HandleFunc("/api/v4/projects/1/job_token_scope/allowlist", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)

		// Print on the response
		fmt.Fprint(w, `[{"id":1},{"id":2}]`)
	})

	want := []*Project{{ID: 1}, {ID: 2}}
	projects, _, err := client.JobTokenScope.GetProjectJobTokenInboundAllowList(
		1,
		&GetJobTokenInboundAllowListOptions{},
	)

	assert.NoError(t, err)
	assert.Equal(t, want, projects)
}

func TestAddProjectToJobScopeAllowList(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/job_token_scope/allowlist", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)

		// Read the request to determine which target project is passed in
		body, err := io.ReadAll(r.Body)
		if err != nil {
			t.Fatalf("JobTokenScope.AddProjectToJobScopeAllowList failed to read body")
		}

		// Parse to object to ensure it's sent on the request appropriately.
		var createTokenRequest JobTokenInboundAllowOptions
		err = json.Unmarshal(body, &createTokenRequest)
		if err != nil {
			t.Fatalf("JobTokenScope.AddProjectToJobScopeAllowList failed to unmarshal body: %v", err)
		}

		// Ensure we provide the proper response
		w.WriteHeader(http.StatusCreated)

		// Print on the response with the proper target project
		fmt.Fprintf(w, `{
			"source_project_id": 1,
			"target_project_id": %d
		}`, *createTokenRequest.TargetProjectID)
	})

	want := &JobTokenInboundAllowItem{
		SourceProjectID: 1,
		TargetProjectID: 2,
	}

	addTokenResponse, resp, err := client.JobTokenScope.AddProjectToJobScopeAllowList(
		1,
		&JobTokenInboundAllowOptions{TargetProjectID: Ptr(2)},
	)
	assert.NoError(t, err)
	assert.Equal(t, want, addTokenResponse)
	assert.Equal(t, 201, resp.StatusCode)
}

func TestRemoveProjectFromJobScopeAllowList(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/job_token_scope/allowlist/2", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodDelete)

		// Read the request to determine which target project is passed in
		body, err := io.ReadAll(r.Body)
		if err != nil {
			t.Fatalf("JobTokenScope.RemoveProjectFromJobScopeAllowList failed to read body")
		}

		// The body should be empty since all attributes are passed in the path
		if body != nil && string(body) != "" {
			t.Fatalf("JobTokenScope.RemoveProjectFromJobScopeAllowList failed to unmarshal body: %v", err)
		}

		// Ensure we provide the proper response
		w.WriteHeader(http.StatusNoContent)

		// Print an empty body, since that's what the API provides.
		fmt.Fprint(w, "")
	})

	resp, err := client.JobTokenScope.RemoveProjectFromJobScopeAllowList(1, 2)
	assert.NoError(t, err)
	assert.Equal(t, 204, resp.StatusCode)
}

// This tests that when calling the GetJobTokenAllowlistGroups, we get a list
// of groups back. There isn't a "deep" test with every attribute specified,
// because the object returned is a *Group object, which is already tested in
// groups.go.
func TestGetJobTokenAllowlistGroups(t *testing.T) {
	mux, client := setup(t)

	// Handle project ID 1, and print a result of two groups
	mux.HandleFunc("/api/v4/projects/1/job_token_scope/groups_allowlist", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)

		// Print on the response
		fmt.Fprint(w, `[{"id":1},{"id":2}]`)
	})

	want := []*Group{{ID: 1}, {ID: 2}}
	groups, _, err := client.JobTokenScope.GetJobTokenAllowlistGroups(
		1,
		&GetJobTokenAllowlistGroupsOptions{},
	)

	assert.NoError(t, err)
	assert.Equal(t, want, groups)
}

func TestAddGroupToJobTokenAllowlist(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/job_token_scope/groups_allowlist", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)

		// Read the request to determine which target group is passed in
		body, err := io.ReadAll(r.Body)
		if err != nil {
			t.Fatalf("JobTokenScope.AddGroupToJobTokenAllowlist failed to read body")
		}

		// Parse to object to ensure it's sent on the request appropriately.
		var createTokenRequest AddGroupToJobTokenAllowlistOptions
		err = json.Unmarshal(body, &createTokenRequest)
		if err != nil {
			t.Fatalf("JobTokenScope.AddGroupToJobTokenAllowlist failed to unmarshal body: %v", err)
		}

		// Ensure we provide the proper response
		w.WriteHeader(http.StatusCreated)

		// Print on the response with the proper target group
		fmt.Fprintf(w, `{
			"source_project_id": 1,
			"target_group_id": %d
		}`, *createTokenRequest.TargetGroupID)
	})

	want := &JobTokenAllowlistItem{
		SourceProjectID: 1,
		TargetGroupID:   2,
	}

	addTokenResponse, resp, err := client.JobTokenScope.AddGroupToJobTokenAllowlist(
		1,
		&AddGroupToJobTokenAllowlistOptions{TargetGroupID: Ptr(2)},
	)
	assert.NoError(t, err)
	assert.Equal(t, want, addTokenResponse)
	assert.Equal(t, 201, resp.StatusCode)
}

func TestRemoveGroupFromJobTokenAllowlist(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/job_token_scope/groups_allowlist/2", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodDelete)

		// Read the request to determine which target group is passed in
		body, err := io.ReadAll(r.Body)
		if err != nil {
			t.Fatalf("JobTokenScope.RemoveGroupFromJobTokenAllowlist failed to read body")
		}

		// The body should be empty since all attributes are passed in the path
		if body != nil && string(body) != "" {
			t.Fatalf("JobTokenScope.RemoveGroupFromJobTokenAllowlist failed to unmarshal body: %v", err)
		}

		// Ensure we provide the proper response
		w.WriteHeader(http.StatusNoContent)

		// Print an empty body, since that's what the API provides.
		fmt.Fprint(w, "")
	})

	resp, err := client.JobTokenScope.RemoveGroupFromJobTokenAllowlist(1, 2)
	assert.NoError(t, err)
	assert.Equal(t, 204, resp.StatusCode)
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/jobs.go000066400000000000000000000425641475761473200223560ustar00rootroot00000000000000//
// Copyright 2021, Arkbriar
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"bytes"
	"fmt"
	"net/http"
	"time"
)

// JobsService handles communication with the ci builds related methods
// of the GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/jobs.html
type JobsService struct {
	client *Client
}

// Job represents a ci build.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/jobs.html
type Job struct {
	Commit            *Commit    `json:"commit"`
	Coverage          float64    `json:"coverage"`
	AllowFailure      bool       `json:"allow_failure"`
	CreatedAt         *time.Time `json:"created_at"`
	StartedAt         *time.Time `json:"started_at"`
	FinishedAt        *time.Time `json:"finished_at"`
	ErasedAt          *time.Time `json:"erased_at"`
	Duration          float64    `json:"duration"`
	QueuedDuration    float64    `json:"queued_duration"`
	ArtifactsExpireAt *time.Time `json:"artifacts_expire_at"`
	TagList           []string   `json:"tag_list"`
	ID                int        `json:"id"`
	Name              string     `json:"name"`
	Pipeline          struct {
		ID        int    `json:"id"`
		ProjectID int    `json:"project_id"`
		Ref       string `json:"ref"`
		Sha       string `json:"sha"`
		Status    string `json:"status"`
	} `json:"pipeline"`
	Ref       string `json:"ref"`
	Artifacts []struct {
		FileType   string `json:"file_type"`
		Filename   string `json:"filename"`
		Size       int    `json:"size"`
		FileFormat string `json:"file_format"`
	} `json:"artifacts"`
	ArtifactsFile struct {
		Filename string `json:"filename"`
		Size     int    `json:"size"`
	} `json:"artifacts_file"`
	Runner struct {
		ID          int    `json:"id"`
		Description string `json:"description"`
		Active      bool   `json:"active"`
		IsShared    bool   `json:"is_shared"`
		Name        string `json:"name"`
	} `json:"runner"`
	Stage         string   `json:"stage"`
	Status        string   `json:"status"`
	FailureReason string   `json:"failure_reason"`
	Tag           bool     `json:"tag"`
	WebURL        string   `json:"web_url"`
	Project       *Project `json:"project"`
	User          *User    `json:"user"`
}

// Bridge represents a pipeline bridge.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/jobs.html#list-pipeline-bridges
type Bridge struct {
	Commit             *Commit       `json:"commit"`
	Coverage           float64       `json:"coverage"`
	AllowFailure       bool          `json:"allow_failure"`
	CreatedAt          *time.Time    `json:"created_at"`
	StartedAt          *time.Time    `json:"started_at"`
	FinishedAt         *time.Time    `json:"finished_at"`
	ErasedAt           *time.Time    `json:"erased_at"`
	Duration           float64       `json:"duration"`
	QueuedDuration     float64       `json:"queued_duration"`
	ID                 int           `json:"id"`
	Name               string        `json:"name"`
	Pipeline           PipelineInfo  `json:"pipeline"`
	Ref                string        `json:"ref"`
	Stage              string        `json:"stage"`
	Status             string        `json:"status"`
	FailureReason      string        `json:"failure_reason"`
	Tag                bool          `json:"tag"`
	WebURL             string        `json:"web_url"`
	User               *User         `json:"user"`
	DownstreamPipeline *PipelineInfo `json:"downstream_pipeline"`
}

// ListJobsOptions represents the available ListProjectJobs() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/jobs.html#list-project-jobs
type ListJobsOptions struct {
	ListOptions
	Scope          *[]BuildStateValue `url:"scope[],omitempty" json:"scope,omitempty"`
	IncludeRetried *bool              `url:"include_retried,omitempty" json:"include_retried,omitempty"`
}

// ListProjectJobs gets a list of jobs in a project.
//
// The scope of jobs to show, one or array of: created, pending, running,
// failed, success, canceled, skipped; showing all jobs if none provided
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/jobs.html#list-project-jobs
func (s *JobsService) ListProjectJobs(pid interface{}, opts *ListJobsOptions, options ...RequestOptionFunc) ([]*Job, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/jobs", PathEscape(project))

	req, err := s.client.NewRequest(http.MethodGet, u, opts, options)
	if err != nil {
		return nil, nil, err
	}

	var jobs []*Job
	resp, err := s.client.Do(req, &jobs)
	if err != nil {
		return nil, resp, err
	}

	return jobs, resp, nil
}

// ListPipelineJobs gets a list of jobs for specific pipeline in a
// project. If the pipeline ID is not found, it will respond with 404.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/jobs.html#list-pipeline-jobs
func (s *JobsService) ListPipelineJobs(pid interface{}, pipelineID int, opts *ListJobsOptions, options ...RequestOptionFunc) ([]*Job, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/pipelines/%d/jobs", PathEscape(project), pipelineID)

	req, err := s.client.NewRequest(http.MethodGet, u, opts, options)
	if err != nil {
		return nil, nil, err
	}

	var jobs []*Job
	resp, err := s.client.Do(req, &jobs)
	if err != nil {
		return nil, resp, err
	}

	return jobs, resp, nil
}

// ListPipelineBridges gets a list of bridges for specific pipeline in a
// project.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/jobs.html#list-pipeline-jobs
func (s *JobsService) ListPipelineBridges(pid interface{}, pipelineID int, opts *ListJobsOptions, options ...RequestOptionFunc) ([]*Bridge, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/pipelines/%d/bridges", PathEscape(project), pipelineID)

	req, err := s.client.NewRequest(http.MethodGet, u, opts, options)
	if err != nil {
		return nil, nil, err
	}

	var bridges []*Bridge
	resp, err := s.client.Do(req, &bridges)
	if err != nil {
		return nil, resp, err
	}

	return bridges, resp, nil
}

// GetJobTokensJobOptions represents the available GetJobTokensJob() options.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/jobs.html#get-job-tokens-job
type GetJobTokensJobOptions struct {
	JobToken *string `url:"job_token,omitempty" json:"job_token,omitempty"`
}

// GetJobTokensJob retrieves the job that generated a job token.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/jobs.html#get-job-tokens-job
func (s *JobsService) GetJobTokensJob(opts *GetJobTokensJobOptions, options ...RequestOptionFunc) (*Job, *Response, error) {
	req, err := s.client.NewRequest(http.MethodGet, "job", opts, options)
	if err != nil {
		return nil, nil, err
	}

	job := new(Job)
	resp, err := s.client.Do(req, job)
	if err != nil {
		return nil, resp, err
	}

	return job, resp, nil
}

// GetJob gets a single job of a project.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/jobs.html#get-a-single-job
func (s *JobsService) GetJob(pid interface{}, jobID int, options ...RequestOptionFunc) (*Job, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/jobs/%d", PathEscape(project), jobID)

	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	job := new(Job)
	resp, err := s.client.Do(req, job)
	if err != nil {
		return nil, resp, err
	}

	return job, resp, nil
}

// GetJobArtifacts get jobs artifacts of a project
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/job_artifacts.html#get-job-artifacts
func (s *JobsService) GetJobArtifacts(pid interface{}, jobID int, options ...RequestOptionFunc) (*bytes.Reader, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/jobs/%d/artifacts", PathEscape(project), jobID)

	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	artifactsBuf := new(bytes.Buffer)
	resp, err := s.client.Do(req, artifactsBuf)
	if err != nil {
		return nil, resp, err
	}

	return bytes.NewReader(artifactsBuf.Bytes()), resp, err
}

// DownloadArtifactsFileOptions represents the available DownloadArtifactsFile()
// options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/job_artifacts.html#download-the-artifacts-archive
type DownloadArtifactsFileOptions struct {
	Job *string `url:"job" json:"job"`
}

// DownloadArtifactsFile download the artifacts file from the given
// reference name and job provided the job finished successfully.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/job_artifacts.html#download-the-artifacts-archive
func (s *JobsService) DownloadArtifactsFile(pid interface{}, refName string, opt *DownloadArtifactsFileOptions, options ...RequestOptionFunc) (*bytes.Reader, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/jobs/artifacts/%s/download", PathEscape(project), refName)

	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	artifactsBuf := new(bytes.Buffer)
	resp, err := s.client.Do(req, artifactsBuf)
	if err != nil {
		return nil, resp, err
	}

	return bytes.NewReader(artifactsBuf.Bytes()), resp, err
}

// DownloadSingleArtifactsFile download a file from the artifacts from the
// given reference name and job provided the job finished successfully.
// Only a single file is going to be extracted from the archive and streamed
// to a client.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/job_artifacts.html#download-a-single-artifact-file-by-job-id
func (s *JobsService) DownloadSingleArtifactsFile(pid interface{}, jobID int, artifactPath string, options ...RequestOptionFunc) (*bytes.Reader, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}

	u := fmt.Sprintf(
		"projects/%s/jobs/%d/artifacts/%s",
		PathEscape(project),
		jobID,
		artifactPath,
	)

	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	artifactBuf := new(bytes.Buffer)
	resp, err := s.client.Do(req, artifactBuf)
	if err != nil {
		return nil, resp, err
	}

	return bytes.NewReader(artifactBuf.Bytes()), resp, err
}

// DownloadSingleArtifactsFile download a single artifact file for a specific
// job of the latest successful pipeline for the given reference name from
// inside the job’s artifacts archive. The file is extracted from the archive
// and streamed to the client.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/job_artifacts.html#download-a-single-artifact-file-from-specific-tag-or-branch
func (s *JobsService) DownloadSingleArtifactsFileByTagOrBranch(pid interface{}, refName string, artifactPath string, opt *DownloadArtifactsFileOptions, options ...RequestOptionFunc) (*bytes.Reader, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}

	u := fmt.Sprintf(
		"projects/%s/jobs/artifacts/%s/raw/%s",
		PathEscape(project),
		PathEscape(refName),
		artifactPath,
	)

	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	artifactBuf := new(bytes.Buffer)
	resp, err := s.client.Do(req, artifactBuf)
	if err != nil {
		return nil, resp, err
	}

	return bytes.NewReader(artifactBuf.Bytes()), resp, err
}

// GetTraceFile gets a trace of a specific job of a project
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/jobs.html#get-a-log-file
func (s *JobsService) GetTraceFile(pid interface{}, jobID int, options ...RequestOptionFunc) (*bytes.Reader, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/jobs/%d/trace", PathEscape(project), jobID)

	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	traceBuf := new(bytes.Buffer)
	resp, err := s.client.Do(req, traceBuf)
	if err != nil {
		return nil, resp, err
	}

	return bytes.NewReader(traceBuf.Bytes()), resp, err
}

// CancelJob cancels a single job of a project.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/jobs.html#cancel-a-job
func (s *JobsService) CancelJob(pid interface{}, jobID int, options ...RequestOptionFunc) (*Job, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/jobs/%d/cancel", PathEscape(project), jobID)

	req, err := s.client.NewRequest(http.MethodPost, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	job := new(Job)
	resp, err := s.client.Do(req, job)
	if err != nil {
		return nil, resp, err
	}

	return job, resp, nil
}

// RetryJob retries a single job of a project
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/jobs.html#retry-a-job
func (s *JobsService) RetryJob(pid interface{}, jobID int, options ...RequestOptionFunc) (*Job, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/jobs/%d/retry", PathEscape(project), jobID)

	req, err := s.client.NewRequest(http.MethodPost, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	job := new(Job)
	resp, err := s.client.Do(req, job)
	if err != nil {
		return nil, resp, err
	}

	return job, resp, nil
}

// EraseJob erases a single job of a project, removes a job
// artifacts and a job trace.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/jobs.html#erase-a-job
func (s *JobsService) EraseJob(pid interface{}, jobID int, options ...RequestOptionFunc) (*Job, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/jobs/%d/erase", PathEscape(project), jobID)

	req, err := s.client.NewRequest(http.MethodPost, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	job := new(Job)
	resp, err := s.client.Do(req, job)
	if err != nil {
		return nil, resp, err
	}

	return job, resp, nil
}

// KeepArtifacts prevents artifacts from being deleted when
// expiration is set.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/job_artifacts.html#keep-artifacts
func (s *JobsService) KeepArtifacts(pid interface{}, jobID int, options ...RequestOptionFunc) (*Job, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/jobs/%d/artifacts/keep", PathEscape(project), jobID)

	req, err := s.client.NewRequest(http.MethodPost, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	job := new(Job)
	resp, err := s.client.Do(req, job)
	if err != nil {
		return nil, resp, err
	}

	return job, resp, nil
}

// PlayJobOptions represents the available PlayJob() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/jobs.html#run-a-job
type PlayJobOptions struct {
	JobVariablesAttributes *[]*JobVariableOptions `url:"job_variables_attributes,omitempty" json:"job_variables_attributes,omitempty"`
}

// JobVariableOptions represents a single job variable.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/jobs.html#run-a-job
type JobVariableOptions struct {
	Key          *string            `url:"key,omitempty" json:"key,omitempty"`
	Value        *string            `url:"value,omitempty" json:"value,omitempty"`
	VariableType *VariableTypeValue `url:"variable_type,omitempty" json:"variable_type,omitempty"`
}

// PlayJob triggers a manual action to start a job.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/jobs.html#run-a-job
func (s *JobsService) PlayJob(pid interface{}, jobID int, opt *PlayJobOptions, options ...RequestOptionFunc) (*Job, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/jobs/%d/play", PathEscape(project), jobID)

	req, err := s.client.NewRequest(http.MethodPost, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	job := new(Job)
	resp, err := s.client.Do(req, job)
	if err != nil {
		return nil, resp, err
	}

	return job, resp, nil
}

// DeleteArtifacts delete artifacts of a job
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/job_artifacts.html#delete-job-artifacts
func (s *JobsService) DeleteArtifacts(pid interface{}, jobID int, options ...RequestOptionFunc) (*Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("projects/%s/jobs/%d/artifacts", PathEscape(project), jobID)

	req, err := s.client.NewRequest(http.MethodDelete, u, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}

// DeleteProjectArtifacts delete artifacts eligible for deletion in a project
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/job_artifacts.html#delete-project-artifacts
func (s *JobsService) DeleteProjectArtifacts(pid interface{}, options ...RequestOptionFunc) (*Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("projects/%s/artifacts", PathEscape(project))

	req, err := s.client.NewRequest(http.MethodDelete, u, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/jobs_test.go000066400000000000000000000140241475761473200234030ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"fmt"
	"io"
	"net/http"
	"reflect"
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestListPipelineJobs(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/pipelines/1/jobs", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprint(w, `[{"id":1},{"id":2}]`)
	})

	jobs, _, err := client.Jobs.ListPipelineJobs(1, 1, nil)
	if err != nil {
		t.Errorf("Jobs.ListPipelineJobs returned error: %v", err)
	}

	want := []*Job{{ID: 1}, {ID: 2}}
	if !reflect.DeepEqual(want, jobs) {
		t.Errorf("Jobs.ListPipelineJobs returned %+v, want %+v", jobs, want)
	}
}

func TestJobsService_ListProjectJobs(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/jobs", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprint(w, `[
  {
    "commit": {
      "author_email": "admin@example.com",
      "author_name": "Administrator",
      "id": "0ff3ae198f8601a285adcf5c0fff204ee6fba5fd",
      "short_id": "0ff3ae19",
      "title": "Test the CI integration."
    },
    "allow_failure": false,
    "tag_list": [
      "docker runner",
      "ubuntu18"
    ],
    "id": 7,
    "name": "teaspoon",
    "pipeline": {
      "id": 6,
      "project_id": 1,
      "ref": "master",
      "sha": "0ff3ae198f8601a285adcf5c0fff204ee6fba5fd",
      "status": "pending"
    },
    "ref": "master",
    "stage": "test",
    "status": "failed",
	  "failure_reason": "script_failure",
    "tag": false,
    "web_url": "https://example.com/foo/bar/-/jobs/7"
  },
  {
    "commit": {
      "author_email": "admin@example.com",
      "author_name": "Administrator",
      "id": "0ff3ae198f8601a285adcf5c0fff204ee6fba5fd",
      "message": "Test the CI integration.",
      "short_id": "0ff3ae19",
      "title": "Test the CI integration."
    },
    "allow_failure": false,
    "duration": 0.192,
    "tag_list": [
      "docker runner",
      "win10-2004"
    ],
    "id": 6,
    "name": "rspec:other",
    "pipeline": {
      "id": 6,
      "project_id": 1,
      "ref": "master",
      "sha": "0ff3ae198f8601a285adcf5c0fff204ee6fba5fd",
      "status": "pending"
    },
    "ref": "master",
    "runner": null,
    "stage": "test",
    "status": "failed",
    "tag": false,
    "web_url": "https://example.com/foo/bar/-/jobs/6"
  }
]`)
	})

	jobs, _, err := client.Jobs.ListProjectJobs(1, nil, nil)
	if err != nil {
		t.Errorf("Jobs.ListProjectJobs returned error: %v", err)
	}

	want := []*Job{
		{
			Commit: &Commit{
				ID:          "0ff3ae198f8601a285adcf5c0fff204ee6fba5fd",
				ShortID:     "0ff3ae19",
				Title:       "Test the CI integration.",
				AuthorName:  "Administrator",
				AuthorEmail: "admin@example.com",
			},
			AllowFailure: false,
			ID:           7,
			Name:         "teaspoon",
			TagList:      []string{"docker runner", "ubuntu18"},
			Pipeline: struct {
				ID        int    `json:"id"`
				ProjectID int    `json:"project_id"`
				Ref       string `json:"ref"`
				Sha       string `json:"sha"`
				Status    string `json:"status"`
			}{
				ID:        6,
				ProjectID: 1,
				Ref:       "master",
				Sha:       "0ff3ae198f8601a285adcf5c0fff204ee6fba5fd",
				Status:    "pending",
			},
			Ref:           "master",
			Stage:         "test",
			Status:        "failed",
			FailureReason: "script_failure",
			Tag:           false,
			WebURL:        "https://example.com/foo/bar/-/jobs/7",
		},
		{
			Commit: &Commit{
				ID:          "0ff3ae198f8601a285adcf5c0fff204ee6fba5fd",
				ShortID:     "0ff3ae19",
				Title:       "Test the CI integration.",
				AuthorName:  "Administrator",
				AuthorEmail: "admin@example.com",
				Message:     "Test the CI integration.",
			},
			AllowFailure: false,
			Duration:     0.192,
			ID:           6,
			Name:         "rspec:other",
			TagList:      []string{"docker runner", "win10-2004"},
			Pipeline: struct {
				ID        int    `json:"id"`
				ProjectID int    `json:"project_id"`
				Ref       string `json:"ref"`
				Sha       string `json:"sha"`
				Status    string `json:"status"`
			}{
				ID:        6,
				ProjectID: 1,
				Ref:       "master",
				Sha:       "0ff3ae198f8601a285adcf5c0fff204ee6fba5fd",
				Status:    "pending",
			},
			Ref:    "master",
			Stage:  "test",
			Status: "failed",
			Tag:    false,
			WebURL: "https://example.com/foo/bar/-/jobs/6",
		},
	}
	assert.Equal(t, want, jobs)
}

func TestDownloadSingleArtifactsFileByTagOrBranch(t *testing.T) {
	mux, client := setup(t)

	wantContent := []byte("This is the file content")
	mux.HandleFunc("/api/v4/projects/9/jobs/artifacts/abranch/raw/foo/bar.pdf", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		w.WriteHeader(http.StatusOK)
		w.Write(wantContent)
	})

	opt := &DownloadArtifactsFileOptions{Job: Ptr("publish")}
	reader, resp, err := client.Jobs.DownloadSingleArtifactsFileByTagOrBranch(9, "abranch", "foo/bar.pdf", opt)
	if err != nil {
		t.Fatalf("Jobs.DownloadSingleArtifactsFileByTagOrBranch returns an error: %v", err)
	}

	content, err := io.ReadAll(reader)
	if err != nil {
		t.Fatalf("Jobs.DownloadSingleArtifactsFileByTagOrBranch error reading: %v", err)
	}
	if !reflect.DeepEqual(content, wantContent) {
		t.Errorf("Jobs.DownloadSingleArtifactsFileByTagOrBranch returned %+v, want %+v", content, wantContent)
	}

	wantCode := 200
	if !reflect.DeepEqual(wantCode, resp.StatusCode) {
		t.Errorf("Jobs.DownloadSingleArtifactsFileByTagOrBranch returned returned status code  %+v, want %+v", resp.StatusCode, wantCode)
	}
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/keys.go000066400000000000000000000053301475761473200223620ustar00rootroot00000000000000//
// Copyright 2021, Patrick Webster
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"fmt"
	"net/http"
	"time"
)

// KeysService handles communication with the
// keys related methods of the GitLab API.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/keys.html
type KeysService struct {
	client *Client
}

// Key represents a GitLab user's SSH key.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/keys.html
type Key struct {
	ID        int        `json:"id"`
	Title     string     `json:"title"`
	Key       string     `json:"key"`
	CreatedAt *time.Time `json:"created_at"`
	User      User       `json:"user"`
}

// GetKeyWithUser gets a single key by id along with the associated
// user information.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/keys.html#get-ssh-key-with-user-by-id-of-an-ssh-key
func (s *KeysService) GetKeyWithUser(key int, options ...RequestOptionFunc) (*Key, *Response, error) {
	u := fmt.Sprintf("keys/%d", key)

	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	k := new(Key)
	resp, err := s.client.Do(req, k)
	if err != nil {
		return nil, resp, err
	}

	return k, resp, nil
}

// GetKeyByFingerprintOptions represents the available GetKeyByFingerprint()
// options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/keys.html#get-user-by-fingerprint-of-ssh-key
// https://docs.gitlab.com/ee/api/keys.html#get-user-by-deploy-key-fingerprint
type GetKeyByFingerprintOptions struct {
	Fingerprint string `url:"fingerprint" json:"fingerprint"`
}

// GetKeyByFingerprint gets a specific SSH key or deploy key by fingerprint
// along with the associated user information.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/keys.html#get-user-by-fingerprint-of-ssh-key
// https://docs.gitlab.com/ee/api/keys.html#get-user-by-deploy-key-fingerprint
func (s *KeysService) GetKeyByFingerprint(opt *GetKeyByFingerprintOptions, options ...RequestOptionFunc) (*Key, *Response, error) {
	req, err := s.client.NewRequest(http.MethodGet, "keys", opt, options)
	if err != nil {
		return nil, nil, err
	}

	k := new(Key)
	resp, err := s.client.Do(req, k)
	if err != nil {
		return nil, resp, err
	}

	return k, resp, nil
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/keys_test.go000066400000000000000000000135361475761473200234300ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"fmt"
	"net/http"
	"reflect"
	"testing"
)

func TestGetKeyWithUser(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/keys/1",
		func(w http.ResponseWriter, r *http.Request) {
			testMethod(t, r, http.MethodGet)
			fmt.Fprint(w, `{
			  "id": 1,
			  "title": "Sample key 25",
			  "key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt1256k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=",
			  "user": {
			    "id": 25,
			    "username": "john_smith",
			    "name": "John Smith",
			    "email": "john@example.com",
			    "state": "active",
			    "bio": null,
			    "location": null,
			    "skype": "",
			    "linkedin": "",
			    "twitter": "",
			    "website_url": "http://localhost:3000/john_smith",
			    "organization": null,
			    "theme_id": 2,
			    "color_scheme_id": 1,
			    "avatar_url": "http://www.gravatar.com/avatar/cfa35b8cd2ec278026357769582fa563?s=40\u0026d=identicon",
			    "can_create_group": true,
			    "can_create_project": true,
			    "projects_limit": 10,
			    "two_factor_enabled": false,
			    "identities": [],
			    "external": false,
			    "public_email": "john@example.com"
			  }
			}`)
		})

	key, _, err := client.Keys.GetKeyWithUser(1)
	if err != nil {
		t.Errorf("Keys.GetKeyWithUser returned error: %v", err)
	}

	want := &Key{
		ID:    1,
		Title: "Sample key 25",
		Key:   "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt1256k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=",
		User: User{
			ID:               25,
			Username:         "john_smith",
			Email:            "john@example.com",
			Name:             exampleEventUserName,
			State:            "active",
			Bio:              "",
			Location:         "",
			Skype:            "",
			Linkedin:         "",
			Twitter:          "",
			WebsiteURL:       "http://localhost:3000/john_smith",
			Organization:     "",
			ThemeID:          2,
			ColorSchemeID:    1,
			AvatarURL:        "http://www.gravatar.com/avatar/cfa35b8cd2ec278026357769582fa563?s=40\u0026d=identicon",
			CanCreateGroup:   true,
			CanCreateProject: true,
			ProjectsLimit:    10,
			TwoFactorEnabled: false,
			Identities:       []*UserIdentity{},
			External:         false,
			PublicEmail:      "john@example.com",
		},
	}

	if !reflect.DeepEqual(want, key) {
		t.Errorf("Keys.GetKeyWithUser returned %+v, want %+v", key, want)
	}
}

func TestGetKeyByFingerprint(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/keys",
		func(w http.ResponseWriter, r *http.Request) {
			testMethod(t, r, http.MethodGet)
			if r.URL.Query().Get("fingerprint") == "07:51:20:af:17:e4:a8:ab:22:79:9b:31:ae:a9:61:f3" {
				fmt.Fprint(w, `{
			  "id": 1,
			  "title": "Sample key 25",
			  "key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt1256k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=",
			  "user": {
			    "id": 25,
			    "username": "john_smith",
			    "name": "John Smith",
			    "email": "john@example.com",
			    "state": "active",
			    "bio": null,
			    "location": null,
			    "skype": "",
			    "linkedin": "",
			    "twitter": "",
			    "website_url": "http://localhost:3000/john_smith",
			    "organization": null,
			    "theme_id": 2,
			    "color_scheme_id": 1,
			    "avatar_url": "http://www.gravatar.com/avatar/cfa35b8cd2ec278026357769582fa563?s=40\u0026d=identicon",
			    "can_create_group": true,
			    "can_create_project": true,
			    "projects_limit": 10,
			    "two_factor_enabled": false,
			    "identities": [],
			    "external": false,
			    "public_email": "john@example.com"
			  }
			}`)

				return
			}

			fmt.Fprint(w, `{}`)
		})

	key, _, err := client.Keys.GetKeyByFingerprint(&GetKeyByFingerprintOptions{
		Fingerprint: "07:51:20:af:17:e4:a8:ab:22:79:9b:31:ae:a9:61:f3",
	})
	if err != nil {
		t.Errorf("Keys.GetKeyWithUserByFingerprint returned error: %v", err)
	}

	want := &Key{
		ID:    1,
		Title: "Sample key 25",
		Key:   "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt1256k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=",
		User: User{
			ID:               25,
			Username:         "john_smith",
			Email:            "john@example.com",
			Name:             exampleEventUserName,
			State:            "active",
			Bio:              "",
			Location:         "",
			Skype:            "",
			Linkedin:         "",
			Twitter:          "",
			WebsiteURL:       "http://localhost:3000/john_smith",
			Organization:     "",
			ThemeID:          2,
			ColorSchemeID:    1,
			AvatarURL:        "http://www.gravatar.com/avatar/cfa35b8cd2ec278026357769582fa563?s=40\u0026d=identicon",
			CanCreateGroup:   true,
			CanCreateProject: true,
			ProjectsLimit:    10,
			TwoFactorEnabled: false,
			Identities:       []*UserIdentity{},
			External:         false,
			PublicEmail:      "john@example.com",
		},
	}

	if !reflect.DeepEqual(want, key) {
		t.Errorf("Keys.GetKeyWithUserByFingerprint returned %+v, want %+v", key, want)
	}
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/labels.go000066400000000000000000000231021475761473200226460ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"encoding/json"
	"fmt"
	"net/http"
)

// LabelsService handles communication with the label related methods of the
// GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/labels.html
type LabelsService struct {
	client *Client
}

// Label represents a GitLab label.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/labels.html
type Label struct {
	ID                     int    `json:"id"`
	Name                   string `json:"name"`
	Color                  string `json:"color"`
	TextColor              string `json:"text_color"`
	Description            string `json:"description"`
	OpenIssuesCount        int    `json:"open_issues_count"`
	ClosedIssuesCount      int    `json:"closed_issues_count"`
	OpenMergeRequestsCount int    `json:"open_merge_requests_count"`
	Subscribed             bool   `json:"subscribed"`
	Priority               int    `json:"priority"`
	IsProjectLabel         bool   `json:"is_project_label"`
}

// UnmarshalJSON implements the json.Unmarshaler interface.
func (l *Label) UnmarshalJSON(data []byte) error {
	type alias Label
	if err := json.Unmarshal(data, (*alias)(l)); err != nil {
		return err
	}

	if l.Name == "" {
		var raw map[string]interface{}
		if err := json.Unmarshal(data, &raw); err != nil {
			return err
		}
		if title, ok := raw["title"].(string); ok {
			l.Name = title
		}
	}

	return nil
}

func (l Label) String() string {
	return Stringify(l)
}

// ListLabelsOptions represents the available ListLabels() options.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/labels.html#list-labels
type ListLabelsOptions struct {
	ListOptions
	WithCounts            *bool   `url:"with_counts,omitempty" json:"with_counts,omitempty"`
	IncludeAncestorGroups *bool   `url:"include_ancestor_groups,omitempty" json:"include_ancestor_groups,omitempty"`
	Search                *string `url:"search,omitempty" json:"search,omitempty"`
}

// ListLabels gets all labels for given project.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/labels.html#list-labels
func (s *LabelsService) ListLabels(pid interface{}, opt *ListLabelsOptions, options ...RequestOptionFunc) ([]*Label, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/labels", PathEscape(project))

	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var l []*Label
	resp, err := s.client.Do(req, &l)
	if err != nil {
		return nil, resp, err
	}

	return l, resp, nil
}

// GetLabel get a single label for a given project.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/labels.html#get-a-single-project-label
func (s *LabelsService) GetLabel(pid interface{}, lid interface{}, options ...RequestOptionFunc) (*Label, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	label, err := parseID(lid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/labels/%s", PathEscape(project), PathEscape(label))

	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	var l *Label
	resp, err := s.client.Do(req, &l)
	if err != nil {
		return nil, resp, err
	}

	return l, resp, nil
}

// CreateLabelOptions represents the available CreateLabel() options.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/labels.html#create-a-new-label
type CreateLabelOptions struct {
	Name        *string `url:"name,omitempty" json:"name,omitempty"`
	Color       *string `url:"color,omitempty" json:"color,omitempty"`
	Description *string `url:"description,omitempty" json:"description,omitempty"`
	Priority    *int    `url:"priority,omitempty" json:"priority,omitempty"`
}

// CreateLabel creates a new label for given repository with given name and
// color.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/labels.html#create-a-new-label
func (s *LabelsService) CreateLabel(pid interface{}, opt *CreateLabelOptions, options ...RequestOptionFunc) (*Label, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/labels", PathEscape(project))

	req, err := s.client.NewRequest(http.MethodPost, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	l := new(Label)
	resp, err := s.client.Do(req, l)
	if err != nil {
		return nil, resp, err
	}

	return l, resp, nil
}

// DeleteLabelOptions represents the available DeleteLabel() options.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/labels.html#delete-a-label
type DeleteLabelOptions struct {
	Name *string `url:"name,omitempty" json:"name,omitempty"`
}

// DeleteLabel deletes a label given by its name or ID.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/labels.html#delete-a-label
func (s *LabelsService) DeleteLabel(pid interface{}, lid interface{}, opt *DeleteLabelOptions, options ...RequestOptionFunc) (*Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("projects/%s/labels", PathEscape(project))

	if lid != nil {
		label, err := parseID(lid)
		if err != nil {
			return nil, err
		}
		u = fmt.Sprintf("projects/%s/labels/%s", PathEscape(project), PathEscape(label))
	}

	req, err := s.client.NewRequest(http.MethodDelete, u, opt, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}

// UpdateLabelOptions represents the available UpdateLabel() options.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/labels.html#edit-an-existing-label
type UpdateLabelOptions struct {
	Name        *string `url:"name,omitempty" json:"name,omitempty"`
	NewName     *string `url:"new_name,omitempty" json:"new_name,omitempty"`
	Color       *string `url:"color,omitempty" json:"color,omitempty"`
	Description *string `url:"description,omitempty" json:"description,omitempty"`
	Priority    *int    `url:"priority,omitempty" json:"priority,omitempty"`
}

// UpdateLabel updates an existing label with new name or now color. At least
// one parameter is required, to update the label.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/labels.html#edit-an-existing-label
func (s *LabelsService) UpdateLabel(pid interface{}, lid interface{}, opt *UpdateLabelOptions, options ...RequestOptionFunc) (*Label, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/labels", PathEscape(project))

	if lid != nil {
		label, err := parseID(lid)
		if err != nil {
			return nil, nil, err
		}
		u = fmt.Sprintf("projects/%s/labels/%s", PathEscape(project), PathEscape(label))
	}

	req, err := s.client.NewRequest(http.MethodPut, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	l := new(Label)
	resp, err := s.client.Do(req, l)
	if err != nil {
		return nil, resp, err
	}

	return l, resp, nil
}

// SubscribeToLabel subscribes the authenticated user to a label to receive
// notifications. If the user is already subscribed to the label, the status
// code 304 is returned.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/labels.html#subscribe-to-a-label
func (s *LabelsService) SubscribeToLabel(pid interface{}, lid interface{}, options ...RequestOptionFunc) (*Label, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	label, err := parseID(lid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/labels/%s/subscribe", PathEscape(project), PathEscape(label))

	req, err := s.client.NewRequest(http.MethodPost, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	l := new(Label)
	resp, err := s.client.Do(req, l)
	if err != nil {
		return nil, resp, err
	}

	return l, resp, nil
}

// UnsubscribeFromLabel unsubscribes the authenticated user from a label to not
// receive notifications from it. If the user is not subscribed to the label, the
// status code 304 is returned.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/labels.html#unsubscribe-from-a-label
func (s *LabelsService) UnsubscribeFromLabel(pid interface{}, lid interface{}, options ...RequestOptionFunc) (*Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, err
	}
	label, err := parseID(lid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("projects/%s/labels/%s/unsubscribe", PathEscape(project), PathEscape(label))

	req, err := s.client.NewRequest(http.MethodPost, u, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}

// PromoteLabel Promotes a project label to a group label.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/labels.html#promote-a-project-label-to-a-group-label
func (s *LabelsService) PromoteLabel(pid interface{}, lid interface{}, options ...RequestOptionFunc) (*Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, err
	}
	label, err := parseID(lid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("projects/%s/labels/%s/promote", PathEscape(project), PathEscape(label))

	req, err := s.client.NewRequest(http.MethodPut, u, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/labels_test.go000066400000000000000000000133271475761473200237150ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"fmt"
	"net/http"
	"reflect"
	"testing"
)

func TestCreateLabel(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/labels", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		fmt.Fprint(w, `{"id":1, "name": "MyLabel", "color" : "#11FF22", "priority": 2}`)
	})

	// Create new label
	l := &CreateLabelOptions{
		Name:     Ptr("MyLabel"),
		Color:    Ptr("#11FF22"),
		Priority: Ptr(2),
	}
	label, _, err := client.Labels.CreateLabel("1", l)
	if err != nil {
		t.Fatal(err)
	}
	want := &Label{ID: 1, Name: "MyLabel", Color: "#11FF22", Priority: 2}
	if !reflect.DeepEqual(want, label) {
		t.Errorf("Labels.CreateLabel returned %+v, want %+v", label, want)
	}
}

func TestDeleteLabelbyID(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/labels/1", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodDelete)
	})

	// Delete label
	_, err := client.Labels.DeleteLabel("1", "1", nil)
	if err != nil {
		t.Fatal(err)
	}
}

func TestDeleteLabelbyName(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/labels/MyLabel", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodDelete)
	})

	// Delete label
	label := &DeleteLabelOptions{
		Name: Ptr("MyLabel"),
	}

	_, err := client.Labels.DeleteLabel("1", "MyLabel", label)
	if err != nil {
		t.Fatal(err)
	}
}

func TestUpdateLabel(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/labels/MyLabel", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPut)
		fmt.Fprint(w, `{"id":1, "name": "New Label", "color" : "#11FF23" , "description":"This is updated label", "priority": 42}`)
	})

	// Update label
	l := &UpdateLabelOptions{
		NewName:     Ptr("New Label"),
		Color:       Ptr("#11FF23"),
		Description: Ptr("This is updated label"),
		Priority:    Ptr(42),
	}

	label, resp, err := client.Labels.UpdateLabel("1", "MyLabel", l)

	if resp == nil {
		t.Fatal(err)
	}
	if err != nil {
		t.Fatal(err)
	}

	want := &Label{ID: 1, Name: "New Label", Color: "#11FF23", Description: "This is updated label", Priority: 42}

	if !reflect.DeepEqual(want, label) {
		t.Errorf("Labels.UpdateLabel returned %+v, want %+v", label, want)
	}
}

func TestSubscribeToLabel(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/labels/5/subscribe", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		fmt.Fprint(w, `{  "id" : 5, "name" : "kind/bug", "color" : "#d9534f", "description": "Bug reported by user", "open_issues_count": 1, "closed_issues_count": 0, "open_merge_requests_count": 1, "subscribed": true,"priority": null}`)
	})

	label, _, err := client.Labels.SubscribeToLabel("1", "5")
	if err != nil {
		t.Fatal(err)
	}
	want := &Label{ID: 5, Name: "kind/bug", Color: "#d9534f", Description: "Bug reported by user", OpenIssuesCount: 1, ClosedIssuesCount: 0, OpenMergeRequestsCount: 1, Subscribed: true}
	if !reflect.DeepEqual(want, label) {
		t.Errorf("Labels.SubscribeToLabel returned %+v, want %+v", label, want)
	}
}

func TestUnsubscribeFromLabel(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/labels/5/unsubscribe", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
	})

	_, err := client.Labels.UnsubscribeFromLabel("1", "5")
	if err != nil {
		t.Fatal(err)
	}
}

func TestListLabels(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/labels", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprint(w, `[{  "id" : 5, "name" : "kind/bug", "color" : "#d9534f", "description": "Bug reported by user", "open_issues_count": 1, "closed_issues_count": 0, "open_merge_requests_count": 1, "subscribed": true,"priority": null}]`)
	})

	o := &ListLabelsOptions{
		ListOptions: ListOptions{
			Page:    1,
			PerPage: 10,
		},
	}
	label, _, err := client.Labels.ListLabels("1", o)
	if err != nil {
		t.Log(err.Error() == "invalid ID type 1.1, the ID must be an int or a string")
	}
	want := []*Label{{ID: 5, Name: "kind/bug", Color: "#d9534f", Description: "Bug reported by user", OpenIssuesCount: 1, ClosedIssuesCount: 0, OpenMergeRequestsCount: 1, Subscribed: true}}
	if !reflect.DeepEqual(want, label) {
		t.Errorf("Labels.ListLabels returned %+v, want %+v", label, want)
	}
}

func TestGetLabel(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/projects/1/labels/5", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprint(w, `{  "id" : 5, "name" : "kind/bug", "color" : "#d9534f", "description": "Bug reported by user", "open_issues_count": 1, "closed_issues_count": 0, "open_merge_requests_count": 1, "subscribed": true,"priority": null}`)
	})

	label, _, err := client.Labels.GetLabel("1", 5)
	if err != nil {
		t.Log(err)
	}
	want := &Label{ID: 5, Name: "kind/bug", Color: "#d9534f", Description: "Bug reported by user", OpenIssuesCount: 1, ClosedIssuesCount: 0, OpenMergeRequestsCount: 1, Subscribed: true}
	if !reflect.DeepEqual(want, label) {
		t.Errorf("Labels.GetLabel returned %+v, want %+v", label, want)
	}
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/license.go000066400000000000000000000073641475761473200230420ustar00rootroot00000000000000//
// Copyright 2021, Patrick Webster
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"fmt"
	"net/http"
	"time"
)

// LicenseService handles communication with the license
// related methods of the GitLab API.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/license.html
type LicenseService struct {
	client *Client
}

// License represents a GitLab license.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/license.html
type License struct {
	ID               int        `json:"id"`
	Plan             string     `json:"plan"`
	CreatedAt        *time.Time `json:"created_at"`
	StartsAt         *ISOTime   `json:"starts_at"`
	ExpiresAt        *ISOTime   `json:"expires_at"`
	HistoricalMax    int        `json:"historical_max"`
	MaximumUserCount int        `json:"maximum_user_count"`
	Expired          bool       `json:"expired"`
	Overage          int        `json:"overage"`
	UserLimit        int        `json:"user_limit"`
	ActiveUsers      int        `json:"active_users"`
	Licensee         struct {
		Name    string `json:"Name"`
		Company string `json:"Company"`
		Email   string `json:"Email"`
	} `json:"licensee"`
	// Add on codes that may occur in legacy licenses that don't have a plan yet.
	// https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/app/models/license.rb
	AddOns struct {
		GitLabAuditorUser int `json:"GitLab_Auditor_User"`
		GitLabDeployBoard int `json:"GitLab_DeployBoard"`
		GitLabFileLocks   int `json:"GitLab_FileLocks"`
		GitLabGeo         int `json:"GitLab_Geo"`
		GitLabServiceDesk int `json:"GitLab_ServiceDesk"`
	} `json:"add_ons"`
}

func (l License) String() string {
	return Stringify(l)
}

// GetLicense retrieves information about the current license.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/license.html#retrieve-information-about-the-current-license
func (s *LicenseService) GetLicense(options ...RequestOptionFunc) (*License, *Response, error) {
	req, err := s.client.NewRequest(http.MethodGet, "license", nil, options)
	if err != nil {
		return nil, nil, err
	}

	l := new(License)
	resp, err := s.client.Do(req, l)
	if err != nil {
		return nil, resp, err
	}

	return l, resp, nil
}

// AddLicenseOptions represents the available AddLicense() options.
//
// https://docs.gitlab.com/ee/api/license.html#add-a-new-license
type AddLicenseOptions struct {
	License *string `url:"license" json:"license"`
}

// AddLicense adds a new license.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/license.html#add-a-new-license
func (s *LicenseService) AddLicense(opt *AddLicenseOptions, options ...RequestOptionFunc) (*License, *Response, error) {
	req, err := s.client.NewRequest(http.MethodPost, "license", opt, options)
	if err != nil {
		return nil, nil, err
	}

	l := new(License)
	resp, err := s.client.Do(req, l)
	if err != nil {
		return nil, resp, err
	}

	return l, resp, nil
}

// DeleteLicense deletes an existing license.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/license.html#delete-a-license
func (s *LicenseService) DeleteLicense(licenseID int, options ...RequestOptionFunc) (*Response, error) {
	u := fmt.Sprintf("license/%d", licenseID)

	req, err := s.client.NewRequest(http.MethodDelete, u, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/license_templates.go000066400000000000000000000066151475761473200251160ustar00rootroot00000000000000//
// Copyright 2021, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"fmt"
	"net/http"
)

// LicenseTemplate represents a license template.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/templates/licenses.html
type LicenseTemplate struct {
	Key         string   `json:"key"`
	Name        string   `json:"name"`
	Nickname    string   `json:"nickname"`
	Featured    bool     `json:"featured"`
	HTMLURL     string   `json:"html_url"`
	SourceURL   string   `json:"source_url"`
	Description string   `json:"description"`
	Conditions  []string `json:"conditions"`
	Permissions []string `json:"permissions"`
	Limitations []string `json:"limitations"`
	Content     string   `json:"content"`
}

// LicenseTemplatesService handles communication with the license templates
// related methods of the GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/templates/licenses.html
type LicenseTemplatesService struct {
	client *Client
}

// ListLicenseTemplatesOptions represents the available
// ListLicenseTemplates() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/templates/licenses.html#list-license-templates
type ListLicenseTemplatesOptions struct {
	ListOptions
	Popular *bool `url:"popular,omitempty" json:"popular,omitempty"`
}

// ListLicenseTemplates get all license templates.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/templates/licenses.html#list-license-templates
func (s *LicenseTemplatesService) ListLicenseTemplates(opt *ListLicenseTemplatesOptions, options ...RequestOptionFunc) ([]*LicenseTemplate, *Response, error) {
	req, err := s.client.NewRequest(http.MethodGet, "templates/licenses", opt, options)
	if err != nil {
		return nil, nil, err
	}

	var lts []*LicenseTemplate
	resp, err := s.client.Do(req, <s)
	if err != nil {
		return nil, resp, err
	}

	return lts, resp, nil
}

// GetLicenseTemplateOptions represents the available
// GetLicenseTemplate() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/templates/licenses.html#single-license-template
type GetLicenseTemplateOptions struct {
	Project  *string `url:"project,omitempty" json:"project,omitempty"`
	Fullname *string `url:"fullname,omitempty" json:"fullname,omitempty"`
}

// GetLicenseTemplate get a single license template. You can pass parameters
// to replace the license placeholder.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/templates/licenses.html#single-license-template
func (s *LicenseTemplatesService) GetLicenseTemplate(template string, opt *GetLicenseTemplateOptions, options ...RequestOptionFunc) (*LicenseTemplate, *Response, error) {
	u := fmt.Sprintf("templates/licenses/%s", template)

	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	lt := new(LicenseTemplate)
	resp, err := s.client.Do(req, lt)
	if err != nil {
		return nil, resp, err
	}

	return lt, resp, nil
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/license_templates_test.go000066400000000000000000000112661475761473200261530ustar00rootroot00000000000000package gitlab

import (
	"fmt"
	"net/http"
	"testing"

	"github.com/stretchr/testify/require"
)

func TestLicenseTemplatesService_ListLicenseTemplates(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/templates/licenses", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
			[
				{
					"key": "apache-2.0",
					"name": "Apache License 2.0",
					"nickname": null,
					"featured": true,
					"html_url": "http://choosealicense.com/licenses/apache-2.0/",
					"source_url": "http://www.apache.org/licenses/LICENSE-2.0.html",
					"description": "A permissive license that also provides an express grant of patent rights from contributors to users.",
					"conditions": [
						"include-copyright",
						"document-changes"
					],
					"permissions": [
						"commercial-use",
						"modifications",
						"distribution",
						"patent-use",
						"private-use"
					],
					"limitations": [
						"trademark-use",
						"no-liability"
					],
					"content": "Apache License\n Version 2.0, January 2004\n [...]"
				}
			]
		`)
	})

	want := []*LicenseTemplate{{
		Key:         "apache-2.0",
		Name:        "Apache License 2.0",
		Nickname:    "",
		Featured:    true,
		HTMLURL:     "http://choosealicense.com/licenses/apache-2.0/",
		SourceURL:   "http://www.apache.org/licenses/LICENSE-2.0.html",
		Description: "A permissive license that also provides an express grant of patent rights from contributors to users.",
		Conditions:  []string{"include-copyright", "document-changes"},
		Permissions: []string{"commercial-use", "modifications", "distribution", "patent-use", "private-use"},
		Limitations: []string{"trademark-use", "no-liability"},
		Content:     "Apache License\n Version 2.0, January 2004\n [...]",
	}}

	lts, resp, err := client.LicenseTemplates.ListLicenseTemplates(nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, lts)

	lts, resp, err = client.LicenseTemplates.ListLicenseTemplates(nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, lts)
}

func TestLicenseTemplatesService_ListLicenseTemplates_StatusNotFound(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/templates/licenses", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		w.WriteHeader(http.StatusNotFound)
	})

	lts, resp, err := client.LicenseTemplates.ListLicenseTemplates(nil)
	require.Error(t, err)
	require.Nil(t, lts)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestLicenseTemplatesService_GetLicenseTemplate(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/templates/licenses/apache-2.0", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
			{
				"key": "apache-2.0",
				"name": "Apache License 2.0",
				"nickname": null,
				"featured": true,
				"html_url": "http://choosealicense.com/licenses/apache-2.0/",
				"source_url": "http://www.apache.org/licenses/LICENSE-2.0.html",
				"description": "A permissive license that also provides an express grant of patent rights from contributors to users.",
				"conditions": [
					"include-copyright",
					"document-changes"
				],
				"permissions": [
					"commercial-use",
					"modifications",
					"distribution",
					"patent-use",
					"private-use"
				],
				"limitations": [
					"trademark-use",
					"no-liability"
				],
				"content": "Apache License\n Version 2.0, January 2004\n [...]"
			}
		`)
	})

	want := &LicenseTemplate{
		Key:         "apache-2.0",
		Name:        "Apache License 2.0",
		Nickname:    "",
		Featured:    true,
		HTMLURL:     "http://choosealicense.com/licenses/apache-2.0/",
		SourceURL:   "http://www.apache.org/licenses/LICENSE-2.0.html",
		Description: "A permissive license that also provides an express grant of patent rights from contributors to users.",
		Conditions:  []string{"include-copyright", "document-changes"},
		Permissions: []string{"commercial-use", "modifications", "distribution", "patent-use", "private-use"},
		Limitations: []string{"trademark-use", "no-liability"},
		Content:     "Apache License\n Version 2.0, January 2004\n [...]",
	}

	lt, resp, err := client.LicenseTemplates.GetLicenseTemplate("apache-2.0", nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, lt)

	lt, resp, err = client.LicenseTemplates.GetLicenseTemplate("apache-2.0", nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, lt)

	lt, resp, err = client.LicenseTemplates.GetLicenseTemplate("mit", nil)
	require.Error(t, err)
	require.Nil(t, lt)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/license_test.go000066400000000000000000000100151475761473200240640ustar00rootroot00000000000000package gitlab

import (
	"fmt"
	"net/http"
	"testing"

	"github.com/stretchr/testify/require"
)

func TestLicenseService_GetLicense(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/license", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
			{
			  "id": 2,
			  "plan": "gold",
			  "historical_max": 300,
			  "maximum_user_count": 300,
			  "expired": false,
			  "overage": 200,
			  "user_limit": 100,
			  "active_users": 300,
			  "licensee": {
				"Name": "Venkatesh Thalluri"
			  },
			  "add_ons": {
				"GitLab_FileLocks": 1,
				"GitLab_Auditor_User": 1
			  }
			}
		`)
	})

	want := &License{
		ID:               2,
		Plan:             "gold",
		HistoricalMax:    300,
		MaximumUserCount: 300,
		Expired:          false,
		Overage:          200,
		UserLimit:        100,
		ActiveUsers:      300,
		Licensee: struct {
			Name    string `json:"Name"`
			Company string `json:"Company"`
			Email   string `json:"Email"`
		}{
			Name:    "Venkatesh Thalluri",
			Company: "",
			Email:   "",
		},
		AddOns: struct {
			GitLabAuditorUser int `json:"GitLab_Auditor_User"`
			GitLabDeployBoard int `json:"GitLab_DeployBoard"`
			GitLabFileLocks   int `json:"GitLab_FileLocks"`
			GitLabGeo         int `json:"GitLab_Geo"`
			GitLabServiceDesk int `json:"GitLab_ServiceDesk"`
		}{
			GitLabAuditorUser: 1,
			GitLabDeployBoard: 0,
			GitLabFileLocks:   1,
			GitLabGeo:         0,
			GitLabServiceDesk: 0,
		},
	}

	l, resp, err := client.License.GetLicense()
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, l)
}

func TestLicenseService_GetLicense_StatusNotFound(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/license", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		w.WriteHeader(http.StatusNotFound)
	})

	l, resp, err := client.License.GetLicense()
	require.Error(t, err)
	require.Nil(t, l)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestLicenseService_AddLicense(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/license", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		fmt.Fprintf(w, `
			{
			  "id": 2,
			  "plan": "gold",
			  "historical_max": 300,
			  "maximum_user_count": 300,
			  "expired": false,
			  "overage": 200,
			  "user_limit": 100,
			  "active_users": 300,
			  "licensee": {
				"Name": "Venkatesh Thalluri"
			  },
			  "add_ons": {
				"GitLab_FileLocks": 1,
				"GitLab_Auditor_User": 1
			  }
			}
		`)
	})

	want := &License{
		ID:               2,
		Plan:             "gold",
		HistoricalMax:    300,
		MaximumUserCount: 300,
		Expired:          false,
		Overage:          200,
		UserLimit:        100,
		ActiveUsers:      300,
		Licensee: struct {
			Name    string `json:"Name"`
			Company string `json:"Company"`
			Email   string `json:"Email"`
		}{
			Name:    "Venkatesh Thalluri",
			Company: "",
			Email:   "",
		},
		AddOns: struct {
			GitLabAuditorUser int `json:"GitLab_Auditor_User"`
			GitLabDeployBoard int `json:"GitLab_DeployBoard"`
			GitLabFileLocks   int `json:"GitLab_FileLocks"`
			GitLabGeo         int `json:"GitLab_Geo"`
			GitLabServiceDesk int `json:"GitLab_ServiceDesk"`
		}{
			GitLabAuditorUser: 1,
			GitLabDeployBoard: 0,
			GitLabFileLocks:   1,
			GitLabGeo:         0,
			GitLabServiceDesk: 0,
		},
	}

	l, resp, err := client.License.AddLicense(nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, l)

	l, resp, err = client.License.AddLicense(nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, l)
}

func TestLicenseService_AddLicense_StatusNotFound(t *testing.T) {
	mux, client := setup(t)

	mux.HandleFunc("/api/v4/license", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		w.WriteHeader(http.StatusNotFound)
	})

	l, resp, err := client.License.AddLicense(nil)
	require.Error(t, err)
	require.Nil(t, l)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/markdown.go000066400000000000000000000025371475761473200232370ustar00rootroot00000000000000package gitlab

import "net/http"

// MarkdownService handles communication with the markdown related methods of
// the GitLab API.
//
// Gitlab API docs: https://docs.gitlab.com/ee/api/markdown.html
type MarkdownService struct {
	client *Client
}

// Markdown represents a markdown document.
//
// Gitlab API docs: https://docs.gitlab.com/ee/api/markdown.html
type Markdown struct {
	HTML string `json:"html"`
}

// RenderOptions represents the available Render() options.
//
// Gitlab API docs:
// https://docs.gitlab.com/ee/api/markdown.html#render-an-arbitrary-markdown-document
type RenderOptions struct {
	Text                    *string `url:"text,omitempty" json:"text,omitempty"`
	GitlabFlavouredMarkdown *bool   `url:"gfm,omitempty" json:"gfm,omitempty"`
	Project                 *string `url:"project,omitempty" json:"project,omitempty"`
}

// Render an arbitrary markdown document.
//
// Gitlab API docs:
// https://docs.gitlab.com/ee/api/markdown.html#render-an-arbitrary-markdown-document
func (s *MarkdownService) Render(opt *RenderOptions, options ...RequestOptionFunc) (*Markdown, *Response, error) {
	req, err := s.client.NewRequest(http.MethodPost, "markdown", opt, options)
	if err != nil {
		return nil, nil, err
	}

	md := new(Markdown)
	response, err := s.client.Do(req, md)
	if err != nil {
		return nil, response, err
	}

	return md, response, nil
}
golang-gitlab-gitlab-org-api-client-go-0.123.0/markdown_test.go000066400000000000000000000020231475761473200242640ustar00rootroot00000000000000package gitlab

import (
	"encoding/json"
	"net/http"
	"testing"
)

const markdownHTMLResponse = "

Testing

" func TestRender(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/markdown", func(writer http.ResponseWriter, request *http.Request) { testMethod(t, request, http.MethodPost) writer.WriteHeader(http.StatusOK) markdown := Markdown{HTML: markdownHTMLResponse} resp, _ := json.Marshal(markdown) _, _ = writer.Write(resp) }) opt := &RenderOptions{ Text: Ptr("# Testing"), GitlabFlavouredMarkdown: Ptr(true), Project: Ptr("some/sub/group/project"), } markdown, resp, err := client.Markdown.Render(opt) if err != nil { t.Fatalf("Render returned error: %v", err) } if resp.StatusCode != http.StatusOK { t.Fatalf("Render returned status, expected %q but got %q", http.StatusOK, resp.Status) } if markdown.HTML != markdownHTMLResponse { t.Fatalf("Render returned wrong response, expected %q but got %q", markdownHTMLResponse, markdown.HTML) } } golang-gitlab-gitlab-org-api-client-go-0.123.0/member_roles.go000066400000000000000000000160431475761473200240650ustar00rootroot00000000000000package gitlab import ( "fmt" "net/http" ) // MemberRolesService handles communication with the member roles related // methods of the GitLab API. // // GitLab API docs: https://docs.gitlab.com/ee/api/member_roles.html type MemberRolesService struct { client *Client } // MemberRole represents a GitLab member role. // // GitLab API docs: https://docs.gitlab.com/ee/api/member_roles.html type MemberRole struct { ID int `json:"id"` Name string `json:"name"` Description string `json:"description,omitempty"` GroupID int `json:"group_id"` BaseAccessLevel AccessLevelValue `json:"base_access_level"` AdminCICDVariables bool `json:"admin_cicd_variables,omitempty"` AdminComplianceFramework bool `json:"admin_compliance_framework,omitempty"` AdminGroupMembers bool `json:"admin_group_member,omitempty"` AdminMergeRequests bool `json:"admin_merge_request,omitempty"` AdminPushRules bool `json:"admin_push_rules,omitempty"` AdminTerraformState bool `json:"admin_terraform_state,omitempty"` AdminVulnerability bool `json:"admin_vulnerability,omitempty"` AdminWebHook bool `json:"admin_web_hook,omitempty"` ArchiveProject bool `json:"archive_project,omitempty"` ManageDeployTokens bool `json:"manage_deploy_tokens,omitempty"` ManageGroupAccesToken bool `json:"manage_group_access_tokens,omitempty"` ManageMergeRequestSettings bool `json:"manage_merge_request_settings,omitempty"` ManageProjectAccessToken bool `json:"manage_project_access_tokens,omitempty"` ManageSecurityPolicyLink bool `json:"manage_security_policy_link,omitempty"` ReadCode bool `json:"read_code,omitempty"` ReadRunners bool `json:"read_runners,omitempty"` ReadDependency bool `json:"read_dependency,omitempty"` ReadVulnerability bool `json:"read_vulnerability,omitempty"` RemoveGroup bool `json:"remove_group,omitempty"` RemoveProject bool `json:"remove_project,omitempty"` } // ListMemberRoles gets a list of member roles for a specified group. // // Gitlab API docs: // https://docs.gitlab.com/ee/api/member_roles.html#list-all-member-roles-of-a-group func (s *MemberRolesService) ListMemberRoles(gid interface{}, options ...RequestOptionFunc) ([]*MemberRole, *Response, error) { group, err := parseID(gid) if err != nil { return nil, nil, err } u := fmt.Sprintf("groups/%s/member_roles", PathEscape(group)) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } var mrs []*MemberRole resp, err := s.client.Do(req, &mrs) if err != nil { return nil, resp, err } return mrs, resp, nil } // CreateMemberRoleOptions represents the available CreateMemberRole() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/member_roles.html#add-a-member-role-to-a-group type CreateMemberRoleOptions struct { Name *string `url:"name,omitempty" json:"name,omitempty"` BaseAccessLevel *AccessLevelValue `url:"base_access_level,omitempty" json:"base_access_level,omitempty"` Description *string `url:"description,omitempty" json:"description,omitempty"` AdminCICDVariables *bool `url:"admin_cicd_variables" json:"admin_cicd_variables,omitempty"` AdminComplianceFramework *bool `url:"admin_compliance_framework" json:"admin_compliance_framework,omitempty"` AdminGroupMembers *bool `url:"admin_group_member" json:"admin_group_member,omitempty"` AdminMergeRequest *bool `url:"admin_merge_request,omitempty" json:"admin_merge_request,omitempty"` AdminPushRules *bool `url:"admin_push_rules" json:"admin_push_rules,omitempty"` AdminTerraformState *bool `url:"admin_terraform_state" json:"admin_terraform_state,omitempty"` AdminVulnerability *bool `url:"admin_vulnerability,omitempty" json:"admin_vulnerability,omitempty"` AdminWebHook *bool `url:"admin_web_hook" json:"admin_web_hook,omitempty"` ArchiveProject *bool `url:"archive_project" json:"archive_project,omitempty"` ManageDeployTokens *bool `url:"manage_deploy_tokens" json:"manage_deploy_tokens,omitempty"` ManageGroupAccesToken *bool `url:"manage_group_access_tokens" json:"manage_group_access_tokens,omitempty"` ManageMergeRequestSettings *bool `url:"manage_merge_request_settings" json:"manage_merge_request_settings,omitempty"` ManageProjectAccessToken *bool `url:"manage_project_access_tokens" json:"manage_project_access_tokens,omitempty"` ManageSecurityPolicyLink *bool `url:"manage_security_policy_link" json:"manage_security_policy_link,omitempty"` ReadCode *bool `url:"read_code,omitempty" json:"read_code,omitempty"` ReadRunners *bool `url:"read_runners" json:"read_runners,omitempty"` ReadDependency *bool `url:"read_dependency,omitempty" json:"read_dependency,omitempty"` ReadVulnerability *bool `url:"read_vulnerability,omitempty" json:"read_vulnerability,omitempty"` RemoveGroup *bool `url:"remove_group" json:"remove_group,omitempty"` RemoveProject *bool `url:"remove_project" json:"remove_project,omitempty"` } // CreateMemberRole creates a new member role for a specified group. // // Gitlab API docs: // https://docs.gitlab.com/ee/api/member_roles.html#add-a-member-role-to-a-group func (s *MemberRolesService) CreateMemberRole(gid interface{}, opt *CreateMemberRoleOptions, options ...RequestOptionFunc) (*MemberRole, *Response, error) { group, err := parseID(gid) if err != nil { return nil, nil, err } u := fmt.Sprintf("groups/%s/member_roles", PathEscape(group)) req, err := s.client.NewRequest(http.MethodPost, u, opt, options) if err != nil { return nil, nil, err } mr := new(MemberRole) resp, err := s.client.Do(req, mr) if err != nil { return nil, resp, err } return mr, resp, nil } // DeleteMemberRole deletes a member role from a specified group. // // Gitlab API docs: // https://docs.gitlab.com/ee/api/member_roles.html#remove-member-role-of-a-group func (s *MemberRolesService) DeleteMemberRole(gid interface{}, memberRole int, options ...RequestOptionFunc) (*Response, error) { group, err := parseID(gid) if err != nil { return nil, err } u := fmt.Sprintf("groups/%s/member_roles/%d", PathEscape(group), memberRole) req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } golang-gitlab-gitlab-org-api-client-go-0.123.0/member_roles_test.go000066400000000000000000000072501475761473200251240ustar00rootroot00000000000000package gitlab import ( "net/http" "testing" "github.com/stretchr/testify/require" ) func TestListMemberRoles(t *testing.T) { mux, client := setup(t) path := "/api/v4/groups/1/member_roles" mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) mustWriteHTTPResponse(t, w, "testdata/list_member_roles.json") }) memberRoles, _, err := client.MemberRolesService.ListMemberRoles(1) require.NoError(t, err) want := []*MemberRole{ { ID: 1, Name: "GuestCodeReader", Description: "A Guest user that can read code", GroupID: 1, BaseAccessLevel: 10, // Guest Base Level ReadCode: true, }, { ID: 2, Name: "GuestVulnerabilityReader", Description: "A Guest user that can read vulnerabilities", GroupID: 1, BaseAccessLevel: 10, // Guest Base Level ReadVulnerability: true, }, } require.Equal(t, want, memberRoles) } func TestCreateMemberRole(t *testing.T) { mux, client := setup(t) path := "/api/v4/groups/84/member_roles" mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) mustWriteHTTPResponse(t, w, "testdata/create_member_role.json") }) memberRole, _, err := client.MemberRolesService.CreateMemberRole(84, &CreateMemberRoleOptions{ Name: Ptr("Custom guest"), BaseAccessLevel: Ptr(GuestPermissions), Description: Ptr("a sample custom role"), AdminCICDVariables: Ptr(false), AdminComplianceFramework: Ptr(false), AdminGroupMembers: Ptr(false), AdminMergeRequest: Ptr(false), AdminPushRules: Ptr(false), AdminTerraformState: Ptr(false), AdminVulnerability: Ptr(false), AdminWebHook: Ptr(false), ArchiveProject: Ptr(false), ManageDeployTokens: Ptr(false), ManageGroupAccesToken: Ptr(false), ManageMergeRequestSettings: Ptr(false), ManageProjectAccessToken: Ptr(false), ManageSecurityPolicyLink: Ptr(false), ReadCode: Ptr(true), ReadRunners: Ptr(false), ReadDependency: Ptr(false), ReadVulnerability: Ptr(false), RemoveGroup: Ptr(false), RemoveProject: Ptr(false), }) require.NoError(t, err) want := &MemberRole{ ID: 3, Name: "Custom guest", Description: "a sample custom role", BaseAccessLevel: GuestPermissions, GroupID: 84, AdminCICDVariables: false, AdminComplianceFramework: false, AdminGroupMembers: false, AdminMergeRequests: false, AdminPushRules: false, AdminTerraformState: false, AdminVulnerability: false, AdminWebHook: false, ArchiveProject: false, ManageDeployTokens: false, ManageGroupAccesToken: false, ManageMergeRequestSettings: false, ManageProjectAccessToken: false, ManageSecurityPolicyLink: false, ReadCode: true, ReadRunners: false, ReadDependency: false, ReadVulnerability: false, RemoveGroup: false, RemoveProject: false, } require.Equal(t, want, memberRole) } func TestDeleteMemberRole(t *testing.T) { mux, client := setup(t) path := "/api/v4/groups/1/member_roles/2" mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodDelete) }) _, err := client.MemberRolesService.DeleteMemberRole(1, 2) require.NoError(t, err) } golang-gitlab-gitlab-org-api-client-go-0.123.0/merge_request_approvals.go000066400000000000000000000412771475761473200263570ustar00rootroot00000000000000// // Copyright 2021, Sander van Harmelen // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "fmt" "net/http" "time" ) // MergeRequestApprovalsService handles communication with the merge request // approvals related methods of the GitLab API. This includes reading/updating // approval settings and approve/unapproving merge requests // // GitLab API docs: https://docs.gitlab.com/ee/api/merge_request_approvals.html type MergeRequestApprovalsService struct { client *Client } // MergeRequestApprovals represents GitLab merge request approvals. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_request_approvals.html#merge-request-level-mr-approvals type MergeRequestApprovals struct { ID int `json:"id"` IID int `json:"iid"` ProjectID int `json:"project_id"` Title string `json:"title"` Description string `json:"description"` State string `json:"state"` CreatedAt *time.Time `json:"created_at"` UpdatedAt *time.Time `json:"updated_at"` MergeStatus string `json:"merge_status"` Approved bool `json:"approved"` ApprovalsBeforeMerge int `json:"approvals_before_merge"` ApprovalsRequired int `json:"approvals_required"` ApprovalsLeft int `json:"approvals_left"` RequirePasswordToApprove bool `json:"require_password_to_approve"` ApprovedBy []*MergeRequestApproverUser `json:"approved_by"` SuggestedApprovers []*BasicUser `json:"suggested_approvers"` Approvers []*MergeRequestApproverUser `json:"approvers"` ApproverGroups []*MergeRequestApproverGroup `json:"approver_groups"` UserHasApproved bool `json:"user_has_approved"` UserCanApprove bool `json:"user_can_approve"` ApprovalRulesLeft []*MergeRequestApprovalRule `json:"approval_rules_left"` HasApprovalRules bool `json:"has_approval_rules"` MergeRequestApproversAvailable bool `json:"merge_request_approvers_available"` MultipleApprovalRulesAvailable bool `json:"multiple_approval_rules_available"` } func (m MergeRequestApprovals) String() string { return Stringify(m) } // MergeRequestApproverGroup represents GitLab project level merge request approver group. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_request_approvals.html#project-level-mr-approvals type MergeRequestApproverGroup struct { Group struct { ID int `json:"id"` Name string `json:"name"` Path string `json:"path"` Description string `json:"description"` Visibility string `json:"visibility"` AvatarURL string `json:"avatar_url"` WebURL string `json:"web_url"` FullName string `json:"full_name"` FullPath string `json:"full_path"` LFSEnabled bool `json:"lfs_enabled"` RequestAccessEnabled bool `json:"request_access_enabled"` } } // MergeRequestApprovalRule represents a GitLab merge request approval rule. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_request_approvals.html#get-merge-request-level-rules type MergeRequestApprovalRule struct { ID int `json:"id"` Name string `json:"name"` RuleType string `json:"rule_type"` ReportType string `json:"report_type"` EligibleApprovers []*BasicUser `json:"eligible_approvers"` ApprovalsRequired int `json:"approvals_required"` SourceRule *ProjectApprovalRule `json:"source_rule"` Users []*BasicUser `json:"users"` Groups []*Group `json:"groups"` ContainsHiddenGroups bool `json:"contains_hidden_groups"` Section string `json:"section"` ApprovedBy []*BasicUser `json:"approved_by"` Approved bool `json:"approved"` } // MergeRequestApprovalState represents a GitLab merge request approval state. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_request_approvals.html#get-the-approval-state-of-merge-requests type MergeRequestApprovalState struct { ApprovalRulesOverwritten bool `json:"approval_rules_overwritten"` Rules []*MergeRequestApprovalRule `json:"rules"` } // String is a stringify for MergeRequestApprovalRule func (s MergeRequestApprovalRule) String() string { return Stringify(s) } // MergeRequestApproverUser represents GitLab project level merge request approver user. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_request_approvals.html#project-level-mr-approvals type MergeRequestApproverUser struct { User *BasicUser } // ApproveMergeRequestOptions represents the available ApproveMergeRequest() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_request_approvals.html#approve-merge-request type ApproveMergeRequestOptions struct { SHA *string `url:"sha,omitempty" json:"sha,omitempty"` } // ApproveMergeRequest approves a merge request on GitLab. If a non-empty sha // is provided then it must match the sha at the HEAD of the MR. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_request_approvals.html#approve-merge-request func (s *MergeRequestApprovalsService) ApproveMergeRequest(pid interface{}, mr int, opt *ApproveMergeRequestOptions, options ...RequestOptionFunc) (*MergeRequestApprovals, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/merge_requests/%d/approve", PathEscape(project), mr) req, err := s.client.NewRequest(http.MethodPost, u, opt, options) if err != nil { return nil, nil, err } m := new(MergeRequestApprovals) resp, err := s.client.Do(req, m) if err != nil { return nil, resp, err } return m, resp, nil } // UnapproveMergeRequest unapproves a previously approved merge request on GitLab. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_request_approvals.html#unapprove-merge-request func (s *MergeRequestApprovalsService) UnapproveMergeRequest(pid interface{}, mr int, options ...RequestOptionFunc) (*Response, error) { project, err := parseID(pid) if err != nil { return nil, err } u := fmt.Sprintf("projects/%s/merge_requests/%d/unapprove", PathEscape(project), mr) req, err := s.client.NewRequest(http.MethodPost, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } // ResetApprovalsOfMergeRequest clear all approvals of merge request on GitLab. // Available only for bot users based on project or group tokens. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_request_approvals.html#reset-approvals-of-a-merge-request func (s *MergeRequestApprovalsService) ResetApprovalsOfMergeRequest(pid interface{}, mr int, options ...RequestOptionFunc) (*Response, error) { project, err := parseID(pid) if err != nil { return nil, err } u := fmt.Sprintf("projects/%s/merge_requests/%d/reset_approvals", PathEscape(project), mr) req, err := s.client.NewRequest(http.MethodPut, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } // ChangeMergeRequestApprovalConfigurationOptions represents the available // ChangeMergeRequestApprovalConfiguration() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_request_approvals.html#change-approval-configuration-deprecated type ChangeMergeRequestApprovalConfigurationOptions struct { ApprovalsRequired *int `url:"approvals_required,omitempty" json:"approvals_required,omitempty"` } // GetConfiguration shows information about single merge request approvals // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_request_approvals.html#get-configuration-1 func (s *MergeRequestApprovalsService) GetConfiguration(pid interface{}, mr int, options ...RequestOptionFunc) (*MergeRequestApprovals, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/merge_requests/%d/approvals", PathEscape(project), mr) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } m := new(MergeRequestApprovals) resp, err := s.client.Do(req, m) if err != nil { return nil, resp, err } return m, resp, nil } // ChangeApprovalConfiguration updates the approval configuration of a merge request. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_request_approvals.html#change-approval-configuration-deprecated func (s *MergeRequestApprovalsService) ChangeApprovalConfiguration(pid interface{}, mergeRequest int, opt *ChangeMergeRequestApprovalConfigurationOptions, options ...RequestOptionFunc) (*MergeRequest, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/merge_requests/%d/approvals", PathEscape(project), mergeRequest) req, err := s.client.NewRequest(http.MethodPost, u, opt, options) if err != nil { return nil, nil, err } m := new(MergeRequest) resp, err := s.client.Do(req, m) if err != nil { return nil, resp, err } return m, resp, nil } // ChangeMergeRequestAllowedApproversOptions represents the available // ChangeMergeRequestAllowedApprovers() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_request_approvals.html#change-allowed-approvers-for-merge-request type ChangeMergeRequestAllowedApproversOptions struct { ApproverIDs []int `url:"approver_ids" json:"approver_ids"` ApproverGroupIDs []int `url:"approver_group_ids" json:"approver_group_ids"` } // ChangeAllowedApprovers updates the approvers for a merge request. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_request_approvals.html#change-allowed-approvers-for-merge-request func (s *MergeRequestApprovalsService) ChangeAllowedApprovers(pid interface{}, mergeRequest int, opt *ChangeMergeRequestAllowedApproversOptions, options ...RequestOptionFunc) (*MergeRequest, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/merge_requests/%d/approvers", PathEscape(project), mergeRequest) req, err := s.client.NewRequest(http.MethodPut, u, opt, options) if err != nil { return nil, nil, err } m := new(MergeRequest) resp, err := s.client.Do(req, m) if err != nil { return nil, resp, err } return m, resp, nil } // GetApprovalRules requests information about a merge request’s approval rules // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_request_approvals.html#get-merge-request-level-rules func (s *MergeRequestApprovalsService) GetApprovalRules(pid interface{}, mergeRequest int, options ...RequestOptionFunc) ([]*MergeRequestApprovalRule, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/merge_requests/%d/approval_rules", PathEscape(project), mergeRequest) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } var par []*MergeRequestApprovalRule resp, err := s.client.Do(req, &par) if err != nil { return nil, resp, err } return par, resp, nil } // GetApprovalState requests information about a merge request’s approval state // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_request_approvals.html#get-the-approval-state-of-merge-requests func (s *MergeRequestApprovalsService) GetApprovalState(pid interface{}, mergeRequest int, options ...RequestOptionFunc) (*MergeRequestApprovalState, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/merge_requests/%d/approval_state", PathEscape(project), mergeRequest) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } var pas *MergeRequestApprovalState resp, err := s.client.Do(req, &pas) if err != nil { return nil, resp, err } return pas, resp, nil } // CreateMergeRequestApprovalRuleOptions represents the available CreateApprovalRule() // options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_request_approvals.html#create-merge-request-level-rule type CreateMergeRequestApprovalRuleOptions struct { Name *string `url:"name,omitempty" json:"name,omitempty"` ApprovalsRequired *int `url:"approvals_required,omitempty" json:"approvals_required,omitempty"` ApprovalProjectRuleID *int `url:"approval_project_rule_id,omitempty" json:"approval_project_rule_id,omitempty"` UserIDs *[]int `url:"user_ids,omitempty" json:"user_ids,omitempty"` GroupIDs *[]int `url:"group_ids,omitempty" json:"group_ids,omitempty"` } // CreateApprovalRule creates a new MR level approval rule. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_request_approvals.html#create-merge-request-level-rule func (s *MergeRequestApprovalsService) CreateApprovalRule(pid interface{}, mergeRequest int, opt *CreateMergeRequestApprovalRuleOptions, options ...RequestOptionFunc) (*MergeRequestApprovalRule, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/merge_requests/%d/approval_rules", PathEscape(project), mergeRequest) req, err := s.client.NewRequest(http.MethodPost, u, opt, options) if err != nil { return nil, nil, err } par := new(MergeRequestApprovalRule) resp, err := s.client.Do(req, &par) if err != nil { return nil, resp, err } return par, resp, nil } // UpdateMergeRequestApprovalRuleOptions represents the available UpdateApprovalRule() // options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_request_approvals.html#update-merge-request-level-rule type UpdateMergeRequestApprovalRuleOptions struct { Name *string `url:"name,omitempty" json:"name,omitempty"` ApprovalsRequired *int `url:"approvals_required,omitempty" json:"approvals_required,omitempty"` UserIDs *[]int `url:"user_ids,omitempty" json:"user_ids,omitempty"` GroupIDs *[]int `url:"group_ids,omitempty" json:"group_ids,omitempty"` } // UpdateApprovalRule updates an existing approval rule with new options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_request_approvals.html#update-merge-request-level-rule func (s *MergeRequestApprovalsService) UpdateApprovalRule(pid interface{}, mergeRequest int, approvalRule int, opt *UpdateMergeRequestApprovalRuleOptions, options ...RequestOptionFunc) (*MergeRequestApprovalRule, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/merge_requests/%d/approval_rules/%d", PathEscape(project), mergeRequest, approvalRule) req, err := s.client.NewRequest(http.MethodPut, u, opt, options) if err != nil { return nil, nil, err } par := new(MergeRequestApprovalRule) resp, err := s.client.Do(req, &par) if err != nil { return nil, resp, err } return par, resp, nil } // DeleteApprovalRule deletes a mr level approval rule. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_request_approvals.html#delete-merge-request-level-rule func (s *MergeRequestApprovalsService) DeleteApprovalRule(pid interface{}, mergeRequest int, approvalRule int, options ...RequestOptionFunc) (*Response, error) { project, err := parseID(pid) if err != nil { return nil, err } u := fmt.Sprintf("projects/%s/merge_requests/%d/approval_rules/%d", PathEscape(project), mergeRequest, approvalRule) req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } golang-gitlab-gitlab-org-api-client-go-0.123.0/merge_request_approvals_test.go000066400000000000000000000261371475761473200274140ustar00rootroot00000000000000// // Copyright 2021, Sander van Harmelen // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "fmt" "net/http" "reflect" "testing" ) func TestGetApprovalState(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/merge_requests/1/approval_state", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, `{ "approval_rules_overwritten": true, "rules": [ { "id": 1, "name": "security", "rule_type": "regular", "eligible_approvers": [ { "id": 5, "name": "John Doe", "username": "jdoe", "state": "active", "avatar_url": "https://www.gravatar.com/avatar/0?s=80&d=identicon", "web_url": "http://localhost/jdoe" }, { "id": 50, "name": "Group Member 1", "username": "group_member_1", "state": "active", "avatar_url": "https://www.gravatar.com/avatar/0?s=80&d=identicon", "web_url": "http://localhost/group_member_1" } ], "approvals_required": 3, "source_rule": null, "users": [ { "id": 5, "name": "John Doe", "username": "jdoe", "state": "active", "avatar_url": "https://www.gravatar.com/avatar/0?s=80&d=identicon", "web_url": "http://localhost/jdoe" } ], "groups": [ { "id": 5, "name": "group1", "path": "group1", "description": "", "visibility": "public", "lfs_enabled": false, "avatar_url": null, "web_url": "http://localhost/groups/group1", "request_access_enabled": false, "full_name": "group1", "full_path": "group1", "parent_id": null, "ldap_cn": null, "ldap_access": null } ], "contains_hidden_groups": false, "section": "test", "approved_by": [ { "id": 5, "name": "John Doe", "username": "jdoe", "state": "active", "avatar_url": "https://www.gravatar.com/avatar/0?s=80&d=identicon", "web_url": "http://localhost/jdoe" } ], "approved": false } ] }`) }) approvals, _, err := client.MergeRequestApprovals.GetApprovalState(1, 1) if err != nil { t.Errorf("MergeRequestApprovals.GetApprovalState returned error: %v", err) } want := &MergeRequestApprovalState{ ApprovalRulesOverwritten: true, Rules: []*MergeRequestApprovalRule{ { ID: 1, Name: "security", RuleType: "regular", EligibleApprovers: []*BasicUser{ { ID: 5, Name: "John Doe", Username: "jdoe", State: "active", AvatarURL: "https://www.gravatar.com/avatar/0?s=80&d=identicon", WebURL: "http://localhost/jdoe", }, { ID: 50, Name: "Group Member 1", Username: "group_member_1", State: "active", AvatarURL: "https://www.gravatar.com/avatar/0?s=80&d=identicon", WebURL: "http://localhost/group_member_1", }, }, ApprovalsRequired: 3, Users: []*BasicUser{ { ID: 5, Name: "John Doe", Username: "jdoe", State: "active", AvatarURL: "https://www.gravatar.com/avatar/0?s=80&d=identicon", WebURL: "http://localhost/jdoe", }, }, Groups: []*Group{ { ID: 5, Name: "group1", Path: "group1", Description: "", Visibility: PublicVisibility, LFSEnabled: false, AvatarURL: "", WebURL: "http://localhost/groups/group1", RequestAccessEnabled: false, FullName: "group1", FullPath: "group1", }, }, Section: "test", ApprovedBy: []*BasicUser{ { ID: 5, Name: "John Doe", Username: "jdoe", State: "active", AvatarURL: "https://www.gravatar.com/avatar/0?s=80&d=identicon", WebURL: "http://localhost/jdoe", }, }, Approved: false, }, }, } if !reflect.DeepEqual(want, approvals) { t.Errorf("MergeRequestApprovals.GetApprovalState returned %+v, want %+v", approvals, want) } } func TestGetApprovalRules(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/merge_requests/1/approval_rules", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, `[ { "id": 1, "name": "security", "rule_type": "regular", "eligible_approvers": [ { "id": 5, "name": "John Doe", "username": "jdoe", "state": "active", "avatar_url": "https://www.gravatar.com/avatar/0?s=80&d=identicon", "web_url": "http://localhost/jdoe" }, { "id": 50, "name": "Group Member 1", "username": "group_member_1", "state": "active", "avatar_url": "https://www.gravatar.com/avatar/0?s=80&d=identicon", "web_url": "http://localhost/group_member_1" } ], "approvals_required": 3, "source_rule": null, "users": [ { "id": 5, "name": "John Doe", "username": "jdoe", "state": "active", "avatar_url": "https://www.gravatar.com/avatar/0?s=80&d=identicon", "web_url": "http://localhost/jdoe" } ], "groups": [ { "id": 5, "name": "group1", "path": "group1", "description": "", "visibility": "public", "lfs_enabled": false, "avatar_url": null, "web_url": "http://localhost/groups/group1", "request_access_enabled": false, "full_name": "group1", "full_path": "group1", "parent_id": null, "ldap_cn": null, "ldap_access": null } ], "contains_hidden_groups": false, "section": "test" } ]`) }) approvals, _, err := client.MergeRequestApprovals.GetApprovalRules(1, 1) if err != nil { t.Errorf("MergeRequestApprovals.GetApprovalRules returned error: %v", err) } want := []*MergeRequestApprovalRule{ { ID: 1, Name: "security", RuleType: "regular", EligibleApprovers: []*BasicUser{ { ID: 5, Name: "John Doe", Username: "jdoe", State: "active", AvatarURL: "https://www.gravatar.com/avatar/0?s=80&d=identicon", WebURL: "http://localhost/jdoe", }, { ID: 50, Name: "Group Member 1", Username: "group_member_1", State: "active", AvatarURL: "https://www.gravatar.com/avatar/0?s=80&d=identicon", WebURL: "http://localhost/group_member_1", }, }, ApprovalsRequired: 3, Users: []*BasicUser{ { ID: 5, Name: "John Doe", Username: "jdoe", State: "active", AvatarURL: "https://www.gravatar.com/avatar/0?s=80&d=identicon", WebURL: "http://localhost/jdoe", }, }, Groups: []*Group{ { ID: 5, Name: "group1", Path: "group1", Description: "", Visibility: PublicVisibility, LFSEnabled: false, AvatarURL: "", WebURL: "http://localhost/groups/group1", RequestAccessEnabled: false, FullName: "group1", FullPath: "group1", }, }, Section: "test", }, } if !reflect.DeepEqual(want, approvals) { t.Errorf("MergeRequestApprovals.GetApprovalRules returned %+v, want %+v", approvals, want) } } func TestCreateApprovalRules(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/merge_requests/1/approval_rules", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) fmt.Fprint(w, `{ "id": 1, "name": "security", "rule_type": "regular", "eligible_approvers": [ { "id": 5, "name": "John Doe", "username": "jdoe", "state": "active", "avatar_url": "https://www.gravatar.com/avatar/0?s=80&d=identicon", "web_url": "http://localhost/jdoe" }, { "id": 50, "name": "Group Member 1", "username": "group_member_1", "state": "active", "avatar_url": "https://www.gravatar.com/avatar/0?s=80&d=identicon", "web_url": "http://localhost/group_member_1" } ], "approvals_required": 3, "source_rule": null, "users": [ { "id": 5, "name": "John Doe", "username": "jdoe", "state": "active", "avatar_url": "https://www.gravatar.com/avatar/0?s=80&d=identicon", "web_url": "http://localhost/jdoe" } ], "groups": [ { "id": 5, "name": "group1", "path": "group1", "description": "", "visibility": "public", "lfs_enabled": false, "avatar_url": null, "web_url": "http://localhost/groups/group1", "request_access_enabled": false, "full_name": "group1", "full_path": "group1", "parent_id": null, "ldap_cn": null, "ldap_access": null } ], "contains_hidden_groups": false }`) }) opt := &CreateMergeRequestApprovalRuleOptions{ Name: Ptr("security"), ApprovalsRequired: Ptr(3), UserIDs: &[]int{5, 50}, GroupIDs: &[]int{5}, } rule, _, err := client.MergeRequestApprovals.CreateApprovalRule(1, 1, opt) if err != nil { t.Errorf("MergeRequestApprovals.CreateApprovalRule returned error: %v", err) } want := &MergeRequestApprovalRule{ ID: 1, Name: "security", RuleType: "regular", EligibleApprovers: []*BasicUser{ { ID: 5, Name: "John Doe", Username: "jdoe", State: "active", AvatarURL: "https://www.gravatar.com/avatar/0?s=80&d=identicon", WebURL: "http://localhost/jdoe", }, { ID: 50, Name: "Group Member 1", Username: "group_member_1", State: "active", AvatarURL: "https://www.gravatar.com/avatar/0?s=80&d=identicon", WebURL: "http://localhost/group_member_1", }, }, ApprovalsRequired: 3, Users: []*BasicUser{ { ID: 5, Name: "John Doe", Username: "jdoe", State: "active", AvatarURL: "https://www.gravatar.com/avatar/0?s=80&d=identicon", WebURL: "http://localhost/jdoe", }, }, Groups: []*Group{ { ID: 5, Name: "group1", Path: "group1", Description: "", Visibility: PublicVisibility, LFSEnabled: false, AvatarURL: "", WebURL: "http://localhost/groups/group1", RequestAccessEnabled: false, FullName: "group1", FullPath: "group1", }, }, } if !reflect.DeepEqual(want, rule) { t.Errorf("MergeRequestApprovals.CreateApprovalRule returned %+v, want %+v", rule, want) } } golang-gitlab-gitlab-org-api-client-go-0.123.0/merge_requests.go000066400000000000000000001515051475761473200244470ustar00rootroot00000000000000// // Copyright 2021, Sander van Harmelen // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "bytes" "encoding/json" "fmt" "net/http" "time" ) // MergeRequestsService handles communication with the merge requests related // methods of the GitLab API. // // GitLab API docs: https://docs.gitlab.com/ee/api/merge_requests.html type MergeRequestsService struct { client *Client timeStats *timeStatsService } // MergeRequest represents a GitLab merge request. // // GitLab API docs: https://docs.gitlab.com/ee/api/merge_requests.html type MergeRequest struct { ID int `json:"id"` IID int `json:"iid"` TargetBranch string `json:"target_branch"` SourceBranch string `json:"source_branch"` ProjectID int `json:"project_id"` Title string `json:"title"` State string `json:"state"` CreatedAt *time.Time `json:"created_at"` UpdatedAt *time.Time `json:"updated_at"` Upvotes int `json:"upvotes"` Downvotes int `json:"downvotes"` Author *BasicUser `json:"author"` Assignee *BasicUser `json:"assignee"` Assignees []*BasicUser `json:"assignees"` Reviewers []*BasicUser `json:"reviewers"` SourceProjectID int `json:"source_project_id"` TargetProjectID int `json:"target_project_id"` Labels Labels `json:"labels"` LabelDetails []*LabelDetails `json:"label_details"` Description string `json:"description"` Draft bool `json:"draft"` WorkInProgress bool `json:"work_in_progress"` Milestone *Milestone `json:"milestone"` MergeWhenPipelineSucceeds bool `json:"merge_when_pipeline_succeeds"` DetailedMergeStatus string `json:"detailed_merge_status"` MergeError string `json:"merge_error"` MergedBy *BasicUser `json:"merged_by"` MergedAt *time.Time `json:"merged_at"` ClosedBy *BasicUser `json:"closed_by"` ClosedAt *time.Time `json:"closed_at"` Subscribed bool `json:"subscribed"` SHA string `json:"sha"` MergeCommitSHA string `json:"merge_commit_sha"` SquashCommitSHA string `json:"squash_commit_sha"` UserNotesCount int `json:"user_notes_count"` ChangesCount string `json:"changes_count"` ShouldRemoveSourceBranch bool `json:"should_remove_source_branch"` ForceRemoveSourceBranch bool `json:"force_remove_source_branch"` AllowCollaboration bool `json:"allow_collaboration"` WebURL string `json:"web_url"` References *IssueReferences `json:"references"` DiscussionLocked bool `json:"discussion_locked"` Changes []*MergeRequestDiff `json:"changes"` User struct { CanMerge bool `json:"can_merge"` } `json:"user"` TimeStats *TimeStats `json:"time_stats"` Squash bool `json:"squash"` Pipeline *PipelineInfo `json:"pipeline"` HeadPipeline *Pipeline `json:"head_pipeline"` DiffRefs struct { BaseSha string `json:"base_sha"` HeadSha string `json:"head_sha"` StartSha string `json:"start_sha"` } `json:"diff_refs"` DivergedCommitsCount int `json:"diverged_commits_count"` RebaseInProgress bool `json:"rebase_in_progress"` ApprovalsBeforeMerge int `json:"approvals_before_merge"` Reference string `json:"reference"` FirstContribution bool `json:"first_contribution"` TaskCompletionStatus *TasksCompletionStatus `json:"task_completion_status"` HasConflicts bool `json:"has_conflicts"` BlockingDiscussionsResolved bool `json:"blocking_discussions_resolved"` Overflow bool `json:"overflow"` // Deprecated: This parameter is replaced by DetailedMergeStatus in GitLab 15.6. MergeStatus string `json:"merge_status"` } func (m MergeRequest) String() string { return Stringify(m) } func (m *MergeRequest) UnmarshalJSON(data []byte) error { type alias MergeRequest raw := make(map[string]interface{}) err := json.Unmarshal(data, &raw) if err != nil { return err } labelDetails, ok := raw["labels"].([]interface{}) if ok && len(labelDetails) > 0 { // We only want to change anything if we got label details. if _, ok := labelDetails[0].(map[string]interface{}); !ok { return json.Unmarshal(data, (*alias)(m)) } labels := make([]interface{}, len(labelDetails)) for i, details := range labelDetails { labels[i] = details.(map[string]interface{})["name"] } // Set the correct values raw["labels"] = labels raw["label_details"] = labelDetails data, err = json.Marshal(raw) if err != nil { return err } } return json.Unmarshal(data, (*alias)(m)) } // MergeRequestDiff represents Gitlab merge request diff. // // Gitlab API docs: // https://docs.gitlab.com/ee/api/merge_requests.html#list-merge-request-diffs type MergeRequestDiff struct { OldPath string `json:"old_path"` NewPath string `json:"new_path"` AMode string `json:"a_mode"` BMode string `json:"b_mode"` Diff string `json:"diff"` NewFile bool `json:"new_file"` RenamedFile bool `json:"renamed_file"` DeletedFile bool `json:"deleted_file"` } // MergeRequestDiffVersion represents Gitlab merge request version. // // Gitlab API docs: // https://docs.gitlab.com/ee/api/merge_requests.html#get-merge-request-diff-versions type MergeRequestDiffVersion struct { ID int `json:"id"` HeadCommitSHA string `json:"head_commit_sha,omitempty"` BaseCommitSHA string `json:"base_commit_sha,omitempty"` StartCommitSHA string `json:"start_commit_sha,omitempty"` CreatedAt *time.Time `json:"created_at,omitempty"` MergeRequestID int `json:"merge_request_id,omitempty"` State string `json:"state,omitempty"` RealSize string `json:"real_size,omitempty"` Commits []*Commit `json:"commits,omitempty"` Diffs []*Diff `json:"diffs,omitempty"` } func (m MergeRequestDiffVersion) String() string { return Stringify(m) } // ListMergeRequestsOptions represents the available ListMergeRequests() // options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_requests.html#list-merge-requests type ListMergeRequestsOptions struct { ListOptions Approved *string `url:"approved,omitempty" json:"approved,omitempty"` State *string `url:"state,omitempty" json:"state,omitempty"` OrderBy *string `url:"order_by,omitempty" json:"order_by,omitempty"` Sort *string `url:"sort,omitempty" json:"sort,omitempty"` Milestone *string `url:"milestone,omitempty" json:"milestone,omitempty"` View *string `url:"view,omitempty" json:"view,omitempty"` Labels *LabelOptions `url:"labels,comma,omitempty" json:"labels,omitempty"` NotLabels *LabelOptions `url:"not[labels],comma,omitempty" json:"not[labels],omitempty"` WithLabelsDetails *bool `url:"with_labels_details,omitempty" json:"with_labels_details,omitempty"` WithMergeStatusRecheck *bool `url:"with_merge_status_recheck,omitempty" json:"with_merge_status_recheck,omitempty"` CreatedAfter *time.Time `url:"created_after,omitempty" json:"created_after,omitempty"` CreatedBefore *time.Time `url:"created_before,omitempty" json:"created_before,omitempty"` UpdatedAfter *time.Time `url:"updated_after,omitempty" json:"updated_after,omitempty"` UpdatedBefore *time.Time `url:"updated_before,omitempty" json:"updated_before,omitempty"` Scope *string `url:"scope,omitempty" json:"scope,omitempty"` AuthorID *int `url:"author_id,omitempty" json:"author_id,omitempty"` AuthorUsername *string `url:"author_username,omitempty" json:"author_username,omitempty"` NotAuthorUsername *string `url:"not[author_username],omitempty" json:"not[author_username],omitempty"` AssigneeID *AssigneeIDValue `url:"assignee_id,omitempty" json:"assignee_id,omitempty"` ApproverIDs *ApproverIDsValue `url:"approver_ids,omitempty" json:"approver_ids,omitempty"` ApprovedByIDs *ApproverIDsValue `url:"approved_by_ids,omitempty" json:"approved_by_ids,omitempty"` ReviewerID *ReviewerIDValue `url:"reviewer_id,omitempty" json:"reviewer_id,omitempty"` ReviewerUsername *string `url:"reviewer_username,omitempty" json:"reviewer_username,omitempty"` MyReactionEmoji *string `url:"my_reaction_emoji,omitempty" json:"my_reaction_emoji,omitempty"` SourceBranch *string `url:"source_branch,omitempty" json:"source_branch,omitempty"` TargetBranch *string `url:"target_branch,omitempty" json:"target_branch,omitempty"` Search *string `url:"search,omitempty" json:"search,omitempty"` In *string `url:"in,omitempty" json:"in,omitempty"` Draft *bool `url:"draft,omitempty" json:"draft,omitempty"` WIP *string `url:"wip,omitempty" json:"wip,omitempty"` } // ListMergeRequests gets all merge requests. The state parameter can be used // to get only merge requests with a given state (opened, closed, or merged) // or all of them (all). The pagination parameters page and per_page can be // used to restrict the list of merge requests. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_requests.html#list-merge-requests func (s *MergeRequestsService) ListMergeRequests(opt *ListMergeRequestsOptions, options ...RequestOptionFunc) ([]*MergeRequest, *Response, error) { req, err := s.client.NewRequest(http.MethodGet, "merge_requests", opt, options) if err != nil { return nil, nil, err } var m []*MergeRequest resp, err := s.client.Do(req, &m) if err != nil { return nil, resp, err } return m, resp, nil } // ListProjectMergeRequestsOptions represents the available ListMergeRequests() // options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_requests.html#list-project-merge-requests type ListProjectMergeRequestsOptions struct { ListOptions IIDs *[]int `url:"iids[],omitempty" json:"iids,omitempty"` State *string `url:"state,omitempty" json:"state,omitempty"` OrderBy *string `url:"order_by,omitempty" json:"order_by,omitempty"` Sort *string `url:"sort,omitempty" json:"sort,omitempty"` Milestone *string `url:"milestone,omitempty" json:"milestone,omitempty"` View *string `url:"view,omitempty" json:"view,omitempty"` Labels *LabelOptions `url:"labels,comma,omitempty" json:"labels,omitempty"` NotLabels *LabelOptions `url:"not[labels],comma,omitempty" json:"not[labels],omitempty"` WithLabelsDetails *bool `url:"with_labels_details,omitempty" json:"with_labels_details,omitempty"` WithMergeStatusRecheck *bool `url:"with_merge_status_recheck,omitempty" json:"with_merge_status_recheck,omitempty"` CreatedAfter *time.Time `url:"created_after,omitempty" json:"created_after,omitempty"` CreatedBefore *time.Time `url:"created_before,omitempty" json:"created_before,omitempty"` UpdatedAfter *time.Time `url:"updated_after,omitempty" json:"updated_after,omitempty"` UpdatedBefore *time.Time `url:"updated_before,omitempty" json:"updated_before,omitempty"` Scope *string `url:"scope,omitempty" json:"scope,omitempty"` AuthorID *int `url:"author_id,omitempty" json:"author_id,omitempty"` AuthorUsername *string `url:"author_username,omitempty" json:"author_username,omitempty"` NotAuthorUsername *string `url:"not[author_username],omitempty" json:"not[author_username],omitempty"` AssigneeID *AssigneeIDValue `url:"assignee_id,omitempty" json:"assignee_id,omitempty"` ApproverIDs *ApproverIDsValue `url:"approver_ids,omitempty" json:"approver_ids,omitempty"` ApprovedByIDs *ApproverIDsValue `url:"approved_by_ids,omitempty" json:"approved_by_ids,omitempty"` ReviewerID *ReviewerIDValue `url:"reviewer_id,omitempty" json:"reviewer_id,omitempty"` ReviewerUsername *string `url:"reviewer_username,omitempty" json:"reviewer_username,omitempty"` MyReactionEmoji *string `url:"my_reaction_emoji,omitempty" json:"my_reaction_emoji,omitempty"` SourceBranch *string `url:"source_branch,omitempty" json:"source_branch,omitempty"` TargetBranch *string `url:"target_branch,omitempty" json:"target_branch,omitempty"` Search *string `url:"search,omitempty" json:"search,omitempty"` Draft *bool `url:"draft,omitempty" json:"draft,omitempty"` WIP *string `url:"wip,omitempty" json:"wip,omitempty"` } // ListProjectMergeRequests gets all merge requests for this project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_requests.html#list-project-merge-requests func (s *MergeRequestsService) ListProjectMergeRequests(pid interface{}, opt *ListProjectMergeRequestsOptions, options ...RequestOptionFunc) ([]*MergeRequest, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/merge_requests", PathEscape(project)) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } var m []*MergeRequest resp, err := s.client.Do(req, &m) if err != nil { return nil, resp, err } return m, resp, nil } // ListGroupMergeRequestsOptions represents the available ListGroupMergeRequests() // options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_requests.html#list-group-merge-requests type ListGroupMergeRequestsOptions struct { ListOptions State *string `url:"state,omitempty" json:"state,omitempty"` OrderBy *string `url:"order_by,omitempty" json:"order_by,omitempty"` Sort *string `url:"sort,omitempty" json:"sort,omitempty"` Milestone *string `url:"milestone,omitempty" json:"milestone,omitempty"` View *string `url:"view,omitempty" json:"view,omitempty"` Labels *LabelOptions `url:"labels,comma,omitempty" json:"labels,omitempty"` NotLabels *LabelOptions `url:"not[labels],comma,omitempty" json:"not[labels],omitempty"` WithLabelsDetails *bool `url:"with_labels_details,omitempty" json:"with_labels_details,omitempty"` WithMergeStatusRecheck *bool `url:"with_merge_status_recheck,omitempty" json:"with_merge_status_recheck,omitempty"` CreatedAfter *time.Time `url:"created_after,omitempty" json:"created_after,omitempty"` CreatedBefore *time.Time `url:"created_before,omitempty" json:"created_before,omitempty"` UpdatedAfter *time.Time `url:"updated_after,omitempty" json:"updated_after,omitempty"` UpdatedBefore *time.Time `url:"updated_before,omitempty" json:"updated_before,omitempty"` Scope *string `url:"scope,omitempty" json:"scope,omitempty"` AuthorID *int `url:"author_id,omitempty" json:"author_id,omitempty"` AuthorUsername *string `url:"author_username,omitempty" json:"author_username,omitempty"` NotAuthorUsername *string `url:"not[author_username],omitempty" json:"not[author_username],omitempty"` AssigneeID *AssigneeIDValue `url:"assignee_id,omitempty" json:"assignee_id,omitempty"` ApproverIDs *ApproverIDsValue `url:"approver_ids,omitempty" json:"approver_ids,omitempty"` ApprovedByIDs *ApproverIDsValue `url:"approved_by_ids,omitempty" json:"approved_by_ids,omitempty"` ReviewerID *ReviewerIDValue `url:"reviewer_id,omitempty" json:"reviewer_id,omitempty"` ReviewerUsername *string `url:"reviewer_username,omitempty" json:"reviewer_username,omitempty"` MyReactionEmoji *string `url:"my_reaction_emoji,omitempty" json:"my_reaction_emoji,omitempty"` SourceBranch *string `url:"source_branch,omitempty" json:"source_branch,omitempty"` TargetBranch *string `url:"target_branch,omitempty" json:"target_branch,omitempty"` Search *string `url:"search,omitempty" json:"search,omitempty"` In *string `url:"in,omitempty" json:"in,omitempty"` Draft *bool `url:"draft,omitempty" json:"draft,omitempty"` WIP *string `url:"wip,omitempty" json:"wip,omitempty"` } // ListGroupMergeRequests gets all merge requests for this group. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_requests.html#list-group-merge-requests func (s *MergeRequestsService) ListGroupMergeRequests(gid interface{}, opt *ListGroupMergeRequestsOptions, options ...RequestOptionFunc) ([]*MergeRequest, *Response, error) { group, err := parseID(gid) if err != nil { return nil, nil, err } u := fmt.Sprintf("groups/%s/merge_requests", PathEscape(group)) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } var m []*MergeRequest resp, err := s.client.Do(req, &m) if err != nil { return nil, resp, err } return m, resp, nil } // GetMergeRequestsOptions represents the available GetMergeRequests() // options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_requests.html#get-single-mr type GetMergeRequestsOptions struct { RenderHTML *bool `url:"render_html,omitempty" json:"render_html,omitempty"` IncludeDivergedCommitsCount *bool `url:"include_diverged_commits_count,omitempty" json:"include_diverged_commits_count,omitempty"` IncludeRebaseInProgress *bool `url:"include_rebase_in_progress,omitempty" json:"include_rebase_in_progress,omitempty"` } // GetMergeRequest shows information about a single merge request. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_requests.html#get-single-mr func (s *MergeRequestsService) GetMergeRequest(pid interface{}, mergeRequest int, opt *GetMergeRequestsOptions, options ...RequestOptionFunc) (*MergeRequest, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/merge_requests/%d", PathEscape(project), mergeRequest) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } m := new(MergeRequest) resp, err := s.client.Do(req, m) if err != nil { return nil, resp, err } return m, resp, nil } // GetMergeRequestApprovals gets information about a merge requests approvals // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_request_approvals.html#merge-request-level-mr-approvals func (s *MergeRequestsService) GetMergeRequestApprovals(pid interface{}, mergeRequest int, options ...RequestOptionFunc) (*MergeRequestApprovals, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/merge_requests/%d/approvals", PathEscape(project), mergeRequest) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } a := new(MergeRequestApprovals) resp, err := s.client.Do(req, a) if err != nil { return nil, resp, err } return a, resp, nil } // GetMergeRequestCommitsOptions represents the available GetMergeRequestCommits() // options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_requests.html#get-single-merge-request-commits type GetMergeRequestCommitsOptions ListOptions // GetMergeRequestCommits gets a list of merge request commits. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_requests.html#get-single-merge-request-commits func (s *MergeRequestsService) GetMergeRequestCommits(pid interface{}, mergeRequest int, opt *GetMergeRequestCommitsOptions, options ...RequestOptionFunc) ([]*Commit, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/merge_requests/%d/commits", PathEscape(project), mergeRequest) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } var c []*Commit resp, err := s.client.Do(req, &c) if err != nil { return nil, resp, err } return c, resp, nil } // GetMergeRequestChangesOptions represents the available GetMergeRequestChanges() // options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_requests.html#get-single-merge-request-changes type GetMergeRequestChangesOptions struct { AccessRawDiffs *bool `url:"access_raw_diffs,omitempty" json:"access_raw_diffs,omitempty"` Unidiff *bool `url:"unidiff,omitempty" json:"unidiff,omitempty"` } // GetMergeRequestChanges shows information about the merge request including // its files and changes. // // Deprecated: This endpoint has been replaced by // MergeRequestsService.ListMergeRequestDiffs() // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_requests.html#get-single-merge-request-changes func (s *MergeRequestsService) GetMergeRequestChanges(pid interface{}, mergeRequest int, opt *GetMergeRequestChangesOptions, options ...RequestOptionFunc) (*MergeRequest, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/merge_requests/%d/changes", PathEscape(project), mergeRequest) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } m := new(MergeRequest) resp, err := s.client.Do(req, m) if err != nil { return nil, resp, err } return m, resp, nil } // ListMergeRequestDiffsOptions represents the available ListMergeRequestDiffs() // options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_requests.html#list-merge-request-diffs type ListMergeRequestDiffsOptions struct { ListOptions Unidiff *bool `url:"unidiff,omitempty" json:"unidiff,omitempty"` } // ListMergeRequestDiffs List diffs of the files changed in a merge request // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_requests.html#list-merge-request-diffs func (s *MergeRequestsService) ListMergeRequestDiffs(pid interface{}, mergeRequest int, opt *ListMergeRequestDiffsOptions, options ...RequestOptionFunc) ([]*MergeRequestDiff, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/merge_requests/%d/diffs", PathEscape(project), mergeRequest) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } var m []*MergeRequestDiff resp, err := s.client.Do(req, &m) if err != nil { return nil, resp, err } return m, resp, nil } // ShowMergeRequestRawDiffsOptions represents the available ShowMergeRequestRawDiffs() // options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_requests.html#show-merge-request-raw-diffs type ShowMergeRequestRawDiffsOptions struct{} // ShowMergeRequestRawDiffs Show raw diffs of the files changed in a merge request // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_requests.html#show-merge-request-raw-diffs func (s *MergeRequestsService) ShowMergeRequestRawDiffs(pid interface{}, mergeRequest int, opt *ShowMergeRequestRawDiffsOptions, options ...RequestOptionFunc) ([]byte, *Response, error) { project, err := parseID(pid) if err != nil { return []byte{}, nil, err } u := fmt.Sprintf( "projects/%s/merge_requests/%d/raw_diffs", PathEscape(project), mergeRequest, ) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return []byte{}, nil, err } var rd bytes.Buffer resp, err := s.client.Do(req, &rd) if err != nil { return []byte{}, resp, err } return rd.Bytes(), resp, nil } // GetMergeRequestParticipants gets a list of merge request participants. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_requests.html#get-single-merge-request-participants func (s *MergeRequestsService) GetMergeRequestParticipants(pid interface{}, mergeRequest int, options ...RequestOptionFunc) ([]*BasicUser, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/merge_requests/%d/participants", PathEscape(project), mergeRequest) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } var bu []*BasicUser resp, err := s.client.Do(req, &bu) if err != nil { return nil, resp, err } return bu, resp, nil } // MergeRequestReviewer represents a GitLab merge request reviewer. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_requests.html#get-single-merge-request-reviewers type MergeRequestReviewer struct { User *BasicUser `json:"user"` State string `json:"state"` CreatedAt *time.Time `json:"created_at"` } // GetMergeRequestReviewers gets a list of merge request reviewers. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_requests.html#get-single-merge-request-reviewers func (s *MergeRequestsService) GetMergeRequestReviewers(pid interface{}, mergeRequest int, options ...RequestOptionFunc) ([]*MergeRequestReviewer, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/merge_requests/%d/reviewers", PathEscape(project), mergeRequest) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } var mrr []*MergeRequestReviewer resp, err := s.client.Do(req, &mrr) if err != nil { return nil, resp, err } return mrr, resp, nil } // ListMergeRequestPipelines gets all pipelines for the provided merge request. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_requests.html#list-merge-request-pipelines func (s *MergeRequestsService) ListMergeRequestPipelines(pid interface{}, mergeRequest int, options ...RequestOptionFunc) ([]*PipelineInfo, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/merge_requests/%d/pipelines", PathEscape(project), mergeRequest) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } var p []*PipelineInfo resp, err := s.client.Do(req, &p) if err != nil { return nil, resp, err } return p, resp, nil } // CreateMergeRequestPipeline creates a new pipeline for a merge request. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_requests.html#create-merge-request-pipeline func (s *MergeRequestsService) CreateMergeRequestPipeline(pid interface{}, mergeRequest int, options ...RequestOptionFunc) (*PipelineInfo, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/merge_requests/%d/pipelines", PathEscape(project), mergeRequest) req, err := s.client.NewRequest(http.MethodPost, u, nil, options) if err != nil { return nil, nil, err } p := new(PipelineInfo) resp, err := s.client.Do(req, p) if err != nil { return nil, resp, err } return p, resp, nil } // GetIssuesClosedOnMergeOptions represents the available GetIssuesClosedOnMerge() // options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_requests.html#list-issues-that-close-on-merge type GetIssuesClosedOnMergeOptions ListOptions // GetIssuesClosedOnMerge gets all the issues that would be closed by merging the // provided merge request. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_requests.html#list-issues-that-close-on-merge func (s *MergeRequestsService) GetIssuesClosedOnMerge(pid interface{}, mergeRequest int, opt *GetIssuesClosedOnMergeOptions, options ...RequestOptionFunc) ([]*Issue, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/merge_requests/%d/closes_issues", PathEscape(project), mergeRequest) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } var i []*Issue resp, err := s.client.Do(req, &i) if err != nil { return nil, resp, err } return i, resp, nil } // CreateMergeRequestOptions represents the available CreateMergeRequest() // options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_requests.html#create-mr type CreateMergeRequestOptions struct { Title *string `url:"title,omitempty" json:"title,omitempty"` Description *string `url:"description,omitempty" json:"description,omitempty"` SourceBranch *string `url:"source_branch,omitempty" json:"source_branch,omitempty"` TargetBranch *string `url:"target_branch,omitempty" json:"target_branch,omitempty"` Labels *LabelOptions `url:"labels,comma,omitempty" json:"labels,omitempty"` AssigneeID *int `url:"assignee_id,omitempty" json:"assignee_id,omitempty"` AssigneeIDs *[]int `url:"assignee_ids,omitempty" json:"assignee_ids,omitempty"` ReviewerIDs *[]int `url:"reviewer_ids,omitempty" json:"reviewer_ids,omitempty"` TargetProjectID *int `url:"target_project_id,omitempty" json:"target_project_id,omitempty"` MilestoneID *int `url:"milestone_id,omitempty" json:"milestone_id,omitempty"` RemoveSourceBranch *bool `url:"remove_source_branch,omitempty" json:"remove_source_branch,omitempty"` Squash *bool `url:"squash,omitempty" json:"squash,omitempty"` AllowCollaboration *bool `url:"allow_collaboration,omitempty" json:"allow_collaboration,omitempty"` ApprovalsBeforeMerge *int `url:"approvals_before_merge,omitempty" json:"approvals_before_merge,omitempty"` } // CreateMergeRequest creates a new merge request. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_requests.html#create-mr func (s *MergeRequestsService) CreateMergeRequest(pid interface{}, opt *CreateMergeRequestOptions, options ...RequestOptionFunc) (*MergeRequest, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/merge_requests", PathEscape(project)) req, err := s.client.NewRequest(http.MethodPost, u, opt, options) if err != nil { return nil, nil, err } m := new(MergeRequest) resp, err := s.client.Do(req, m) if err != nil { return nil, resp, err } return m, resp, nil } // UpdateMergeRequestOptions represents the available UpdateMergeRequest() // options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_requests.html#update-mr type UpdateMergeRequestOptions struct { Title *string `url:"title,omitempty" json:"title,omitempty"` Description *string `url:"description,omitempty" json:"description,omitempty"` TargetBranch *string `url:"target_branch,omitempty" json:"target_branch,omitempty"` AssigneeID *int `url:"assignee_id,omitempty" json:"assignee_id,omitempty"` AssigneeIDs *[]int `url:"assignee_ids,omitempty" json:"assignee_ids,omitempty"` ReviewerIDs *[]int `url:"reviewer_ids,omitempty" json:"reviewer_ids,omitempty"` Labels *LabelOptions `url:"labels,comma,omitempty" json:"labels,omitempty"` AddLabels *LabelOptions `url:"add_labels,comma,omitempty" json:"add_labels,omitempty"` RemoveLabels *LabelOptions `url:"remove_labels,comma,omitempty" json:"remove_labels,omitempty"` MilestoneID *int `url:"milestone_id,omitempty" json:"milestone_id,omitempty"` StateEvent *string `url:"state_event,omitempty" json:"state_event,omitempty"` RemoveSourceBranch *bool `url:"remove_source_branch,omitempty" json:"remove_source_branch,omitempty"` Squash *bool `url:"squash,omitempty" json:"squash,omitempty"` DiscussionLocked *bool `url:"discussion_locked,omitempty" json:"discussion_locked,omitempty"` AllowCollaboration *bool `url:"allow_collaboration,omitempty" json:"allow_collaboration,omitempty"` } // UpdateMergeRequest updates an existing project milestone. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_requests.html#update-mr func (s *MergeRequestsService) UpdateMergeRequest(pid interface{}, mergeRequest int, opt *UpdateMergeRequestOptions, options ...RequestOptionFunc) (*MergeRequest, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/merge_requests/%d", PathEscape(project), mergeRequest) req, err := s.client.NewRequest(http.MethodPut, u, opt, options) if err != nil { return nil, nil, err } m := new(MergeRequest) resp, err := s.client.Do(req, m) if err != nil { return nil, resp, err } return m, resp, nil } // DeleteMergeRequest deletes a merge request. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_requests.html#delete-a-merge-request func (s *MergeRequestsService) DeleteMergeRequest(pid interface{}, mergeRequest int, options ...RequestOptionFunc) (*Response, error) { project, err := parseID(pid) if err != nil { return nil, err } u := fmt.Sprintf("projects/%s/merge_requests/%d", PathEscape(project), mergeRequest) req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } // AcceptMergeRequestOptions represents the available AcceptMergeRequest() // options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_requests.html#merge-a-merge-request type AcceptMergeRequestOptions struct { MergeCommitMessage *string `url:"merge_commit_message,omitempty" json:"merge_commit_message,omitempty"` SquashCommitMessage *string `url:"squash_commit_message,omitempty" json:"squash_commit_message,omitempty"` Squash *bool `url:"squash,omitempty" json:"squash,omitempty"` ShouldRemoveSourceBranch *bool `url:"should_remove_source_branch,omitempty" json:"should_remove_source_branch,omitempty"` MergeWhenPipelineSucceeds *bool `url:"merge_when_pipeline_succeeds,omitempty" json:"merge_when_pipeline_succeeds,omitempty"` SHA *string `url:"sha,omitempty" json:"sha,omitempty"` } // AcceptMergeRequest merges changes submitted with MR using this API. If merge // success you get 200 OK. If it has some conflicts and can not be merged - you // get 405 and error message 'Branch cannot be merged'. If merge request is // already merged or closed - you get 405 and error message 'Method Not Allowed' // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_requests.html#merge-a-merge-request func (s *MergeRequestsService) AcceptMergeRequest(pid interface{}, mergeRequest int, opt *AcceptMergeRequestOptions, options ...RequestOptionFunc) (*MergeRequest, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/merge_requests/%d/merge", PathEscape(project), mergeRequest) req, err := s.client.NewRequest(http.MethodPut, u, opt, options) if err != nil { return nil, nil, err } m := new(MergeRequest) resp, err := s.client.Do(req, m) if err != nil { return nil, resp, err } return m, resp, nil } // CancelMergeWhenPipelineSucceeds cancels a merge when pipeline succeeds. If // you don't have permissions to accept this merge request - you'll get a 401. // If the merge request is already merged or closed - you get 405 and error // message 'Method Not Allowed'. In case the merge request is not set to be // merged when the pipeline succeeds, you'll also get a 406 error. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_requests.html#cancel-merge-when-pipeline-succeeds func (s *MergeRequestsService) CancelMergeWhenPipelineSucceeds(pid interface{}, mergeRequest int, options ...RequestOptionFunc) (*MergeRequest, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/merge_requests/%d/cancel_merge_when_pipeline_succeeds", PathEscape(project), mergeRequest) req, err := s.client.NewRequest(http.MethodPost, u, nil, options) if err != nil { return nil, nil, err } m := new(MergeRequest) resp, err := s.client.Do(req, m) if err != nil { return nil, resp, err } return m, resp, nil } // RebaseMergeRequestOptions represents the available RebaseMergeRequest() // options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_requests.html#rebase-a-merge-request type RebaseMergeRequestOptions struct { SkipCI *bool `url:"skip_ci,omitempty" json:"skip_ci,omitempty"` } // RebaseMergeRequest automatically rebases the source_branch of the merge // request against its target_branch. If you don’t have permissions to push // to the merge request’s source branch, you’ll get a 403 Forbidden response. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_requests.html#rebase-a-merge-request func (s *MergeRequestsService) RebaseMergeRequest(pid interface{}, mergeRequest int, opt *RebaseMergeRequestOptions, options ...RequestOptionFunc) (*Response, error) { project, err := parseID(pid) if err != nil { return nil, err } u := fmt.Sprintf("projects/%s/merge_requests/%d/rebase", PathEscape(project), mergeRequest) req, err := s.client.NewRequest(http.MethodPut, u, opt, options) if err != nil { return nil, err } return s.client.Do(req, nil) } // GetMergeRequestDiffVersionsOptions represents the available // GetMergeRequestDiffVersions() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_requests.html#get-merge-request-diff-versions type GetMergeRequestDiffVersionsOptions ListOptions // GetMergeRequestDiffVersions get a list of merge request diff versions. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_requests.html#get-merge-request-diff-versions func (s *MergeRequestsService) GetMergeRequestDiffVersions(pid interface{}, mergeRequest int, opt *GetMergeRequestDiffVersionsOptions, options ...RequestOptionFunc) ([]*MergeRequestDiffVersion, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/merge_requests/%d/versions", PathEscape(project), mergeRequest) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } var v []*MergeRequestDiffVersion resp, err := s.client.Do(req, &v) if err != nil { return nil, resp, err } return v, resp, nil } // GetSingleMergeRequestDiffVersionOptions represents the available // GetSingleMergeRequestDiffVersion() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_requests.html#get-a-single-merge-request-diff-version type GetSingleMergeRequestDiffVersionOptions struct { Unidiff *bool `url:"unidiff,omitempty" json:"unidiff,omitempty"` } // GetSingleMergeRequestDiffVersion get a single MR diff version // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_requests.html#get-a-single-merge-request-diff-version func (s *MergeRequestsService) GetSingleMergeRequestDiffVersion(pid interface{}, mergeRequest, version int, opt *GetSingleMergeRequestDiffVersionOptions, options ...RequestOptionFunc) (*MergeRequestDiffVersion, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/merge_requests/%d/versions/%d", PathEscape(project), mergeRequest, version) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } v := new(MergeRequestDiffVersion) resp, err := s.client.Do(req, v) if err != nil { return nil, resp, err } return v, resp, nil } // SubscribeToMergeRequest subscribes the authenticated user to the given merge // request to receive notifications. If the user is already subscribed to the // merge request, the status code 304 is returned. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_requests.html#subscribe-to-a-merge-request func (s *MergeRequestsService) SubscribeToMergeRequest(pid interface{}, mergeRequest int, options ...RequestOptionFunc) (*MergeRequest, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/merge_requests/%d/subscribe", PathEscape(project), mergeRequest) req, err := s.client.NewRequest(http.MethodPost, u, nil, options) if err != nil { return nil, nil, err } m := new(MergeRequest) resp, err := s.client.Do(req, m) if err != nil { return nil, resp, err } return m, resp, nil } // UnsubscribeFromMergeRequest unsubscribes the authenticated user from the // given merge request to not receive notifications from that merge request. // If the user is not subscribed to the merge request, status code 304 is // returned. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_requests.html#unsubscribe-from-a-merge-request func (s *MergeRequestsService) UnsubscribeFromMergeRequest(pid interface{}, mergeRequest int, options ...RequestOptionFunc) (*MergeRequest, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/merge_requests/%d/unsubscribe", PathEscape(project), mergeRequest) req, err := s.client.NewRequest(http.MethodPost, u, nil, options) if err != nil { return nil, nil, err } m := new(MergeRequest) resp, err := s.client.Do(req, m) if err != nil { return nil, resp, err } return m, resp, nil } // CreateTodo manually creates a todo for the current user on a merge request. // If there already exists a todo for the user on that merge request, // status code 304 is returned. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_requests.html#create-a-to-do-item func (s *MergeRequestsService) CreateTodo(pid interface{}, mergeRequest int, options ...RequestOptionFunc) (*Todo, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/merge_requests/%d/todo", PathEscape(project), mergeRequest) req, err := s.client.NewRequest(http.MethodPost, u, nil, options) if err != nil { return nil, nil, err } t := new(Todo) resp, err := s.client.Do(req, t) if err != nil { return nil, resp, err } return t, resp, nil } // SetTimeEstimate sets the time estimate for a single project merge request. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_requests.html#set-a-time-estimate-for-a-merge-request func (s *MergeRequestsService) SetTimeEstimate(pid interface{}, mergeRequest int, opt *SetTimeEstimateOptions, options ...RequestOptionFunc) (*TimeStats, *Response, error) { return s.timeStats.setTimeEstimate(pid, "merge_requests", mergeRequest, opt, options...) } // ResetTimeEstimate resets the time estimate for a single project merge request. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_requests.html#reset-the-time-estimate-for-a-merge-request func (s *MergeRequestsService) ResetTimeEstimate(pid interface{}, mergeRequest int, options ...RequestOptionFunc) (*TimeStats, *Response, error) { return s.timeStats.resetTimeEstimate(pid, "merge_requests", mergeRequest, options...) } // AddSpentTime adds spent time for a single project merge request. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_requests.html#add-spent-time-for-a-merge-request func (s *MergeRequestsService) AddSpentTime(pid interface{}, mergeRequest int, opt *AddSpentTimeOptions, options ...RequestOptionFunc) (*TimeStats, *Response, error) { return s.timeStats.addSpentTime(pid, "merge_requests", mergeRequest, opt, options...) } // ResetSpentTime resets the spent time for a single project merge request. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_requests.html#reset-spent-time-for-a-merge-request func (s *MergeRequestsService) ResetSpentTime(pid interface{}, mergeRequest int, options ...RequestOptionFunc) (*TimeStats, *Response, error) { return s.timeStats.resetSpentTime(pid, "merge_requests", mergeRequest, options...) } // GetTimeSpent gets the spent time for a single project merge request. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_requests.html#get-time-tracking-stats func (s *MergeRequestsService) GetTimeSpent(pid interface{}, mergeRequest int, options ...RequestOptionFunc) (*TimeStats, *Response, error) { return s.timeStats.getTimeSpent(pid, "merge_requests", mergeRequest, options...) } // MergeRequestDependency represents a GitLab merge request dependency. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_requests.html#create-a-merge-request-dependency type MergeRequestDependency struct { ID int `json:"id"` BlockingMergeRequest BlockingMergeRequest `json:"blocking_merge_request"` ProjectID int `json:"project_id"` } // BlockingMergeRequest represents a GitLab merge request dependency. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_requests.html#create-a-merge-request-dependency type BlockingMergeRequest struct { ID int `json:"id"` Iid int `json:"iid"` TargetBranch string `json:"target_branch"` SourceBranch string `json:"source_branch"` ProjectID int `json:"project_id"` Title string `json:"title"` State string `json:"state"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` Upvotes int `json:"upvotes"` Downvotes int `json:"downvotes"` Author *BasicUser `json:"author"` Assignee *BasicUser `json:"assignee"` Assignees []*BasicUser `json:"assignees"` Reviewers []*BasicUser `json:"reviewers"` SourceProjectID int `json:"source_project_id"` TargetProjectID int `json:"target_project_id"` Labels *LabelOptions `json:"labels"` Description string `json:"description"` Draft bool `json:"draft"` WorkInProgress bool `json:"work_in_progress"` Milestone *string `json:"milestone"` MergeWhenPipelineSucceeds bool `json:"merge_when_pipeline_succeeds"` DetailedMergeStatus string `json:"detailed_merge_status"` MergedBy *BasicUser `json:"merged_by"` MergedAt *time.Time `json:"merged_at"` ClosedBy *BasicUser `json:"closed_by"` ClosedAt *time.Time `json:"closed_at"` Sha string `json:"sha"` MergeCommitSha string `json:"merge_commit_sha"` SquashCommitSha string `json:"squash_commit_sha"` UserNotesCount int `json:"user_notes_count"` ShouldRemoveSourceBranch *bool `json:"should_remove_source_branch"` ForceRemoveSourceBranch bool `json:"force_remove_source_branch"` WebURL string `json:"web_url"` References *IssueReferences `json:"references"` DiscussionLocked *bool `json:"discussion_locked"` TimeStats *TimeStats `json:"time_stats"` Squash bool `json:"squash"` ApprovalsBeforeMerge *int `json:"approvals_before_merge"` Reference string `json:"reference"` TaskCompletionStatus *TasksCompletionStatus `json:"task_completion_status"` HasConflicts bool `json:"has_conflicts"` BlockingDiscussionsResolved bool `json:"blocking_discussions_resolved"` MergeStatus string `json:"merge_status"` MergeUser *BasicUser `json:"merge_user"` MergeAfter time.Time `json:"merge_after"` Imported bool `json:"imported"` ImportedFrom string `json:"imported_from"` PreparedAt *time.Time `json:"prepared_at"` SquashOnMerge bool `json:"squash_on_merge"` } func (m MergeRequestDependency) String() string { return Stringify(m) } // CreateMergeRequestDependencyOptions represents the available CreateMergeRequestDependency() // options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_requests.html#create-a-merge-request-dependency type CreateMergeRequestDependencyOptions struct { BlockingMergeRequestID *int `url:"blocking_merge_request_id,omitempty" json:"blocking_merge_request_id,omitempty"` } // CreateMergeRequestDependency creates a new merge request dependency for a given // merge request. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_requests.html#create-a-merge-request-dependency func (s *MergeRequestsService) CreateMergeRequestDependency(pid interface{}, mergeRequest int, opts CreateMergeRequestDependencyOptions, options ...RequestOptionFunc) ([]MergeRequestDependency, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/merge_requests/%d/blocks", PathEscape(project), mergeRequest) req, err := s.client.NewRequest(http.MethodPost, u, opts, options) if err != nil { return nil, nil, err } var mrd []MergeRequestDependency resp, err := s.client.Do(req, &mrd) if err != nil { return nil, resp, err } return mrd, resp, err } // DeleteMergeRequestDependency deletes a merge request dependency for a given // merge request. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_requests.html#delete-a-merge-request-dependency func (s *MergeRequestsService) DeleteMergeRequestDependency(pid interface{}, mergeRequest int, blockingMergeRequest int, options ...RequestOptionFunc) (*Response, error) { project, err := parseID(pid) if err != nil { return nil, err } u := fmt.Sprintf("projects/%s/merge_requests/%d/blocks/%d", PathEscape(project), mergeRequest, blockingMergeRequest) req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) if err != nil { return nil, err } resp, err := s.client.Do(req, nil) if err != nil { return resp, err } return resp, err } // GetMergeRequestDependencies gets a list of merge request dependencies. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_requests.html#get-merge-request-dependencies func (s *MergeRequestsService) GetMergeRequestDependencies(pid interface{}, mergeRequest int, options ...RequestOptionFunc) ([]MergeRequestDependency, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/merge_requests/%d/blocks", PathEscape(project), mergeRequest) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } var mrd []MergeRequestDependency resp, err := s.client.Do(req, &mrd) if err != nil { return nil, resp, err } return mrd, resp, err } golang-gitlab-gitlab-org-api-client-go-0.123.0/merge_requests_test.go000066400000000000000000000531521475761473200255050ustar00rootroot00000000000000// // Copyright 2021, Sander van Harmelen // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "encoding/json" "fmt" "net/http" "reflect" "testing" "time" "github.com/google/go-querystring/query" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) var ( ajk = BasicUser{ ID: 3614858, Name: "Alex Kalderimis", Username: "alexkalderimis", State: "active", AvatarURL: "https://assets.gitlab-static.net/uploads/-/system/user/avatar/3614858/avatar.png", WebURL: "https://gitlab.com/alexkalderimis", } tk = BasicUser{ ID: 2535118, Name: "Thong Kuah", Username: "tkuah", State: "active", AvatarURL: "https://secure.gravatar.com/avatar/f7b51bdd49a4914d29504d7ff4c3f7b9?s=80&d=identicon", WebURL: "https://gitlab.com/tkuah", } getOpts = GetMergeRequestsOptions{} labels = Labels{ "GitLab Enterprise Edition", "backend", "database", "database::reviewed", "design management", "feature", "frontend", "group::knowledge", "missed:12.1", } pipelineCreation = time.Date(2019, 8, 19, 9, 50, 58, 157000000, time.UTC) pipelineUpdate = time.Date(2019, 8, 19, 19, 22, 29, 647000000, time.UTC) pipelineBasic = PipelineInfo{ ID: 77056819, SHA: "8e0b45049b6253b8984cde9241830d2851168142", Ref: "delete-designs-v2", Status: "success", WebURL: "https://gitlab.com/gitlab-org/gitlab-ee/pipelines/77056819", CreatedAt: &pipelineCreation, UpdatedAt: &pipelineUpdate, } pipelineStarted = time.Date(2019, 8, 19, 9, 51, 6, 545000000, time.UTC) pipelineFinished = time.Date(2019, 8, 19, 19, 22, 29, 632000000, time.UTC) pipelineDetailed = Pipeline{ ID: 77056819, SHA: "8e0b45049b6253b8984cde9241830d2851168142", Ref: "delete-designs-v2", Status: "success", WebURL: "https://gitlab.com/gitlab-org/gitlab-ee/pipelines/77056819", BeforeSHA: "3fe568caacb261b63090886f5b879ca0d9c6f4c3", Tag: false, User: &ajk, CreatedAt: &pipelineCreation, UpdatedAt: &pipelineUpdate, StartedAt: &pipelineStarted, FinishedAt: &pipelineFinished, Duration: 4916, Coverage: "82.68", DetailedStatus: &DetailedStatus{ Icon: "status_warning", Text: "passed", Label: "passed with warnings", Group: "success-with-warnings", Tooltip: "passed", HasDetails: true, DetailsPath: "/gitlab-org/gitlab-ee/pipelines/77056819", Favicon: "https://gitlab.com/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png", }, } ) func TestGetMergeRequest(t *testing.T) { mux, client := setup(t) path := `/api/v4/projects/namespace%2Fname/merge_requests/123` mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) mustWriteHTTPResponse(t, w, "testdata/get_merge_request.json") }) mergeRequest, _, err := client.MergeRequests.GetMergeRequest("namespace/name", 123, &getOpts) require.NoError(t, err) require.Equal(t, mergeRequest.ID, 33092005) require.Equal(t, mergeRequest.SHA, "8e0b45049b6253b8984cde9241830d2851168142") require.Equal(t, mergeRequest.IID, 14656) require.Equal(t, mergeRequest.Reference, "!14656") require.Equal(t, mergeRequest.ProjectID, 278964) require.Equal(t, mergeRequest.SourceBranch, "delete-designs-v2") require.Equal(t, mergeRequest.TaskCompletionStatus.Count, 9) require.Equal(t, mergeRequest.TaskCompletionStatus.CompletedCount, 8) require.Equal(t, mergeRequest.Title, "Add deletion support for designs") require.Equal(t, mergeRequest.Description, "## What does this MR do?\r\n\r\nThis adds the capability to destroy/hide designs.") require.Equal(t, mergeRequest.WebURL, "https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/14656") require.Equal(t, mergeRequest.DetailedMergeStatus, "mergeable") require.Equal(t, mergeRequest.Author, &ajk) require.Equal(t, mergeRequest.Assignee, &tk) require.Equal(t, mergeRequest.Assignees, []*BasicUser{&tk}) require.Equal(t, mergeRequest.Reviewers, []*BasicUser{&tk}) require.Equal(t, mergeRequest.Labels, labels) require.Equal(t, mergeRequest.Squash, true) require.Equal(t, mergeRequest.UserNotesCount, 245) require.Equal(t, mergeRequest.Pipeline, &pipelineBasic) require.Equal(t, mergeRequest.HeadPipeline, &pipelineDetailed) mrCreation := time.Date(2019, 7, 11, 22, 34, 43, 500000000, time.UTC) require.Equal(t, mergeRequest.CreatedAt, &mrCreation) mrUpdate := time.Date(2019, 8, 20, 9, 9, 56, 690000000, time.UTC) require.Equal(t, mergeRequest.UpdatedAt, &mrUpdate) require.Equal(t, mergeRequest.FirstContribution, true) require.Equal(t, mergeRequest.HasConflicts, true) require.Equal(t, mergeRequest.Draft, true) } func TestListProjectMergeRequests(t *testing.T) { mux, client := setup(t) path := "/api/v4/projects/278964/merge_requests" mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) testParams(t, r, "assignee_id=Any&with_labels_details=true&with_merge_status_recheck=true") mustWriteHTTPResponse(t, w, "testdata/get_merge_requests.json") }) opts := ListProjectMergeRequestsOptions{ AssigneeID: AssigneeID(UserIDAny), WithLabelsDetails: Ptr(true), WithMergeStatusRecheck: Ptr(true), } mergeRequests, _, err := client.MergeRequests.ListProjectMergeRequests(278964, &opts) require.NoError(t, err) require.Equal(t, 3, len(mergeRequests)) validStates := []string{"opened", "closed", "locked", "merged"} detailedMergeStatuses := []string{ "blocked_status", "broken_status", "checking", "ci_must_pass", "ci_still_running", "discussions_not_resolved", "draft_status", "external_status_checks", "mergeable", "not_approved", "not_open", "policies_denied", "unchecked", } allCreatedBefore := time.Date(2019, 8, 21, 0, 0, 0, 0, time.UTC) allCreatedAfter := time.Date(2019, 8, 17, 0, 0, 0, 0, time.UTC) for _, mr := range mergeRequests { require.Equal(t, 278964, mr.ProjectID) require.Contains(t, validStates, mr.State) assert.Less(t, mr.CreatedAt.Unix(), allCreatedBefore.Unix()) assert.Greater(t, mr.CreatedAt.Unix(), allCreatedAfter.Unix()) assert.LessOrEqual(t, mr.CreatedAt.Unix(), mr.UpdatedAt.Unix()) assert.LessOrEqual(t, mr.TaskCompletionStatus.CompletedCount, mr.TaskCompletionStatus.Count) require.Contains(t, detailedMergeStatuses, mr.DetailedMergeStatus) // list requests do not provide these fields: assert.Nil(t, mr.Pipeline) assert.Nil(t, mr.HeadPipeline) assert.Equal(t, "", mr.DiffRefs.HeadSha) } } func TestListProjectMergeRequestsAuthorUsername(t *testing.T) { mux, client := setup(t) path := "/api/v4/projects/278964/merge_requests" mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) testParams(t, r, "assignee_id=Any&author_username=hfyngvason&with_labels_details=true&with_merge_status_recheck=true") mustWriteHTTPResponse(t, w, "testdata/get_merge_requests_author_username.json") }) opts := ListProjectMergeRequestsOptions{ AssigneeID: AssigneeID(UserIDAny), AuthorUsername: String("hfyngvason"), WithLabelsDetails: Bool(true), WithMergeStatusRecheck: Bool(true), } mergeRequests, _, err := client.MergeRequests.ListProjectMergeRequests(278964, &opts) require.NoError(t, err) require.Equal(t, 1, len(mergeRequests)) validStates := []string{"opened", "closed", "locked", "merged"} detailedMergeStatuses := []string{ "blocked_status", "broken_status", "checking", "ci_must_pass", "ci_still_running", "discussions_not_resolved", "draft_status", "external_status_checks", "mergeable", "not_approved", "not_open", "policies_denied", "unchecked", } allCreatedBefore := time.Date(2019, 8, 21, 0, 0, 0, 0, time.UTC) allCreatedAfter := time.Date(2019, 8, 17, 0, 0, 0, 0, time.UTC) for _, mr := range mergeRequests { require.Equal(t, 278964, mr.ProjectID) require.Contains(t, validStates, mr.State) assert.Less(t, mr.CreatedAt.Unix(), allCreatedBefore.Unix()) assert.Greater(t, mr.CreatedAt.Unix(), allCreatedAfter.Unix()) assert.LessOrEqual(t, mr.CreatedAt.Unix(), mr.UpdatedAt.Unix()) assert.LessOrEqual(t, mr.TaskCompletionStatus.CompletedCount, mr.TaskCompletionStatus.Count) require.Contains(t, detailedMergeStatuses, mr.DetailedMergeStatus) // list requests do not provide these fields: assert.Nil(t, mr.Pipeline) assert.Nil(t, mr.HeadPipeline) assert.Equal(t, "", mr.DiffRefs.HeadSha) } } func TestListProjectMergeRequestsNotAuthorUsername(t *testing.T) { mux, client := setup(t) path := "/api/v4/projects/278964/merge_requests" mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) testParams(t, r, "assignee_id=Any¬%5Bauthor_username%5D=hfyngvason&with_labels_details=true&with_merge_status_recheck=true") mustWriteHTTPResponse(t, w, "testdata/get_merge_requests_not_author_username.json") }) opts := ListProjectMergeRequestsOptions{ AssigneeID: AssigneeID(UserIDAny), NotAuthorUsername: String("hfyngvason"), WithLabelsDetails: Bool(true), WithMergeStatusRecheck: Bool(true), } mergeRequests, _, err := client.MergeRequests.ListProjectMergeRequests(278964, &opts) require.NoError(t, err) require.Equal(t, 2, len(mergeRequests)) validStates := []string{"opened", "closed", "locked", "merged"} detailedMergeStatuses := []string{ "blocked_status", "broken_status", "checking", "ci_must_pass", "ci_still_running", "discussions_not_resolved", "draft_status", "external_status_checks", "mergeable", "not_approved", "not_open", "policies_denied", "unchecked", } allCreatedBefore := time.Date(2019, 8, 21, 0, 0, 0, 0, time.UTC) allCreatedAfter := time.Date(2019, 8, 17, 0, 0, 0, 0, time.UTC) for _, mr := range mergeRequests { require.Equal(t, 278964, mr.ProjectID) require.Contains(t, validStates, mr.State) assert.Less(t, mr.CreatedAt.Unix(), allCreatedBefore.Unix()) assert.Greater(t, mr.CreatedAt.Unix(), allCreatedAfter.Unix()) assert.LessOrEqual(t, mr.CreatedAt.Unix(), mr.UpdatedAt.Unix()) assert.LessOrEqual(t, mr.TaskCompletionStatus.CompletedCount, mr.TaskCompletionStatus.Count) require.Contains(t, detailedMergeStatuses, mr.DetailedMergeStatus) // list requests do not provide these fields: assert.Nil(t, mr.Pipeline) assert.Nil(t, mr.HeadPipeline) assert.Equal(t, "", mr.DiffRefs.HeadSha) } } func TestCreateMergeRequestPipeline(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/merge_requests/1/pipelines", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) fmt.Fprint(w, `{"id":1, "status":"pending"}`) }) pipeline, _, err := client.MergeRequests.CreateMergeRequestPipeline(1, 1) if err != nil { t.Errorf("MergeRequests.CreateMergeRequestPipeline returned error: %v", err) } assert.Equal(t, 1, pipeline.ID) assert.Equal(t, "pending", pipeline.Status) } func TestGetMergeRequestParticipants(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/merge_requests/5/participants", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) testURL(t, r, "/api/v4/projects/1/merge_requests/5/participants") fmt.Fprint(w, `[{"id":1,"name":"User1","username":"User1","state":"active","avatar_url":"","web_url":"https://localhost/User1"}, {"id":2,"name":"User2","username":"User2","state":"active","avatar_url":"https://localhost/uploads/-/system/user/avatar/2/avatar.png","web_url":"https://localhost/User2"}]`) }) mergeRequestParticipants, _, err := client.MergeRequests.GetMergeRequestParticipants("1", 5) if err != nil { t.Fatal(err) } want := []*BasicUser{ {ID: 1, Name: "User1", Username: "User1", State: "active", AvatarURL: "", WebURL: "https://localhost/User1"}, {ID: 2, Name: "User2", Username: "User2", State: "active", AvatarURL: "https://localhost/uploads/-/system/user/avatar/2/avatar.png", WebURL: "https://localhost/User2"}, } if !reflect.DeepEqual(want, mergeRequestParticipants) { t.Errorf("Issues.GetMergeRequestParticipants returned %+v, want %+v", mergeRequestParticipants, want) } } func TestGetMergeRequestReviewers(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/merge_requests/5/reviewers", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) testURL(t, r, "/api/v4/projects/1/merge_requests/5/reviewers") fmt.Fprint(w, `[{"user":{"id":1,"name":"John Doe1","username":"user1","state":"active","avatar_url":"http://www.gravatar.com/avatar/c922747a93b40d1ea88262bf1aebee62?s=80&d=identicon","web_url":"http://localhost/user1"},"state":"unreviewed","created_at":"2022-07-27T17:03:27.684Z"},{"user":{"id":2,"name":"John Doe2","username":"user2","state":"active","avatar_url":"http://www.gravatar.com/avatar/10fc7f102be8de7657fb4d80898bbfe3?s=80&d=identicon","web_url":"http://localhost/user2"},"state":"reviewed","created_at":"2022-07-27T17:03:27.684Z"}]`) }) mergeRequestReviewers, _, err := client.MergeRequests.GetMergeRequestReviewers("1", 5) if err != nil { t.Fatal(err) } createdAt := time.Date(2022, 0o7, 27, 17, 3, 27, 684000000, time.UTC) user1 := BasicUser{ID: 1, Name: "John Doe1", Username: "user1", State: "active", AvatarURL: "http://www.gravatar.com/avatar/c922747a93b40d1ea88262bf1aebee62?s=80&d=identicon", WebURL: "http://localhost/user1"} user2 := BasicUser{ID: 2, Name: "John Doe2", Username: "user2", State: "active", AvatarURL: "http://www.gravatar.com/avatar/10fc7f102be8de7657fb4d80898bbfe3?s=80&d=identicon", WebURL: "http://localhost/user2"} assert.Len(t, mergeRequestReviewers, 2) assert.Equal(t, "unreviewed", mergeRequestReviewers[0].State) require.Equal(t, &user1, mergeRequestReviewers[0].User) require.Equal(t, &createdAt, mergeRequestReviewers[0].CreatedAt) assert.Equal(t, "reviewed", mergeRequestReviewers[1].State) require.Equal(t, &user2, mergeRequestReviewers[1].User) require.Equal(t, &createdAt, mergeRequestReviewers[1].CreatedAt) } func TestGetIssuesClosedOnMerge_Jira(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/merge_requests/1/closes_issues", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") fmt.Fprint(w, `[{"id":"PROJECT-123","title":"Title of this issue"}]`) }) issues, _, err := client.MergeRequests.GetIssuesClosedOnMerge(1, 1, nil) assert.NoError(t, err) assert.Len(t, issues, 1) assert.Equal(t, "PROJECT-123", issues[0].ExternalID) assert.Equal(t, "Title of this issue", issues[0].Title) } func TestListMergeRequestDiffs(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/merge_requests/1/diffs", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) mustWriteHTTPResponse(t, w, "testdata/list_merge_request_diff.json") }) opts := &ListMergeRequestDiffsOptions{ ListOptions: ListOptions{Page: 1, PerPage: 2}, } diffs, _, err := client.MergeRequests.ListMergeRequestDiffs(1, 1, opts) if err != nil { t.Errorf("MergeRequests.ListMergeRequestDiffs returned error: %v", err) } want := []*MergeRequestDiff{ { OldPath: "README", NewPath: "README", AMode: "100644", BMode: "100644", Diff: "@@ -1 +1 @@ -Title +README", NewFile: false, RenamedFile: false, DeletedFile: false, }, { OldPath: "VERSION", NewPath: "VERSION", AMode: "100644", BMode: "100644", Diff: "@@ -1.9.7 +1.9.8", NewFile: false, RenamedFile: false, DeletedFile: false, }, } if !reflect.DeepEqual(want, diffs) { t.Errorf("MergeRequests.ListMergeRequestDiffs returned %+v, want %+v", diffs, want) } } func TestShowMergeRequestRawDiffs(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/merge_requests/1/raw_diffs", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) mustWriteHTTPResponse(t, w, "testdata/show_merge_request_raw_diff.txt") }) opts := &ShowMergeRequestRawDiffsOptions{} rawDiff, _, err := client.MergeRequests.ShowMergeRequestRawDiffs(1, 1, opts) if err != nil { t.Errorf("MergeRequests.ShowMergeRequestRawDiffs returned error: %v", err) } want := `diff --git a/also_testing b/also_testing index d4d510b..2a2c3b1 100644 --- a/also_testing +++ b/also_testing @@ -1,3 +1,2 @@ aaaaaaaaaaaaaaaa -bbbbbbbbbbbbbbbb cccccccccccccccc diff --git a/testing b/testing index c7c7da3..ce2cd85 100644 --- a/testing +++ b/testing @@ -1 +1,2 @@ hello there +i'm a test :) ` require.Equal(t, []byte(want), rawDiff) } func TestIntSliceOrString(t *testing.T) { t.Run("any", func(t *testing.T) { opts := &ListMergeRequestsOptions{} opts.ApprovedByIDs = ApproverIDs(UserIDAny) q, err := query.Values(opts) assert.NoError(t, err) assert.Equal(t, "Any", q.Get("approved_by_ids")) }) t.Run("none", func(t *testing.T) { opts := &ListMergeRequestsOptions{} opts.ApprovedByIDs = ApproverIDs(UserIDNone) q, err := query.Values(opts) assert.NoError(t, err) assert.Equal(t, "None", q.Get("approved_by_ids")) }) t.Run("ids", func(t *testing.T) { opts := &ListMergeRequestsOptions{} opts.ApprovedByIDs = ApproverIDs([]int{1, 2, 3}) q, err := query.Values(opts) assert.NoError(t, err) includedIDs := q["approved_by_ids[]"] assert.Equal(t, []string{"1", "2", "3"}, includedIDs) }) } func TestAssigneeIDMarshalling(t *testing.T) { t.Run("any", func(t *testing.T) { opts := &ListMergeRequestsOptions{} opts.AssigneeID = AssigneeID(UserIDAny) q, err := query.Values(opts) assert.NoError(t, err) assert.Equal(t, "Any", q.Get("assignee_id")) js, _ := json.Marshal(opts) assert.Equal(t, `{"assignee_id":"Any"}`, string(js)) }) t.Run("none", func(t *testing.T) { opts := &ListMergeRequestsOptions{} opts.AssigneeID = AssigneeID(UserIDNone) q, err := query.Values(opts) assert.NoError(t, err) assert.Equal(t, "None", q.Get("assignee_id")) js, _ := json.Marshal(opts) assert.Equal(t, `{"assignee_id":"None"}`, string(js)) }) t.Run("id", func(t *testing.T) { opts := &ListMergeRequestsOptions{} opts.AssigneeID = AssigneeID(5) q, err := query.Values(opts) assert.NoError(t, err) assert.Equal(t, "5", q.Get("assignee_id")) js, _ := json.Marshal(opts) assert.Equal(t, `{"assignee_id":5}`, string(js)) }) } func TestCreateMergeRequestDependency(t *testing.T) { mux, client := setup(t) const project = "12345" const mergeRequest = 1 blockingMergeRequest := int(2) path := fmt.Sprintf("/%sprojects/%s/merge_requests/%d/blocks", apiVersionPath, project, mergeRequest) mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) w.WriteHeader(http.StatusCreated) fmt.Fprint(w, `[{"id": 1, "blocking_merge_request": {"iid":12}, "project_id": 7}]`) }) opts := CreateMergeRequestDependencyOptions{ BlockingMergeRequestID: &blockingMergeRequest, } dependencies, resp, err := client.MergeRequests.CreateMergeRequestDependency(project, mergeRequest, opts, nil) if err != nil { t.Errorf("MergeRequestDependencies.CreateMergeRequestDependency returned error: %v", err) } if resp != nil { if resp.StatusCode != http.StatusCreated { t.Errorf("MergeRequestDependencies.CreateMergeRequestDependency = %v, want %v", resp.StatusCode, http.StatusCreated) } } want := []MergeRequestDependency{ { ID: 1, BlockingMergeRequest: BlockingMergeRequest{ Iid: 12, }, ProjectID: 7, }, } if !reflect.DeepEqual(want, dependencies) { t.Fatalf("MergeRequestDependencies.GetMergeRequestDependencies returned %+v, want %+v", dependencies, want) } } func TestGetMergeRequestDependencies(t *testing.T) { mux, client := setup(t) const project = "12345" const mergeRequest = 1 path := fmt.Sprintf("/%sprojects/%s/merge_requests/%d/blocks", apiVersionPath, project, mergeRequest) mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) mustWriteHTTPResponse(t, w, "testdata/get_merge_request_dependencies.json") }) dependencies, resp, err := client.MergeRequests.GetMergeRequestDependencies(project, mergeRequest, nil) if err != nil { t.Errorf("MergeRequestDependencies.GetMergeRequestDependencies returned error: %v", err) } if resp == nil { t.Error("MergeRequestDependencies.GetMergeRequestDependencies did not return a response") } if len(dependencies) != 1 { t.Errorf("MergeRequestDependencies.GetMergeRequestDependencies returned %d dependencies, want 1", len(dependencies)) } } func TestDeleteMergeRequestDependency(t *testing.T) { mux, client := setup(t) const project = "12345" const mergeRequest = 1 const blockingMergeRequest = 2 path := fmt.Sprintf("/%sprojects/%s/merge_requests/%d/blocks/%d", apiVersionPath, project, mergeRequest, blockingMergeRequest) mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodDelete) w.WriteHeader(http.StatusNoContent) }) resp, err := client.MergeRequests.DeleteMergeRequestDependency(project, mergeRequest, blockingMergeRequest, nil) if err != nil { t.Errorf("MergeRequestDependencies.DeleteMergeRequestDependency returned error: %v", err) } if resp != nil { if resp.StatusCode != http.StatusNoContent { t.Errorf("MergeRequestDependencies.DeleteMergeRequestDependency = %v, want %v", resp.StatusCode, http.StatusNoContent) } } } golang-gitlab-gitlab-org-api-client-go-0.123.0/merge_trains.go000066400000000000000000000127721475761473200240760ustar00rootroot00000000000000package gitlab import ( "fmt" "net/http" "time" ) // MergeTrainsService handles communication with the merge trains related // methods of the GitLab API. // // GitLab API docs: https://docs.gitlab.com/ee/api/merge_trains.html type MergeTrainsService struct { client *Client } // MergeTrain represents a Gitlab merge train. // // GitLab API docs: https://docs.gitlab.com/ee/api/merge_trains.html type MergeTrain struct { ID int `json:"id"` MergeRequest *MergeTrainMergeRequest `json:"merge_request"` User *BasicUser `json:"user"` Pipeline *Pipeline `json:"pipeline"` CreatedAt *time.Time `json:"created_at"` UpdatedAt *time.Time `json:"updated_at"` TargetBranch string `json:"target_branch"` Status string `json:"status"` MergedAt *time.Time `json:"merged_at"` Duration int `json:"duration"` } // MergeTrainMergeRequest represents a Gitlab merge request inside merge train. // // GitLab API docs: https://docs.gitlab.com/ee/api/merge_trains.html type MergeTrainMergeRequest struct { ID int `json:"id"` IID int `json:"iid"` ProjectID int `json:"project_id"` Title string `json:"title"` Description string `json:"description"` State string `json:"state"` CreatedAt *time.Time `json:"created_at"` UpdatedAt *time.Time `json:"updated_at"` WebURL string `json:"web_url"` } // ListMergeTrainsOptions represents the available ListMergeTrain() options. // // Gitab API docs: // https://docs.gitlab.com/ee/api/merge_trains.html#list-merge-trains-for-a-project type ListMergeTrainsOptions struct { ListOptions Scope *string `url:"scope,omitempty" json:"scope,omitempty"` Sort *string `url:"sort,omitempty" json:"sort,omitempty"` } // ListProjectMergeTrains get a list of merge trains in a project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_trains.html#list-merge-trains-for-a-project func (s *MergeTrainsService) ListProjectMergeTrains(pid interface{}, opt *ListMergeTrainsOptions, options ...RequestOptionFunc) ([]*MergeTrain, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/merge_trains", PathEscape(project)) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } var mts []*MergeTrain resp, err := s.client.Do(req, &mts) if err != nil { return nil, resp, err } return mts, resp, nil } // ListMergeRequestInMergeTrain gets a list of merge requests added to a merge // train for the requested target branch. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_trains.html#list-merge-requests-in-a-merge-train func (s *MergeTrainsService) ListMergeRequestInMergeTrain(pid interface{}, targetBranch string, opts *ListMergeTrainsOptions, options ...RequestOptionFunc) ([]*MergeTrain, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/merge_trains/%s", PathEscape(project), targetBranch) req, err := s.client.NewRequest(http.MethodGet, u, opts, options) if err != nil { return nil, nil, err } var mts []*MergeTrain resp, err := s.client.Do(req, &mts) if err != nil { return nil, resp, err } return mts, resp, nil } // GetMergeRequestOnAMergeTrain Get merge train information for the requested // merge request. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_trains.html#get-the-status-of-a-merge-request-on-a-merge-train func (s *MergeTrainsService) GetMergeRequestOnAMergeTrain(pid interface{}, mergeRequest int, options ...RequestOptionFunc) (*MergeTrain, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/merge_trains/merge_requests/%d", PathEscape(project), mergeRequest) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } mt := new(MergeTrain) resp, err := s.client.Do(req, mt) if err != nil { return nil, resp, err } return mt, resp, nil } // AddMergeRequestToMergeTrainOptions represents the available // AddMergeRequestToMergeTrain() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_trains.html#add-a-merge-request-to-a-merge-train type AddMergeRequestToMergeTrainOptions struct { WhenPipelineSucceeds *bool `url:"when_pipeline_succeeds,omitempty" json:"when_pipeline_succeeds,omitempty"` SHA *string `url:"sha,omitempty" json:"sha,omitempty"` Squash *bool `url:"squash,omitempty" json:"squash,omitempty"` } // AddMergeRequestToMergeTrain Add a merge request to the merge train targeting // the merge request’s target branch. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_trains.html#add-a-merge-request-to-a-merge-train func (s *MergeTrainsService) AddMergeRequestToMergeTrain(pid interface{}, mergeRequest int, opts *AddMergeRequestToMergeTrainOptions, options ...RequestOptionFunc) ([]*MergeTrain, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/merge_trains/merge_requests/%d", PathEscape(project), mergeRequest) req, err := s.client.NewRequest(http.MethodPost, u, opts, options) if err != nil { return nil, nil, err } var mts []*MergeTrain resp, err := s.client.Do(req, &mts) if err != nil { return nil, resp, err } return mts, resp, nil } golang-gitlab-gitlab-org-api-client-go-0.123.0/merge_trains_test.go000066400000000000000000000226411475761473200251310ustar00rootroot00000000000000package gitlab import ( "net/http" "reflect" "testing" "time" ) func TestListProjectMergeTrains(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/merge_trains", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) mustWriteHTTPResponse(t, w, "testdata/list_merge_trains_in_project.json") }) opts := &ListMergeTrainsOptions{} mergeTrains, _, err := client.MergeTrains.ListProjectMergeTrains(1, opts) if err != nil { t.Errorf("MergeTrains.ListProjectMergeTrains returned error: %v", err) } mergeRequestCreatedAt := time.Date(2020, 2, 6, 8, 39, 14, 883000000, time.UTC) mergeRequestUpdatedAt := time.Date(2020, 0o2, 6, 8, 40, 57, 38000000, time.UTC) pipelineCreatedAt := time.Date(2020, 2, 6, 8, 40, 42, 410000000, time.UTC) pipelineUpdatedAt := time.Date(2020, 2, 6, 8, 40, 46, 912000000, time.UTC) mergeTrainCreatedAt := time.Date(2020, 2, 6, 8, 39, 47, 217000000, time.UTC) mergeTrainUpdatedAt := time.Date(2020, 2, 6, 8, 40, 57, 720000000, time.UTC) mergeTrainMergedAt := time.Date(2020, 2, 6, 8, 40, 57, 719000000, time.UTC) want := []*MergeTrain{ { ID: 110, MergeRequest: &MergeTrainMergeRequest{ ID: 126, IID: 59, ProjectID: 20, Title: "Test MR 1580978354", Description: "", State: "merged", CreatedAt: &mergeRequestCreatedAt, UpdatedAt: &mergeRequestUpdatedAt, WebURL: "http://local.gitlab.test:8181/root/merge-train-race-condition/-/merge_requests/59", }, User: &BasicUser{ ID: 1, Name: "Administrator", Username: "root", State: "active", AvatarURL: "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon", WebURL: "http://local.gitlab.test:8181/root", }, Pipeline: &Pipeline{ ID: 246, SHA: "bcc17a8ffd51be1afe45605e714085df28b80b13", Ref: "refs/merge-requests/59/train", Status: "success", CreatedAt: &pipelineCreatedAt, UpdatedAt: &pipelineUpdatedAt, WebURL: "http://local.gitlab.test:8181/root/merge-train-race-condition/pipelines/246", }, CreatedAt: &mergeTrainCreatedAt, UpdatedAt: &mergeTrainUpdatedAt, TargetBranch: "feature-1580973432", Status: "merged", MergedAt: &mergeTrainMergedAt, Duration: 70, }, } if !reflect.DeepEqual(want, mergeTrains) { t.Errorf("MergeTrains.ListProjectMergeTrains returned %+v, want %+v", mergeTrains, want) } } func TestListMergeRequestInMergeTrain(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/597/merge_trains/main", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) mustWriteHTTPResponse(t, w, "testdata/list_merge_requests_in_merge_train.json") }) opts := &ListMergeTrainsOptions{} mergeTrains, _, err := client.MergeTrains.ListMergeRequestInMergeTrain(597, "main", opts) if err != nil { t.Errorf("MergeTrains.ListMergeRequestInMergeTrain returned error: %v", err) } mergeRequestCreatedAt := time.Date(2022, 10, 31, 19, 6, 5, 725000000, time.UTC) mergeRequestUpdatedAt := time.Date(2022, 10, 31, 19, 6, 5, 725000000, time.UTC) pipelineCreatedAt := time.Date(2022, 10, 31, 19, 0o6, 0o6, 231000000, time.UTC) pipelineUpdatedAt := time.Date(2022, 10, 31, 19, 0o6, 0o6, 231000000, time.UTC) mergeTrainCreatedAt := time.Date(2022, 10, 31, 19, 0o6, 0o6, 237000000, time.UTC) mergeTrainUpdatedAt := time.Date(2022, 10, 31, 19, 0o6, 0o6, 237000000, time.UTC) want := []*MergeTrain{ { ID: 267, MergeRequest: &MergeTrainMergeRequest{ ID: 273, IID: 1, ProjectID: 597, Title: "My title 9", Description: "", State: "opened", CreatedAt: &mergeRequestCreatedAt, UpdatedAt: &mergeRequestUpdatedAt, WebURL: "http://localhost/namespace18/project21/-/merge_requests/1", }, User: &BasicUser{ ID: 933, Username: "user12", Name: "Sidney Jones31", State: "active", AvatarURL: "https://www.gravatar.com/avatar/6c8365de387cb3db10ecc7b1880203c4?s=80\u0026d=identicon", WebURL: "http://localhost/user12", }, Pipeline: &Pipeline{ ID: 273, IID: 1, ProjectID: 598, SHA: "b83d6e391c22777fca1ed3012fce84f633d7fed0", Ref: "main", Status: "pending", Source: "push", CreatedAt: &pipelineCreatedAt, UpdatedAt: &pipelineUpdatedAt, WebURL: "http://localhost/namespace19/project22/-/pipelines/273", }, CreatedAt: &mergeTrainCreatedAt, UpdatedAt: &mergeTrainUpdatedAt, TargetBranch: "main", Status: "idle", MergedAt: nil, Duration: 0, }, } if !reflect.DeepEqual(want, mergeTrains) { t.Errorf("MergeTrains.ListMergeRequestInMergeTrain returned %+v, want %+v", mergeTrains, want) } } func TestGetMergeRequestOnAMergeTrain(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/597/merge_trains/merge_requests/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) mustWriteHTTPResponse(t, w, "testdata/get_merge_request_in_merge_train.json") }) mergeTrain, _, err := client.MergeTrains.GetMergeRequestOnAMergeTrain(597, 1) if err != nil { t.Errorf("MergeTrains.GetMergeRequestOnAMergeTrain returned error: %v", err) } mergeRequestCreatedAt := time.Date(2022, 10, 31, 19, 6, 5, 725000000, time.UTC) mergeRequestUpdatedAt := time.Date(2022, 10, 31, 19, 6, 5, 725000000, time.UTC) pipelineCreatedAt := time.Date(2022, 10, 31, 19, 0o6, 0o6, 231000000, time.UTC) pipelineUpdatedAt := time.Date(2022, 10, 31, 19, 0o6, 0o6, 231000000, time.UTC) mergeTrainCreatedAt := time.Date(2022, 10, 31, 19, 0o6, 0o6, 237000000, time.UTC) mergeTrainUpdatedAt := time.Date(2022, 10, 31, 19, 0o6, 0o6, 237000000, time.UTC) want := &MergeTrain{ ID: 267, MergeRequest: &MergeTrainMergeRequest{ ID: 273, IID: 1, ProjectID: 597, Title: "My title 9", Description: "", State: "opened", CreatedAt: &mergeRequestCreatedAt, UpdatedAt: &mergeRequestUpdatedAt, WebURL: "http://localhost/namespace18/project21/-/merge_requests/1", }, User: &BasicUser{ ID: 933, Username: "user12", Name: "Sidney Jones31", State: "active", AvatarURL: "https://www.gravatar.com/avatar/6c8365de387cb3db10ecc7b1880203c4?s=80\u0026d=identicon", WebURL: "http://localhost/user12", }, Pipeline: &Pipeline{ ID: 273, IID: 1, ProjectID: 598, SHA: "b83d6e391c22777fca1ed3012fce84f633d7fed0", Ref: "main", Status: "pending", Source: "push", CreatedAt: &pipelineCreatedAt, UpdatedAt: &pipelineUpdatedAt, WebURL: "http://localhost/namespace19/project22/-/pipelines/273", }, CreatedAt: &mergeTrainCreatedAt, UpdatedAt: &mergeTrainUpdatedAt, TargetBranch: "main", Status: "idle", MergedAt: nil, Duration: 0, } if !reflect.DeepEqual(want, mergeTrain) { t.Errorf("MergeTrains.GetMergeRequestOnAMergeTrain returned %+v, want %+v", mergeTrain, want) } } func TestAddMergeRequestToMergeTrain(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/597/merge_trains/merge_requests/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) mustWriteHTTPResponse(t, w, "testdata/add_merge_request_in_merge_train.json") }) opt := &AddMergeRequestToMergeTrainOptions{WhenPipelineSucceeds: Ptr(true), Squash: Ptr(true)} mergeTrains, _, err := client.MergeTrains.AddMergeRequestToMergeTrain(597, 1, opt) if err != nil { t.Errorf("MergeTrains.AddMergeRequestToMergeTrain returned error: %v", err) } mergeRequestCreatedAt := time.Date(2022, 10, 31, 19, 6, 5, 725000000, time.UTC) mergeRequestUpdatedAt := time.Date(2022, 10, 31, 19, 6, 5, 725000000, time.UTC) pipelineCreatedAt := time.Date(2022, 10, 31, 19, 0o6, 0o6, 231000000, time.UTC) pipelineUpdatedAt := time.Date(2022, 10, 31, 19, 0o6, 0o6, 231000000, time.UTC) mergeTrainCreatedAt := time.Date(2022, 10, 31, 19, 0o6, 0o6, 237000000, time.UTC) mergeTrainUpdatedAt := time.Date(2022, 10, 31, 19, 0o6, 0o6, 237000000, time.UTC) want := []*MergeTrain{ { ID: 267, MergeRequest: &MergeTrainMergeRequest{ ID: 273, IID: 1, ProjectID: 597, Title: "My title 9", Description: "", State: "opened", CreatedAt: &mergeRequestCreatedAt, UpdatedAt: &mergeRequestUpdatedAt, WebURL: "http://localhost/namespace18/project21/-/merge_requests/1", }, User: &BasicUser{ ID: 933, Username: "user12", Name: "Sidney Jones31", State: "active", AvatarURL: "https://www.gravatar.com/avatar/6c8365de387cb3db10ecc7b1880203c4?s=80\u0026d=identicon", WebURL: "http://localhost/user12", }, Pipeline: &Pipeline{ ID: 273, IID: 1, ProjectID: 598, SHA: "b83d6e391c22777fca1ed3012fce84f633d7fed0", Ref: "main", Status: "pending", Source: "push", CreatedAt: &pipelineCreatedAt, UpdatedAt: &pipelineUpdatedAt, WebURL: "http://localhost/namespace19/project22/-/pipelines/273", }, CreatedAt: &mergeTrainCreatedAt, UpdatedAt: &mergeTrainUpdatedAt, TargetBranch: "main", Status: "idle", MergedAt: nil, Duration: 0, }, } if !reflect.DeepEqual(want, mergeTrains) { t.Errorf("MergeTrains.AddMergeRequestToMergeTrain returned %+v, want %+v", mergeTrains, want) } } golang-gitlab-gitlab-org-api-client-go-0.123.0/metadata.go000066400000000000000000000035671475761473200232010ustar00rootroot00000000000000// // Copyright 2022, Timo Furrer // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import "net/http" // MetadataService handles communication with the GitLab server instance to // retrieve its metadata information via the GitLab API. // // GitLab API docs: https://docs.gitlab.com/ee/api/metadata.html type MetadataService struct { client *Client } // Metadata represents a GitLab instance version. // // GitLab API docs: https://docs.gitlab.com/ee/api/metadata.html type Metadata struct { Version string `json:"version"` Revision string `json:"revision"` KAS struct { Enabled bool `json:"enabled"` ExternalURL string `json:"externalUrl"` ExternalK8SProxyURL string `json:"externalK8sProxyUrl"` Version string `json:"version"` } `json:"kas"` Enterprise bool `json:"enterprise"` } func (s Metadata) String() string { return Stringify(s) } // GetMetadata gets a GitLab server instance meteadata. // // GitLab API docs: https://docs.gitlab.com/ee/api/metadata.html func (s *MetadataService) GetMetadata(options ...RequestOptionFunc) (*Metadata, *Response, error) { req, err := s.client.NewRequest(http.MethodGet, "metadata", nil, options) if err != nil { return nil, nil, err } v := new(Metadata) resp, err := s.client.Do(req, v) if err != nil { return nil, resp, err } return v, resp, nil } golang-gitlab-gitlab-org-api-client-go-0.123.0/metadata_test.go000066400000000000000000000037001475761473200242250ustar00rootroot00000000000000// // Copyright 2022, Timo Furrer // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "fmt" "net/http" "reflect" "testing" ) func TestGetMetadata(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/metadata", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, `{ "version": "15.6.0-pre", "revision": "016e8d8bdc3", "enterprise": true, "kas": { "enabled": true, "externalUrl": "wss://kas.gitlab.example.com", "externalK8sProxyUrl": "https://kas.gitlab.example.com/k8s-proxy", "version": "15.6.0-rc2" } }`) }) version, _, err := client.Metadata.GetMetadata() if err != nil { t.Errorf("Metadata.GetMetadata returned error: %v", err) } want := &Metadata{ Version: "15.6.0-pre", Revision: "016e8d8bdc3", KAS: struct { Enabled bool `json:"enabled"` ExternalURL string `json:"externalUrl"` ExternalK8SProxyURL string `json:"externalK8sProxyUrl"` Version string `json:"version"` }{ Enabled: true, ExternalURL: "wss://kas.gitlab.example.com", ExternalK8SProxyURL: "https://kas.gitlab.example.com/k8s-proxy", Version: "15.6.0-rc2", }, Enterprise: true, } if !reflect.DeepEqual(want, version) { t.Errorf("Metadata.GetMetadata returned %+v, want %+v", version, want) } } golang-gitlab-gitlab-org-api-client-go-0.123.0/milestones.go000066400000000000000000000211301475761473200235650ustar00rootroot00000000000000// // Copyright 2021, Sander van Harmelen // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "fmt" "net/http" "time" ) // MilestonesService handles communication with the milestone related methods // of the GitLab API. // // GitLab API docs: https://docs.gitlab.com/ee/api/milestones.html type MilestonesService struct { client *Client } // Milestone represents a GitLab milestone. // // GitLab API docs: https://docs.gitlab.com/ee/api/milestones.html type Milestone struct { ID int `json:"id"` IID int `json:"iid"` GroupID int `json:"group_id"` ProjectID int `json:"project_id"` Title string `json:"title"` Description string `json:"description"` StartDate *ISOTime `json:"start_date"` DueDate *ISOTime `json:"due_date"` State string `json:"state"` WebURL string `json:"web_url"` UpdatedAt *time.Time `json:"updated_at"` CreatedAt *time.Time `json:"created_at"` Expired *bool `json:"expired"` } func (m Milestone) String() string { return Stringify(m) } // ListMilestonesOptions represents the available ListMilestones() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/milestones.html#list-project-milestones type ListMilestonesOptions struct { ListOptions IIDs *[]int `url:"iids[],omitempty" json:"iids,omitempty"` Title *string `url:"title,omitempty" json:"title,omitempty"` State *string `url:"state,omitempty" json:"state,omitempty"` Search *string `url:"search,omitempty" json:"search,omitempty"` IncludeParentMilestones *bool `url:"include_parent_milestones,omitempty" json:"include_parent_milestones,omitempty"` } // ListMilestones returns a list of project milestones. // // GitLab API docs: // https://docs.gitlab.com/ee/api/milestones.html#list-project-milestones func (s *MilestonesService) ListMilestones(pid interface{}, opt *ListMilestonesOptions, options ...RequestOptionFunc) ([]*Milestone, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/milestones", PathEscape(project)) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } var m []*Milestone resp, err := s.client.Do(req, &m) if err != nil { return nil, resp, err } return m, resp, nil } // GetMilestone gets a single project milestone. // // GitLab API docs: // https://docs.gitlab.com/ee/api/milestones.html#get-single-milestone func (s *MilestonesService) GetMilestone(pid interface{}, milestone int, options ...RequestOptionFunc) (*Milestone, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/milestones/%d", PathEscape(project), milestone) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } m := new(Milestone) resp, err := s.client.Do(req, m) if err != nil { return nil, resp, err } return m, resp, nil } // CreateMilestoneOptions represents the available CreateMilestone() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/milestones.html#create-new-milestone type CreateMilestoneOptions struct { Title *string `url:"title,omitempty" json:"title,omitempty"` Description *string `url:"description,omitempty" json:"description,omitempty"` StartDate *ISOTime `url:"start_date,omitempty" json:"start_date,omitempty"` DueDate *ISOTime `url:"due_date,omitempty" json:"due_date,omitempty"` } // CreateMilestone creates a new project milestone. // // GitLab API docs: // https://docs.gitlab.com/ee/api/milestones.html#create-new-milestone func (s *MilestonesService) CreateMilestone(pid interface{}, opt *CreateMilestoneOptions, options ...RequestOptionFunc) (*Milestone, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/milestones", PathEscape(project)) req, err := s.client.NewRequest(http.MethodPost, u, opt, options) if err != nil { return nil, nil, err } m := new(Milestone) resp, err := s.client.Do(req, m) if err != nil { return nil, resp, err } return m, resp, nil } // UpdateMilestoneOptions represents the available UpdateMilestone() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/milestones.html#edit-milestone type UpdateMilestoneOptions struct { Title *string `url:"title,omitempty" json:"title,omitempty"` Description *string `url:"description,omitempty" json:"description,omitempty"` StartDate *ISOTime `url:"start_date,omitempty" json:"start_date,omitempty"` DueDate *ISOTime `url:"due_date,omitempty" json:"due_date,omitempty"` StateEvent *string `url:"state_event,omitempty" json:"state_event,omitempty"` } // UpdateMilestone updates an existing project milestone. // // GitLab API docs: // https://docs.gitlab.com/ee/api/milestones.html#edit-milestone func (s *MilestonesService) UpdateMilestone(pid interface{}, milestone int, opt *UpdateMilestoneOptions, options ...RequestOptionFunc) (*Milestone, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/milestones/%d", PathEscape(project), milestone) req, err := s.client.NewRequest(http.MethodPut, u, opt, options) if err != nil { return nil, nil, err } m := new(Milestone) resp, err := s.client.Do(req, m) if err != nil { return nil, resp, err } return m, resp, nil } // DeleteMilestone deletes a specified project milestone. // // GitLab API docs: // https://docs.gitlab.com/ee/api/milestones.html#delete-project-milestone func (s *MilestonesService) DeleteMilestone(pid interface{}, milestone int, options ...RequestOptionFunc) (*Response, error) { project, err := parseID(pid) if err != nil { return nil, err } u := fmt.Sprintf("projects/%s/milestones/%d", PathEscape(project), milestone) req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } // GetMilestoneIssuesOptions represents the available GetMilestoneIssues() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/milestones.html#get-all-issues-assigned-to-a-single-milestone type GetMilestoneIssuesOptions ListOptions // GetMilestoneIssues gets all issues assigned to a single project milestone. // // GitLab API docs: // https://docs.gitlab.com/ee/api/milestones.html#get-all-issues-assigned-to-a-single-milestone func (s *MilestonesService) GetMilestoneIssues(pid interface{}, milestone int, opt *GetMilestoneIssuesOptions, options ...RequestOptionFunc) ([]*Issue, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/milestones/%d/issues", PathEscape(project), milestone) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } var i []*Issue resp, err := s.client.Do(req, &i) if err != nil { return nil, resp, err } return i, resp, nil } // GetMilestoneMergeRequestsOptions represents the available // GetMilestoneMergeRequests() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/milestones.html#get-all-merge-requests-assigned-to-a-single-milestone type GetMilestoneMergeRequestsOptions ListOptions // GetMilestoneMergeRequests gets all merge requests assigned to a single // project milestone. // // GitLab API docs: // https://docs.gitlab.com/ee/api/milestones.html#get-all-merge-requests-assigned-to-a-single-milestone func (s *MilestonesService) GetMilestoneMergeRequests(pid interface{}, milestone int, opt *GetMilestoneMergeRequestsOptions, options ...RequestOptionFunc) ([]*MergeRequest, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/milestones/%d/merge_requests", PathEscape(project), milestone) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } var mr []*MergeRequest resp, err := s.client.Do(req, &mr) if err != nil { return nil, resp, err } return mr, resp, nil } golang-gitlab-gitlab-org-api-client-go-0.123.0/milestones_test.go000066400000000000000000000371031475761473200246330ustar00rootroot00000000000000package gitlab import ( "fmt" "net/http" "testing" "github.com/stretchr/testify/require" ) func TestMilestonesService_ListMilestones(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/5/milestones", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprintf(w, ` [ { "id": 12, "iid": 3, "project_id": 16, "title": "10.0", "description": "Version", "state": "active", "expired": false } ] `) }) want := []*Milestone{{ ID: 12, IID: 3, ProjectID: 16, Title: "10.0", Description: "Version", State: "active", WebURL: "", Expired: Ptr(false), }} ms, resp, err := client.Milestones.ListMilestones(5, nil) require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, want, ms) ms, resp, err = client.Milestones.ListMilestones(5.01, nil) require.EqualError(t, err, "invalid ID type 5.01, the ID must be an int or a string") require.Nil(t, resp) require.Nil(t, ms) ms, resp, err = client.Milestones.ListMilestones(5, nil, errorOption) require.EqualError(t, err, "RequestOptionFunc returns an error") require.Nil(t, resp) require.Nil(t, ms) ms, resp, err = client.Milestones.ListMilestones(3, nil) require.Error(t, err) require.Nil(t, ms) require.Equal(t, http.StatusNotFound, resp.StatusCode) } func TestMilestonesService_GetMilestone(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/5/milestones/12", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprintf(w, ` { "id": 12, "iid": 3, "project_id": 16, "title": "10.0", "description": "Version", "state": "active", "expired": false } `) }) want := &Milestone{ ID: 12, IID: 3, ProjectID: 16, Title: "10.0", Description: "Version", State: "active", WebURL: "", Expired: Ptr(false), } m, resp, err := client.Milestones.GetMilestone(5, 12, nil) require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, want, m) m, resp, err = client.Milestones.GetMilestone(5.01, 12, nil) require.EqualError(t, err, "invalid ID type 5.01, the ID must be an int or a string") require.Nil(t, resp) require.Nil(t, m) m, resp, err = client.Milestones.GetMilestone(5, 12, nil, errorOption) require.EqualError(t, err, "RequestOptionFunc returns an error") require.Nil(t, resp) require.Nil(t, m) m, resp, err = client.Milestones.GetMilestone(3, 12, nil) require.Error(t, err) require.Nil(t, m) require.Equal(t, http.StatusNotFound, resp.StatusCode) } func TestMilestonesService_CreateMilestone(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/5/milestones", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) fmt.Fprintf(w, ` { "id": 12, "iid": 3, "project_id": 16, "title": "10.0", "description": "Version", "state": "active", "expired": false } `) }) want := &Milestone{ ID: 12, IID: 3, ProjectID: 16, Title: "10.0", Description: "Version", State: "active", WebURL: "", Expired: Ptr(false), } m, resp, err := client.Milestones.CreateMilestone(5, nil) require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, want, m) m, resp, err = client.Milestones.CreateMilestone(5.01, nil) require.EqualError(t, err, "invalid ID type 5.01, the ID must be an int or a string") require.Nil(t, resp) require.Nil(t, m) m, resp, err = client.Milestones.CreateMilestone(5, nil, errorOption) require.EqualError(t, err, "RequestOptionFunc returns an error") require.Nil(t, resp) require.Nil(t, m) m, resp, err = client.Milestones.CreateMilestone(3, nil) require.Error(t, err) require.Nil(t, m) require.Equal(t, http.StatusNotFound, resp.StatusCode) } func TestMilestonesService_UpdateMilestone(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/5/milestones/12", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPut) fmt.Fprintf(w, ` { "id": 12, "iid": 3, "project_id": 16, "title": "10.0", "description": "Version", "state": "active", "expired": false } `) }) want := &Milestone{ ID: 12, IID: 3, ProjectID: 16, Title: "10.0", Description: "Version", State: "active", WebURL: "", Expired: Ptr(false), } m, resp, err := client.Milestones.UpdateMilestone(5, 12, nil) require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, want, m) m, resp, err = client.Milestones.UpdateMilestone(5.01, 12, nil) require.EqualError(t, err, "invalid ID type 5.01, the ID must be an int or a string") require.Nil(t, resp) require.Nil(t, m) m, resp, err = client.Milestones.UpdateMilestone(5, 12, nil, errorOption) require.EqualError(t, err, "RequestOptionFunc returns an error") require.Nil(t, resp) require.Nil(t, m) m, resp, err = client.Milestones.UpdateMilestone(3, 12, nil) require.Error(t, err) require.Nil(t, m) require.Equal(t, http.StatusNotFound, resp.StatusCode) } func TestMilestonesService_DeleteMilestone(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/5/milestones/12", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodDelete) }) resp, err := client.Milestones.DeleteMilestone(5, 12, nil) require.NoError(t, err) require.NotNil(t, resp) resp, err = client.Milestones.DeleteMilestone(5.01, 12, nil) require.EqualError(t, err, "invalid ID type 5.01, the ID must be an int or a string") require.Nil(t, resp) resp, err = client.Milestones.DeleteMilestone(5, 12, nil, errorOption) require.EqualError(t, err, "RequestOptionFunc returns an error") require.Nil(t, resp) resp, err = client.Milestones.DeleteMilestone(3, 12, nil) require.Error(t, err) require.Equal(t, http.StatusNotFound, resp.StatusCode) } func TestMilestonesService_GetMilestoneIssues(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/5/milestones/12/issues", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprintf(w, ` [ { "project_id" : 5, "milestone" : { "due_date" : null, "project_id" : 5, "state" : "closed", "description" : "Rerum est voluptatem provident consequuntur molestias similique ipsum dolor.", "iid" : 3, "id" : 11, "title" : "v3.0" }, "author" : { "state" : "active", "web_url" : "https://gitlab.example.com/root", "avatar_url" : null, "username" : "root", "id" : 1, "name" : "Administrator" }, "description" : "Omnis vero earum sunt corporis dolor et placeat.", "state" : "closed", "iid" : 1, "assignees" : [{ "avatar_url" : null, "web_url" : "https://gitlab.example.com/venky333", "state" : "active", "username" : "venky333", "id" : 9, "name" : "Venkatesh Thalluri" }], "assignee" : { "avatar_url" : null, "web_url" : "https://gitlab.example.com/venky333", "state" : "active", "username" : "venky333", "id" : 9, "name" : "Venkatesh Thalluri" }, "id" : 41 } ] `) }) want := []*Issue{{ ID: 41, IID: 1, ExternalID: "", State: "closed", Description: "Omnis vero earum sunt corporis dolor et placeat.", Author: &IssueAuthor{ ID: 1, State: "active", WebURL: "https://gitlab.example.com/root", Name: "Administrator", AvatarURL: "", Username: "root", }, Milestone: &Milestone{ ID: 11, IID: 3, ProjectID: 5, Title: "v3.0", Description: "Rerum est voluptatem provident consequuntur molestias similique ipsum dolor.", StartDate: nil, DueDate: nil, State: "closed", WebURL: "", UpdatedAt: nil, CreatedAt: nil, Expired: nil, }, ProjectID: 5, Assignees: []*IssueAssignee{{ ID: 9, State: "active", WebURL: "https://gitlab.example.com/venky333", Name: "Venkatesh Thalluri", AvatarURL: "", Username: "venky333", }}, Assignee: &IssueAssignee{ ID: 9, State: "active", WebURL: "https://gitlab.example.com/venky333", Name: "Venkatesh Thalluri", AvatarURL: "", Username: "venky333", }, }} is, resp, err := client.Milestones.GetMilestoneIssues(5, 12, nil) require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, want, is) is, resp, err = client.Milestones.GetMilestoneIssues(5.01, 12, nil) require.EqualError(t, err, "invalid ID type 5.01, the ID must be an int or a string") require.Nil(t, resp) require.Nil(t, is) is, resp, err = client.Milestones.GetMilestoneIssues(5, 12, nil, errorOption) require.EqualError(t, err, "RequestOptionFunc returns an error") require.Nil(t, resp) require.Nil(t, is) is, resp, err = client.Milestones.GetMilestoneIssues(3, 12, nil) require.Error(t, err) require.Nil(t, is) require.Equal(t, http.StatusNotFound, resp.StatusCode) } func TestMilestonesService_GetMilestoneMergeRequests(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/3/milestones/12/merge_requests", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprintf(w, ` [ { "id": 1, "iid": 1, "project_id": 3, "title": "test1", "description": "fixed login page css paddings", "state": "merged", "merged_by": { "id": 87854, "name": "Douwe Maan", "username": "DouweM", "state": "active", "avatar_url": "https://gitlab.example.com/uploads/-/system/user/avatar/87854/avatar.png", "web_url": "https://gitlab.com/DouweM" }, "closed_by": null, "closed_at": null, "target_branch": "master", "source_branch": "test1", "upvotes": 0, "downvotes": 0, "author": { "id": 1, "name": "Administrator", "username": "admin", "state": "active", "avatar_url": null, "web_url" : "https://gitlab.example.com/admin" }, "assignee": { "id": 1, "name": "Administrator", "username": "admin", "state": "active", "avatar_url": null, "web_url" : "https://gitlab.example.com/admin" }, "assignees": [{ "name": "Venkatesh Thalluri", "username": "venkatesh.thalluri", "id": 12, "state": "active", "avatar_url": "http://www.gravatar.com/avatar/46f6f7dc858ada7be1853f7fb96e81da?s=80&d=identicon", "web_url": "https://gitlab.example.com/axel.block" }], "reviewers": [{ "id": 2, "name": "Sam Bauch", "username": "kenyatta_oconnell", "state": "active", "avatar_url": "https://www.gravatar.com/avatar/956c92487c6f6f7616b536927e22c9a0?s=80&d=identicon", "web_url": "http://gitlab.example.com//kenyatta_oconnell" }], "source_project_id": 2, "target_project_id": 3, "draft": false, "work_in_progress": false, "milestone": { "id": 5, "iid": 1, "project_id": 3, "title": "v2.0", "description": "Assumenda aut placeat expedita exercitationem labore sunt enim earum.", "state": "closed", "web_url": "https://gitlab.example.com/my-group/my-project/milestones/1" }, "merge_when_pipeline_succeeds": true, "detailed_merge_status": "mergeable", "sha": "8888888888888888888888888888888888888888", "merge_commit_sha": null, "squash_commit_sha": null, "user_notes_count": 1, "discussion_locked": null, "should_remove_source_branch": true, "force_remove_source_branch": false, "allow_collaboration": false, "allow_maintainer_to_push": false, "web_url": "http://gitlab.example.com/my-group/my-project/merge_requests/1", "references": { "short": "!1", "relative": "my-group/my-project!1", "full": "my-group/my-project!1" }, "squash": false, "task_completion_status":{ "count":0, "completed_count":0 } } ] `) }) want := []*MergeRequest{{ ID: 1, IID: 1, TargetBranch: "master", SourceBranch: "test1", ProjectID: 3, Title: "test1", State: "merged", Upvotes: 0, Downvotes: 0, Author: &BasicUser{ ID: 1, Username: "admin", Name: "Administrator", State: "active", CreatedAt: nil, AvatarURL: "", WebURL: "https://gitlab.example.com/admin", }, Assignee: &BasicUser{ ID: 1, Username: "admin", Name: "Administrator", State: "active", AvatarURL: "", WebURL: "https://gitlab.example.com/admin", }, Assignees: []*BasicUser{{ ID: 12, Username: "venkatesh.thalluri", Name: "Venkatesh Thalluri", State: "active", AvatarURL: "http://www.gravatar.com/avatar/46f6f7dc858ada7be1853f7fb96e81da?s=80&d=identicon", WebURL: "https://gitlab.example.com/axel.block", }}, Reviewers: []*BasicUser{{ ID: 2, Username: "kenyatta_oconnell", Name: "Sam Bauch", State: "active", AvatarURL: "https://www.gravatar.com/avatar/956c92487c6f6f7616b536927e22c9a0?s=80&d=identicon", WebURL: "http://gitlab.example.com//kenyatta_oconnell", }}, SourceProjectID: 2, TargetProjectID: 3, Description: "fixed login page css paddings", WorkInProgress: false, Milestone: &Milestone{ ID: 5, IID: 1, ProjectID: 3, Title: "v2.0", Description: "Assumenda aut placeat expedita exercitationem labore sunt enim earum.", State: "closed", WebURL: "https://gitlab.example.com/my-group/my-project/milestones/1", }, MergeWhenPipelineSucceeds: true, DetailedMergeStatus: "mergeable", MergeError: "", MergedBy: &BasicUser{ ID: 87854, Username: "DouweM", Name: "Douwe Maan", State: "active", AvatarURL: "https://gitlab.example.com/uploads/-/system/user/avatar/87854/avatar.png", WebURL: "https://gitlab.com/DouweM", }, Subscribed: false, SHA: "8888888888888888888888888888888888888888", MergeCommitSHA: "", SquashCommitSHA: "", UserNotesCount: 1, ChangesCount: "", ShouldRemoveSourceBranch: true, ForceRemoveSourceBranch: false, AllowCollaboration: false, WebURL: "http://gitlab.example.com/my-group/my-project/merge_requests/1", References: &IssueReferences{ Short: "!1", Relative: "my-group/my-project!1", Full: "my-group/my-project!1", }, DiscussionLocked: false, Squash: false, DivergedCommitsCount: 0, RebaseInProgress: false, ApprovalsBeforeMerge: 0, Reference: "", FirstContribution: false, TaskCompletionStatus: &TasksCompletionStatus{ Count: 0, CompletedCount: 0, }, HasConflicts: false, BlockingDiscussionsResolved: false, Overflow: false, }} mrs, resp, err := client.Milestones.GetMilestoneMergeRequests(3, 12, nil) require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, want, mrs) mrs, resp, err = client.Milestones.GetMilestoneMergeRequests(3.01, 12, nil) require.EqualError(t, err, "invalid ID type 3.01, the ID must be an int or a string") require.Nil(t, resp) require.Nil(t, mrs) mrs, resp, err = client.Milestones.GetMilestoneMergeRequests(3, 12, nil, errorOption) require.EqualError(t, err, "RequestOptionFunc returns an error") require.Nil(t, resp) require.Nil(t, mrs) mrs, resp, err = client.Milestones.GetMilestoneMergeRequests(5, 12, nil) require.Error(t, err) require.Nil(t, mrs) require.Equal(t, http.StatusNotFound, resp.StatusCode) } golang-gitlab-gitlab-org-api-client-go-0.123.0/namespaces.go000066400000000000000000000125211475761473200235260ustar00rootroot00000000000000// // Copyright 2021, Sander van Harmelen // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "fmt" "net/http" ) // NamespacesService handles communication with the namespace related methods // of the GitLab API. // // GitLab API docs: https://docs.gitlab.com/ee/api/namespaces.html type NamespacesService struct { client *Client } // Namespace represents a GitLab namespace. // // GitLab API docs: https://docs.gitlab.com/ee/api/namespaces.html type Namespace struct { ID int `json:"id"` Name string `json:"name"` Path string `json:"path"` Kind string `json:"kind"` FullPath string `json:"full_path"` ParentID int `json:"parent_id"` AvatarURL *string `json:"avatar_url"` WebURL string `json:"web_url"` MembersCountWithDescendants int `json:"members_count_with_descendants"` BillableMembersCount int `json:"billable_members_count"` Plan string `json:"plan"` TrialEndsOn *ISOTime `json:"trial_ends_on"` Trial bool `json:"trial"` MaxSeatsUsed *int `json:"max_seats_used"` SeatsInUse *int `json:"seats_in_use"` } func (n Namespace) String() string { return Stringify(n) } // ListNamespacesOptions represents the available ListNamespaces() options. // // GitLab API docs: https://docs.gitlab.com/ee/api/namespaces.html#list-namespaces type ListNamespacesOptions struct { ListOptions Search *string `url:"search,omitempty" json:"search,omitempty"` OwnedOnly *bool `url:"owned_only,omitempty" json:"owned_only,omitempty"` TopLevelOnly *bool `url:"top_level_only,omitempty" json:"top_level_only,omitempty"` } // ListNamespaces gets a list of projects accessible by the authenticated user. // // GitLab API docs: https://docs.gitlab.com/ee/api/namespaces.html#list-namespaces func (s *NamespacesService) ListNamespaces(opt *ListNamespacesOptions, options ...RequestOptionFunc) ([]*Namespace, *Response, error) { req, err := s.client.NewRequest(http.MethodGet, "namespaces", opt, options) if err != nil { return nil, nil, err } var n []*Namespace resp, err := s.client.Do(req, &n) if err != nil { return nil, resp, err } return n, resp, nil } // SearchNamespace gets all namespaces that match your string in their name // or path. // // GitLab API docs: // https://docs.gitlab.com/ee/api/namespaces.html#list-namespaces func (s *NamespacesService) SearchNamespace(query string, options ...RequestOptionFunc) ([]*Namespace, *Response, error) { var q struct { Search string `url:"search,omitempty" json:"search,omitempty"` } q.Search = query req, err := s.client.NewRequest(http.MethodGet, "namespaces", &q, options) if err != nil { return nil, nil, err } var n []*Namespace resp, err := s.client.Do(req, &n) if err != nil { return nil, resp, err } return n, resp, nil } // GetNamespace gets a namespace by id. // // GitLab API docs: // https://docs.gitlab.com/ee/api/namespaces.html#get-namespace-by-id func (s *NamespacesService) GetNamespace(id interface{}, options ...RequestOptionFunc) (*Namespace, *Response, error) { namespace, err := parseID(id) if err != nil { return nil, nil, err } u := fmt.Sprintf("namespaces/%s", PathEscape(namespace)) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } n := new(Namespace) resp, err := s.client.Do(req, n) if err != nil { return nil, resp, err } return n, resp, nil } // NamespaceExistance represents a namespace exists result. // // GitLab API docs: // https://docs.gitlab.com/ee/api/namespaces.html#get-existence-of-a-namespace type NamespaceExistance struct { Exists bool `json:"exists"` Suggests []string `json:"suggests"` } // NamespaceExistsOptions represents the available NamespaceExists() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/namespaces.html#get-existence-of-a-namespace type NamespaceExistsOptions struct { ParentID *int `url:"parent_id,omitempty" json:"parent_id,omitempty"` } // NamespaceExists checks the existence of a namespace. // // GitLab API docs: // https://docs.gitlab.com/ee/api/namespaces.html#get-existence-of-a-namespace func (s *NamespacesService) NamespaceExists(id interface{}, opt *NamespaceExistsOptions, options ...RequestOptionFunc) (*NamespaceExistance, *Response, error) { namespace, err := parseID(id) if err != nil { return nil, nil, err } u := fmt.Sprintf("namespaces/%s/exists", namespace) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } n := new(NamespaceExistance) resp, err := s.client.Do(req, n) if err != nil { return nil, resp, err } return n, resp, nil } golang-gitlab-gitlab-org-api-client-go-0.123.0/namespaces_test.go000066400000000000000000000214371475761473200245730ustar00rootroot00000000000000// // Copyright 2021, Sander van Harmelen // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "fmt" "net/http" "reflect" "testing" "time" ) func TestListNamespaces(t *testing.T) { mux, client := setup(t) trialEndsOn, _ := time.Parse(time.RFC3339, "2022-05-08T00:00:00Z") trialEndsOnISOTime := ISOTime(trialEndsOn) mux.HandleFunc("/api/v4/namespaces", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprintf(w, `[ { "id": 1, "name": "user1", "path": "user1", "kind": "user", "full_path": "user1", "avatar_url": "https://secure.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon", "web_url": "https://gitlab.example.com/user1", "billable_members_count": 1, "plan": "default", "trial_ends_on": null, "trial": false }, { "id": 2, "name": "group1", "path": "group1", "kind": "group", "full_path": "group1", "web_url": "https://gitlab.example.com/groups/group1", "members_count_with_descendants": 2, "billable_members_count": 2, "plan": "default", "trial_ends_on": null, "trial": false }, { "id": 3, "name": "bar", "path": "bar", "kind": "group", "full_path": "foo/bar", "parent_id": 9, "web_url": "https://gitlab.example.com/groups/foo/bar", "members_count_with_descendants": 5, "billable_members_count": 5, "plan": "default", "trial_ends_on": null, "trial": false }, { "id": 4, "name": "group2", "path": "group2", "kind": "group", "full_path": "group2", "avatar_url": "https://gitlab.example.com/groups/group2", "web_url": "https://gitlab.example.com/group2", "billable_members_count": 1, "plan": "default", "trial_ends_on": "2022-05-08", "trial": true } ]`) }) testCases := []struct { event string search *string ownedOnly *bool }{ {"with_nothing", nil, nil}, {"with_search", Ptr("foobar"), nil}, {"with_owned_only", nil, Ptr(false)}, } for _, tc := range testCases { t.Run(tc.event, func(t *testing.T) { namespaces, _, err := client.Namespaces.ListNamespaces(&ListNamespacesOptions{Search: tc.search, OwnedOnly: tc.ownedOnly}) if err != nil { t.Errorf("Namespaces.ListNamespaces returned error: %v", err) } want := []*Namespace{ { ID: 1, Name: "user1", Path: "user1", Kind: "user", FullPath: "user1", AvatarURL: Ptr("https://secure.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon"), WebURL: "https://gitlab.example.com/user1", Plan: "default", BillableMembersCount: 1, TrialEndsOn: nil, Trial: false, }, { ID: 2, Name: "group1", Path: "group1", Kind: "group", FullPath: "group1", AvatarURL: nil, WebURL: "https://gitlab.example.com/groups/group1", MembersCountWithDescendants: 2, BillableMembersCount: 2, Plan: "default", TrialEndsOn: nil, Trial: false, }, { ID: 3, Name: "bar", Path: "bar", Kind: "group", FullPath: "foo/bar", ParentID: 9, AvatarURL: nil, WebURL: "https://gitlab.example.com/groups/foo/bar", MembersCountWithDescendants: 5, BillableMembersCount: 5, Plan: "default", TrialEndsOn: nil, Trial: false, }, { ID: 4, Name: "group2", Path: "group2", Kind: "group", FullPath: "group2", AvatarURL: Ptr("https://gitlab.example.com/groups/group2"), WebURL: "https://gitlab.example.com/group2", Plan: "default", BillableMembersCount: 1, TrialEndsOn: &trialEndsOnISOTime, Trial: true, }, } if !reflect.DeepEqual(namespaces, want) { t.Errorf("Namespaces.ListNamespaces returned \ngot:\n%v\nwant:\n%v", Stringify(namespaces), Stringify(want)) } }) } } func TestGetNamespace(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/namespaces/2", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprintf(w, `{ "id": 2, "name": "group1", "path": "group1", "kind": "group", "full_path": "group1", "avatar_url": null, "web_url": "https://gitlab.example.com/groups/group1", "members_count_with_descendants": 2, "billable_members_count": 2, "max_seats_used": 0, "seats_in_use": 0, "plan": "default", "trial_ends_on": null, "trial": false }`) }) namespace, _, err := client.Namespaces.GetNamespace(2) if err != nil { t.Errorf("Namespaces.GetNamespace returned error: %v", err) } want := &Namespace{ ID: 2, Name: "group1", Path: "group1", Kind: "group", FullPath: "group1", AvatarURL: nil, WebURL: "https://gitlab.example.com/groups/group1", MembersCountWithDescendants: 2, BillableMembersCount: 2, MaxSeatsUsed: Ptr(0), SeatsInUse: Ptr(0), Plan: "default", TrialEndsOn: nil, Trial: false, } if !reflect.DeepEqual(namespace, want) { t.Errorf("Namespaces.ListNamespaces returned \ngot:\n%v\nwant:\n%v", Stringify(namespace), Stringify(want)) } } func TestNamespaceExists(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/namespaces/my-group/exists", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprintf(w, `{ "exists": true, "suggests": [ "my-group1" ] }`) }) opt := &NamespaceExistsOptions{ ParentID: Ptr(1), } exists, _, err := client.Namespaces.NamespaceExists("my-group", opt) if err != nil { t.Errorf("Namespaces.NamespaceExists returned error: %v", err) } want := &NamespaceExistance{ Exists: true, Suggests: []string{"my-group1"}, } if !reflect.DeepEqual(exists, want) { t.Errorf("Namespaces.NamespaceExists returned \ngot:\n%v\nwant:\n%v", Stringify(exists), Stringify(want)) } } func TestSearchNamespace(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/namespaces", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprintf(w, `[ { "id": 4, "name": "twitter", "path": "twitter", "kind": "group", "full_path": "twitter", "avatar_url": null, "web_url": "https://gitlab.example.com/groups/twitter", "members_count_with_descendants": 2, "billable_members_count": 2, "max_seats_used": 0, "seats_in_use": 0, "plan": "default", "trial_ends_on": null, "trial": false } ]`) }) namespaces, _, err := client.Namespaces.SearchNamespace("?search=twitter") if err != nil { t.Errorf("Namespaces.SearchNamespaces returned error: %v", err) } want := []*Namespace{ { ID: 4, Name: "twitter", Path: "twitter", Kind: "group", FullPath: "twitter", AvatarURL: nil, WebURL: "https://gitlab.example.com/groups/twitter", MembersCountWithDescendants: 2, BillableMembersCount: 2, MaxSeatsUsed: Ptr(0), SeatsInUse: Ptr(0), Plan: "default", TrialEndsOn: nil, Trial: false, }, } if !reflect.DeepEqual(namespaces, want) { t.Errorf("Namespaces.SearchNamespaces returned \ngot:\n%v\nwant:\n%v", Stringify(namespaces), Stringify(want)) } } golang-gitlab-gitlab-org-api-client-go-0.123.0/notes.go000066400000000000000000000505341475761473200225450ustar00rootroot00000000000000// // Copyright 2021, Sander van Harmelen // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "fmt" "net/http" "time" ) // NotesService handles communication with the notes related methods // of the GitLab API. // // GitLab API docs: https://docs.gitlab.com/ee/api/notes.html type NotesService struct { client *Client } // Note represents a GitLab note. // // GitLab API docs: https://docs.gitlab.com/ee/api/notes.html type Note struct { ID int `json:"id"` Type NoteTypeValue `json:"type"` Body string `json:"body"` Attachment string `json:"attachment"` Title string `json:"title"` FileName string `json:"file_name"` Author struct { ID int `json:"id"` Username string `json:"username"` Email string `json:"email"` Name string `json:"name"` State string `json:"state"` AvatarURL string `json:"avatar_url"` WebURL string `json:"web_url"` } `json:"author"` System bool `json:"system"` CreatedAt *time.Time `json:"created_at"` UpdatedAt *time.Time `json:"updated_at"` ExpiresAt *time.Time `json:"expires_at"` CommitID string `json:"commit_id"` Position *NotePosition `json:"position"` NoteableID int `json:"noteable_id"` NoteableType string `json:"noteable_type"` ProjectID int `json:"project_id"` NoteableIID int `json:"noteable_iid"` Resolvable bool `json:"resolvable"` Resolved bool `json:"resolved"` ResolvedAt *time.Time `json:"resolved_at"` ResolvedBy struct { ID int `json:"id"` Username string `json:"username"` Email string `json:"email"` Name string `json:"name"` State string `json:"state"` AvatarURL string `json:"avatar_url"` WebURL string `json:"web_url"` } `json:"resolved_by"` Confidential bool `json:"confidential"` Internal bool `json:"internal"` } // NotePosition represents the position attributes of a note. type NotePosition struct { BaseSHA string `json:"base_sha"` StartSHA string `json:"start_sha"` HeadSHA string `json:"head_sha"` PositionType string `json:"position_type"` NewPath string `json:"new_path,omitempty"` NewLine int `json:"new_line,omitempty"` OldPath string `json:"old_path,omitempty"` OldLine int `json:"old_line,omitempty"` LineRange *LineRange `json:"line_range,omitempty"` } // LineRange represents the range of a note. type LineRange struct { StartRange *LinePosition `json:"start"` EndRange *LinePosition `json:"end"` } // LinePosition represents a position in a line range. type LinePosition struct { LineCode string `json:"line_code"` Type string `json:"type"` OldLine int `json:"old_line"` NewLine int `json:"new_line"` } func (n Note) String() string { return Stringify(n) } // ListIssueNotesOptions represents the available ListIssueNotes() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/notes.html#list-project-issue-notes type ListIssueNotesOptions struct { ListOptions OrderBy *string `url:"order_by,omitempty" json:"order_by,omitempty"` Sort *string `url:"sort,omitempty" json:"sort,omitempty"` } // ListIssueNotes gets a list of all notes for a single issue. // // GitLab API docs: // https://docs.gitlab.com/ee/api/notes.html#list-project-issue-notes func (s *NotesService) ListIssueNotes(pid interface{}, issue int, opt *ListIssueNotesOptions, options ...RequestOptionFunc) ([]*Note, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/issues/%d/notes", PathEscape(project), issue) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } var n []*Note resp, err := s.client.Do(req, &n) if err != nil { return nil, resp, err } return n, resp, nil } // GetIssueNote returns a single note for a specific project issue. // // GitLab API docs: // https://docs.gitlab.com/ee/api/notes.html#get-single-issue-note func (s *NotesService) GetIssueNote(pid interface{}, issue, note int, options ...RequestOptionFunc) (*Note, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/issues/%d/notes/%d", PathEscape(project), issue, note) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } n := new(Note) resp, err := s.client.Do(req, n) if err != nil { return nil, resp, err } return n, resp, nil } // CreateIssueNoteOptions represents the available CreateIssueNote() // options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/notes.html#create-new-issue-note type CreateIssueNoteOptions struct { Body *string `url:"body,omitempty" json:"body,omitempty"` CreatedAt *time.Time `url:"created_at,omitempty" json:"created_at,omitempty"` } // CreateIssueNote creates a new note to a single project issue. // // GitLab API docs: // https://docs.gitlab.com/ee/api/notes.html#create-new-issue-note func (s *NotesService) CreateIssueNote(pid interface{}, issue int, opt *CreateIssueNoteOptions, options ...RequestOptionFunc) (*Note, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/issues/%d/notes", PathEscape(project), issue) req, err := s.client.NewRequest(http.MethodPost, u, opt, options) if err != nil { return nil, nil, err } n := new(Note) resp, err := s.client.Do(req, n) if err != nil { return nil, resp, err } return n, resp, nil } // UpdateIssueNoteOptions represents the available UpdateIssueNote() // options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/notes.html#modify-existing-issue-note type UpdateIssueNoteOptions struct { Body *string `url:"body,omitempty" json:"body,omitempty"` } // UpdateIssueNote modifies existing note of an issue. // // GitLab API docs: // https://docs.gitlab.com/ee/api/notes.html#modify-existing-issue-note func (s *NotesService) UpdateIssueNote(pid interface{}, issue, note int, opt *UpdateIssueNoteOptions, options ...RequestOptionFunc) (*Note, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/issues/%d/notes/%d", PathEscape(project), issue, note) req, err := s.client.NewRequest(http.MethodPut, u, opt, options) if err != nil { return nil, nil, err } n := new(Note) resp, err := s.client.Do(req, n) if err != nil { return nil, resp, err } return n, resp, nil } // DeleteIssueNote deletes an existing note of an issue. // // GitLab API docs: // https://docs.gitlab.com/ee/api/notes.html#delete-an-issue-note func (s *NotesService) DeleteIssueNote(pid interface{}, issue, note int, options ...RequestOptionFunc) (*Response, error) { project, err := parseID(pid) if err != nil { return nil, err } u := fmt.Sprintf("projects/%s/issues/%d/notes/%d", PathEscape(project), issue, note) req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } // ListSnippetNotesOptions represents the available ListSnippetNotes() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/notes.html#list-all-snippet-notes type ListSnippetNotesOptions struct { ListOptions OrderBy *string `url:"order_by,omitempty" json:"order_by,omitempty"` Sort *string `url:"sort,omitempty" json:"sort,omitempty"` } // ListSnippetNotes gets a list of all notes for a single snippet. Snippet // notes are comments users can post to a snippet. // // GitLab API docs: // https://docs.gitlab.com/ee/api/notes.html#list-all-snippet-notes func (s *NotesService) ListSnippetNotes(pid interface{}, snippet int, opt *ListSnippetNotesOptions, options ...RequestOptionFunc) ([]*Note, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/snippets/%d/notes", PathEscape(project), snippet) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } var n []*Note resp, err := s.client.Do(req, &n) if err != nil { return nil, resp, err } return n, resp, nil } // GetSnippetNote returns a single note for a given snippet. // // GitLab API docs: // https://docs.gitlab.com/ee/api/notes.html#get-single-snippet-note func (s *NotesService) GetSnippetNote(pid interface{}, snippet, note int, options ...RequestOptionFunc) (*Note, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/snippets/%d/notes/%d", PathEscape(project), snippet, note) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } n := new(Note) resp, err := s.client.Do(req, n) if err != nil { return nil, resp, err } return n, resp, nil } // CreateSnippetNoteOptions represents the available CreateSnippetNote() // options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/notes.html#create-new-snippet-note type CreateSnippetNoteOptions struct { Body *string `url:"body,omitempty" json:"body,omitempty"` } // CreateSnippetNote creates a new note for a single snippet. Snippet notes are // comments users can post to a snippet. // // GitLab API docs: // https://docs.gitlab.com/ee/api/notes.html#create-new-snippet-note func (s *NotesService) CreateSnippetNote(pid interface{}, snippet int, opt *CreateSnippetNoteOptions, options ...RequestOptionFunc) (*Note, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/snippets/%d/notes", PathEscape(project), snippet) req, err := s.client.NewRequest(http.MethodPost, u, opt, options) if err != nil { return nil, nil, err } n := new(Note) resp, err := s.client.Do(req, n) if err != nil { return nil, resp, err } return n, resp, nil } // UpdateSnippetNoteOptions represents the available UpdateSnippetNote() // options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/notes.html#modify-existing-snippet-note type UpdateSnippetNoteOptions struct { Body *string `url:"body,omitempty" json:"body,omitempty"` } // UpdateSnippetNote modifies existing note of a snippet. // // GitLab API docs: // https://docs.gitlab.com/ee/api/notes.html#modify-existing-snippet-note func (s *NotesService) UpdateSnippetNote(pid interface{}, snippet, note int, opt *UpdateSnippetNoteOptions, options ...RequestOptionFunc) (*Note, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/snippets/%d/notes/%d", PathEscape(project), snippet, note) req, err := s.client.NewRequest(http.MethodPut, u, opt, options) if err != nil { return nil, nil, err } n := new(Note) resp, err := s.client.Do(req, n) if err != nil { return nil, resp, err } return n, resp, nil } // DeleteSnippetNote deletes an existing note of a snippet. // // GitLab API docs: // https://docs.gitlab.com/ee/api/notes.html#delete-a-snippet-note func (s *NotesService) DeleteSnippetNote(pid interface{}, snippet, note int, options ...RequestOptionFunc) (*Response, error) { project, err := parseID(pid) if err != nil { return nil, err } u := fmt.Sprintf("projects/%s/snippets/%d/notes/%d", PathEscape(project), snippet, note) req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } // ListMergeRequestNotesOptions represents the available ListMergeRequestNotes() // options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/notes.html#list-all-merge-request-notes type ListMergeRequestNotesOptions struct { ListOptions OrderBy *string `url:"order_by,omitempty" json:"order_by,omitempty"` Sort *string `url:"sort,omitempty" json:"sort,omitempty"` } // ListMergeRequestNotes gets a list of all notes for a single merge request. // // GitLab API docs: // https://docs.gitlab.com/ee/api/notes.html#list-all-merge-request-notes func (s *NotesService) ListMergeRequestNotes(pid interface{}, mergeRequest int, opt *ListMergeRequestNotesOptions, options ...RequestOptionFunc) ([]*Note, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/merge_requests/%d/notes", PathEscape(project), mergeRequest) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } var n []*Note resp, err := s.client.Do(req, &n) if err != nil { return nil, resp, err } return n, resp, nil } // GetMergeRequestNote returns a single note for a given merge request. // // GitLab API docs: // https://docs.gitlab.com/ee/api/notes.html#get-single-merge-request-note func (s *NotesService) GetMergeRequestNote(pid interface{}, mergeRequest, note int, options ...RequestOptionFunc) (*Note, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/merge_requests/%d/notes/%d", PathEscape(project), mergeRequest, note) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } n := new(Note) resp, err := s.client.Do(req, n) if err != nil { return nil, resp, err } return n, resp, nil } // CreateMergeRequestNoteOptions represents the available // CreateMergeRequestNote() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/notes.html#create-new-merge-request-note type CreateMergeRequestNoteOptions struct { Body *string `url:"body,omitempty" json:"body,omitempty"` } // CreateMergeRequestNote creates a new note for a single merge request. // // GitLab API docs: // https://docs.gitlab.com/ee/api/notes.html#create-new-merge-request-note func (s *NotesService) CreateMergeRequestNote(pid interface{}, mergeRequest int, opt *CreateMergeRequestNoteOptions, options ...RequestOptionFunc) (*Note, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/merge_requests/%d/notes", PathEscape(project), mergeRequest) req, err := s.client.NewRequest(http.MethodPost, u, opt, options) if err != nil { return nil, nil, err } n := new(Note) resp, err := s.client.Do(req, n) if err != nil { return nil, resp, err } return n, resp, nil } // UpdateMergeRequestNoteOptions represents the available // UpdateMergeRequestNote() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/notes.html#modify-existing-merge-request-note type UpdateMergeRequestNoteOptions struct { Body *string `url:"body,omitempty" json:"body,omitempty"` } // UpdateMergeRequestNote modifies existing note of a merge request. // // GitLab API docs: // https://docs.gitlab.com/ee/api/notes.html#modify-existing-merge-request-note func (s *NotesService) UpdateMergeRequestNote(pid interface{}, mergeRequest, note int, opt *UpdateMergeRequestNoteOptions, options ...RequestOptionFunc) (*Note, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf( "projects/%s/merge_requests/%d/notes/%d", PathEscape(project), mergeRequest, note) req, err := s.client.NewRequest(http.MethodPut, u, opt, options) if err != nil { return nil, nil, err } n := new(Note) resp, err := s.client.Do(req, n) if err != nil { return nil, resp, err } return n, resp, nil } // DeleteMergeRequestNote deletes an existing note of a merge request. // // GitLab API docs: // https://docs.gitlab.com/ee/api/notes.html#delete-a-merge-request-note func (s *NotesService) DeleteMergeRequestNote(pid interface{}, mergeRequest, note int, options ...RequestOptionFunc) (*Response, error) { project, err := parseID(pid) if err != nil { return nil, err } u := fmt.Sprintf( "projects/%s/merge_requests/%d/notes/%d", PathEscape(project), mergeRequest, note) req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } // ListEpicNotesOptions represents the available ListEpicNotes() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/notes.html#list-all-epic-notes type ListEpicNotesOptions struct { ListOptions OrderBy *string `url:"order_by,omitempty" json:"order_by,omitempty"` Sort *string `url:"sort,omitempty" json:"sort,omitempty"` } // ListEpicNotes gets a list of all notes for a single epic. // // GitLab API docs: // https://docs.gitlab.com/ee/api/notes.html#list-all-epic-notes func (s *NotesService) ListEpicNotes(gid interface{}, epic int, opt *ListEpicNotesOptions, options ...RequestOptionFunc) ([]*Note, *Response, error) { group, err := parseID(gid) if err != nil { return nil, nil, err } u := fmt.Sprintf("groups/%s/epics/%d/notes", PathEscape(group), epic) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } var n []*Note resp, err := s.client.Do(req, &n) if err != nil { return nil, resp, err } return n, resp, nil } // GetEpicNote returns a single note for an epic. // // GitLab API docs: // https://docs.gitlab.com/ee/api/notes.html#get-single-epic-note func (s *NotesService) GetEpicNote(gid interface{}, epic, note int, options ...RequestOptionFunc) (*Note, *Response, error) { group, err := parseID(gid) if err != nil { return nil, nil, err } u := fmt.Sprintf("groups/%s/epics/%d/notes/%d", PathEscape(group), epic, note) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } n := new(Note) resp, err := s.client.Do(req, n) if err != nil { return nil, resp, err } return n, resp, nil } // CreateEpicNoteOptions represents the available CreateEpicNote() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/notes.html#create-new-epic-note type CreateEpicNoteOptions struct { Body *string `url:"body,omitempty" json:"body,omitempty"` } // CreateEpicNote creates a new note for a single merge request. // // GitLab API docs: // https://docs.gitlab.com/ee/api/notes.html#create-new-epic-note func (s *NotesService) CreateEpicNote(gid interface{}, epic int, opt *CreateEpicNoteOptions, options ...RequestOptionFunc) (*Note, *Response, error) { group, err := parseID(gid) if err != nil { return nil, nil, err } u := fmt.Sprintf("groups/%s/epics/%d/notes", PathEscape(group), epic) req, err := s.client.NewRequest(http.MethodPost, u, opt, options) if err != nil { return nil, nil, err } n := new(Note) resp, err := s.client.Do(req, n) if err != nil { return nil, resp, err } return n, resp, nil } // UpdateEpicNoteOptions represents the available UpdateEpicNote() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/notes.html#modify-existing-epic-note type UpdateEpicNoteOptions struct { Body *string `url:"body,omitempty" json:"body,omitempty"` } // UpdateEpicNote modifies existing note of an epic. // // https://docs.gitlab.com/ee/api/notes.html#modify-existing-epic-note func (s *NotesService) UpdateEpicNote(gid interface{}, epic, note int, opt *UpdateEpicNoteOptions, options ...RequestOptionFunc) (*Note, *Response, error) { group, err := parseID(gid) if err != nil { return nil, nil, err } u := fmt.Sprintf("groups/%s/epics/%d/notes/%d", PathEscape(group), epic, note) req, err := s.client.NewRequest(http.MethodPut, u, opt, options) if err != nil { return nil, nil, err } n := new(Note) resp, err := s.client.Do(req, n) if err != nil { return nil, resp, err } return n, resp, nil } // DeleteEpicNote deletes an existing note of a merge request. // // https://docs.gitlab.com/ee/api/notes.html#delete-an-epic-note func (s *NotesService) DeleteEpicNote(gid interface{}, epic, note int, options ...RequestOptionFunc) (*Response, error) { group, err := parseID(gid) if err != nil { return nil, err } u := fmt.Sprintf("groups/%s/epics/%d/notes/%d", PathEscape(group), epic, note) req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } golang-gitlab-gitlab-org-api-client-go-0.123.0/notes_test.go000066400000000000000000000042511475761473200235770ustar00rootroot00000000000000// // Copyright 2021, Sander van Harmelen // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "fmt" "net/http" "reflect" "testing" ) func TestGetEpicNote(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/groups/1/epics/4329/notes/3", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, `{"id":3,"type":null,"body":"foo bar","attachment":null,"system":false,"noteable_id":4392,"noteable_type":"Epic","resolvable":false,"noteable_iid":null}`) }) note, _, err := client.Notes.GetEpicNote("1", 4329, 3, nil) if err != nil { t.Fatal(err) } want := &Note{ ID: 3, Body: "foo bar", Attachment: "", Title: "", FileName: "", System: false, NoteableID: 4392, NoteableType: "Epic", } if !reflect.DeepEqual(note, want) { t.Errorf("Notes.GetEpicNote want %#v, got %#v", note, want) } } func TestGetMergeRequestNote(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/merge_requests/4329/notes/3", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, `{"id":3,"type":"DiffNote","body":"foo bar","attachment":null,"system":false,"noteable_id":4392,"noteable_type":"Epic","resolvable":false,"noteable_iid":null}`) }) note, _, err := client.Notes.GetMergeRequestNote("1", 4329, 3, nil) if err != nil { t.Fatal(err) } want := &Note{ ID: 3, Type: DiffNote, Body: "foo bar", System: false, NoteableID: 4392, NoteableType: "Epic", } if !reflect.DeepEqual(note, want) { t.Errorf("Notes.GetEpicNote want %#v, got %#v", note, want) } } golang-gitlab-gitlab-org-api-client-go-0.123.0/notifications.go000066400000000000000000000227131475761473200242640ustar00rootroot00000000000000// // Copyright 2021, Sander van Harmelen // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "errors" "fmt" "net/http" ) // NotificationSettingsService handles communication with the notification settings // related methods of the GitLab API. // // GitLab API docs: https://docs.gitlab.com/ee/api/notification_settings.html type NotificationSettingsService struct { client *Client } // NotificationSettings represents the Gitlab notification setting. // // GitLab API docs: // https://docs.gitlab.com/ee/api/notification_settings.html#valid-notification-levels type NotificationSettings struct { Level NotificationLevelValue `json:"level"` NotificationEmail string `json:"notification_email"` Events *NotificationEvents `json:"events"` } // NotificationEvents represents the available notification setting events. // // GitLab API docs: // https://docs.gitlab.com/ee/api/notification_settings.html#valid-notification-levels type NotificationEvents struct { CloseIssue bool `json:"close_issue"` CloseMergeRequest bool `json:"close_merge_request"` FailedPipeline bool `json:"failed_pipeline"` FixedPipeline bool `json:"fixed_pipeline"` IssueDue bool `json:"issue_due"` MergeWhenPipelineSucceeds bool `json:"merge_when_pipeline_succeeds"` MergeMergeRequest bool `json:"merge_merge_request"` MovedProject bool `json:"moved_project"` NewIssue bool `json:"new_issue"` NewMergeRequest bool `json:"new_merge_request"` NewEpic bool `json:"new_epic"` NewNote bool `json:"new_note"` PushToMergeRequest bool `json:"push_to_merge_request"` ReassignIssue bool `json:"reassign_issue"` ReassignMergeRequest bool `json:"reassign_merge_request"` ReopenIssue bool `json:"reopen_issue"` ReopenMergeRequest bool `json:"reopen_merge_request"` SuccessPipeline bool `json:"success_pipeline"` } func (ns NotificationSettings) String() string { return Stringify(ns) } // GetGlobalSettings returns current notification settings and email address. // // GitLab API docs: // https://docs.gitlab.com/ee/api/notification_settings.html#global-notification-settings func (s *NotificationSettingsService) GetGlobalSettings(options ...RequestOptionFunc) (*NotificationSettings, *Response, error) { u := "notification_settings" req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } ns := new(NotificationSettings) resp, err := s.client.Do(req, ns) if err != nil { return nil, resp, err } return ns, resp, nil } // NotificationSettingsOptions represents the available options that can be passed // to the API when updating the notification settings. type NotificationSettingsOptions struct { Level *NotificationLevelValue `url:"level,omitempty" json:"level,omitempty"` NotificationEmail *string `url:"notification_email,omitempty" json:"notification_email,omitempty"` CloseIssue *bool `url:"close_issue,omitempty" json:"close_issue,omitempty"` CloseMergeRequest *bool `url:"close_merge_request,omitempty" json:"close_merge_request,omitempty"` FailedPipeline *bool `url:"failed_pipeline,omitempty" json:"failed_pipeline,omitempty"` FixedPipeline *bool `url:"fixed_pipeline,omitempty" json:"fixed_pipeline,omitempty"` IssueDue *bool `url:"issue_due,omitempty" json:"issue_due,omitempty"` MergeMergeRequest *bool `url:"merge_merge_request,omitempty" json:"merge_merge_request,omitempty"` MergeWhenPipelineSucceeds *bool `url:"merge_when_pipeline_succeeds,omitempty" json:"merge_when_pipeline_succeeds,omitempty"` MovedProject *bool `url:"moved_project,omitempty" json:"moved_project,omitempty"` NewEpic *bool `url:"new_epic,omitempty" json:"new_epic,omitempty"` NewIssue *bool `url:"new_issue,omitempty" json:"new_issue,omitempty"` NewMergeRequest *bool `url:"new_merge_request,omitempty" json:"new_merge_request,omitempty"` NewNote *bool `url:"new_note,omitempty" json:"new_note,omitempty"` PushToMergeRequest *bool `url:"push_to_merge_request,omitempty" json:"push_to_merge_request,omitempty"` ReassignIssue *bool `url:"reassign_issue,omitempty" json:"reassign_issue,omitempty"` ReassignMergeRequest *bool `url:"reassign_merge_request,omitempty" json:"reassign_merge_request,omitempty"` ReopenIssue *bool `url:"reopen_issue,omitempty" json:"reopen_issue,omitempty"` ReopenMergeRequest *bool `url:"reopen_merge_request,omitempty" json:"reopen_merge_request,omitempty"` SuccessPipeline *bool `url:"success_pipeline,omitempty" json:"success_pipeline,omitempty"` } // UpdateGlobalSettings updates current notification settings and email address. // // GitLab API docs: // https://docs.gitlab.com/ee/api/notification_settings.html#update-global-notification-settings func (s *NotificationSettingsService) UpdateGlobalSettings(opt *NotificationSettingsOptions, options ...RequestOptionFunc) (*NotificationSettings, *Response, error) { if opt.Level != nil && *opt.Level == GlobalNotificationLevel { return nil, nil, errors.New( "notification level 'global' is not valid for global notification settings") } u := "notification_settings" req, err := s.client.NewRequest(http.MethodPut, u, opt, options) if err != nil { return nil, nil, err } ns := new(NotificationSettings) resp, err := s.client.Do(req, ns) if err != nil { return nil, resp, err } return ns, resp, nil } // GetSettingsForGroup returns current group notification settings. // // GitLab API docs: // https://docs.gitlab.com/ee/api/notification_settings.html#group--project-level-notification-settings func (s *NotificationSettingsService) GetSettingsForGroup(gid interface{}, options ...RequestOptionFunc) (*NotificationSettings, *Response, error) { group, err := parseID(gid) if err != nil { return nil, nil, err } u := fmt.Sprintf("groups/%s/notification_settings", PathEscape(group)) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } ns := new(NotificationSettings) resp, err := s.client.Do(req, ns) if err != nil { return nil, resp, err } return ns, resp, nil } // GetSettingsForProject returns current project notification settings. // // GitLab API docs: // https://docs.gitlab.com/ee/api/notification_settings.html#group--project-level-notification-settings func (s *NotificationSettingsService) GetSettingsForProject(pid interface{}, options ...RequestOptionFunc) (*NotificationSettings, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/notification_settings", PathEscape(project)) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } ns := new(NotificationSettings) resp, err := s.client.Do(req, ns) if err != nil { return nil, resp, err } return ns, resp, nil } // UpdateSettingsForGroup updates current group notification settings. // // GitLab API docs: // https://docs.gitlab.com/ee/api/notification_settings.html#update-groupproject-level-notification-settings func (s *NotificationSettingsService) UpdateSettingsForGroup(gid interface{}, opt *NotificationSettingsOptions, options ...RequestOptionFunc) (*NotificationSettings, *Response, error) { group, err := parseID(gid) if err != nil { return nil, nil, err } u := fmt.Sprintf("groups/%s/notification_settings", PathEscape(group)) req, err := s.client.NewRequest(http.MethodPut, u, opt, options) if err != nil { return nil, nil, err } ns := new(NotificationSettings) resp, err := s.client.Do(req, ns) if err != nil { return nil, resp, err } return ns, resp, nil } // UpdateSettingsForProject updates current project notification settings. // // GitLab API docs: // https://docs.gitlab.com/ee/api/notification_settings.html#update-groupproject-level-notification-settings func (s *NotificationSettingsService) UpdateSettingsForProject(pid interface{}, opt *NotificationSettingsOptions, options ...RequestOptionFunc) (*NotificationSettings, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/notification_settings", PathEscape(project)) req, err := s.client.NewRequest(http.MethodPut, u, opt, options) if err != nil { return nil, nil, err } ns := new(NotificationSettings) resp, err := s.client.Do(req, ns) if err != nil { return nil, resp, err } return ns, resp, nil } golang-gitlab-gitlab-org-api-client-go-0.123.0/notifications_test.go000066400000000000000000000133731475761473200253250ustar00rootroot00000000000000// // Copyright 2021, Sander van Harmelen // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "encoding/json" "fmt" "io" "net/http" "reflect" "testing" ) func TestGetGlobalSettings(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/notification_settings", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprintf(w, `{ "level": "participating", "notification_email": "admin@example.com" }`) }) settings, _, err := client.NotificationSettings.GetGlobalSettings() if err != nil { t.Errorf("NotifcationSettings.GetGlobalSettings returned error: %v", err) } want := &NotificationSettings{ Level: 1, NotificationEmail: "admin@example.com", } if !reflect.DeepEqual(settings, want) { t.Errorf("NotificationSettings.GetGlobalSettings returned %+v, want %+v", settings, want) } } func TestGetProjectSettings(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/notification_settings", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprintf(w, `{ "level":"custom", "events":{ "new_note":true, "new_issue":true, "reopen_issue":true, "close_issue":true, "reassign_issue":true, "issue_due":true, "new_merge_request":true, "push_to_merge_request":true, "reopen_merge_request":true, "close_merge_request":true, "reassign_merge_request":true, "merge_merge_request":true, "failed_pipeline":true, "fixed_pipeline":true, "success_pipeline":true, "moved_project":true, "merge_when_pipeline_succeeds":true, "new_epic":true } }`) }) settings, _, err := client.NotificationSettings.GetSettingsForProject(1) if err != nil { t.Errorf("NotifcationSettings.GetSettingsForProject returned error: %v", err) } want := &NotificationSettings{ Level: 5, // custom Events: &NotificationEvents{ NewEpic: true, NewNote: true, NewIssue: true, ReopenIssue: true, CloseIssue: true, ReassignIssue: true, IssueDue: true, NewMergeRequest: true, PushToMergeRequest: true, ReopenMergeRequest: true, CloseMergeRequest: true, ReassignMergeRequest: true, MergeMergeRequest: true, FailedPipeline: true, FixedPipeline: true, SuccessPipeline: true, MovedProject: true, MergeWhenPipelineSucceeds: true, }, } if !reflect.DeepEqual(settings, want) { t.Errorf("NotificationSettings.GetSettingsForProject returned %+v, want %+v", settings, want) } } func TestUpdateProjectSettings(t *testing.T) { mux, client := setup(t) // Create the request to send var reqBody NotificationSettingsOptions customLevel := notificationLevelTypes["custom"] options := NotificationSettingsOptions{ Level: &customLevel, NewEpic: Bool(true), MovedProject: Bool(true), CloseIssue: Bool(true), } // Handle the request on the server, and return a fully hydrated response mux.HandleFunc("/api/v4/projects/1/notification_settings", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPut) // Store the body for later, so we can check only some values are marshaled properly for update body, _ := io.ReadAll(r.Body) json.Unmarshal(body, &reqBody) fmt.Fprintf(w, `{ "level":"custom", "events":{ "new_note":true, "new_issue":true, "reopen_issue":true, "close_issue":true, "reassign_issue":true, "issue_due":true, "new_merge_request":true, "push_to_merge_request":true, "reopen_merge_request":true, "close_merge_request":true, "reassign_merge_request":true, "merge_merge_request":true, "failed_pipeline":true, "fixed_pipeline":true, "success_pipeline":true, "moved_project":true, "merge_when_pipeline_succeeds":true, "new_epic":true } }`) }) // Make the actual request settings, _, err := client.NotificationSettings.UpdateSettingsForProject(1, &options) if err != nil { t.Errorf("NotifcationSettings.UpdateSettingsForProject returned error: %v", err) } // Test the response and the request wantResponse := &NotificationSettings{ Level: customLevel, Events: &NotificationEvents{ NewEpic: true, NewNote: true, NewIssue: true, ReopenIssue: true, CloseIssue: true, ReassignIssue: true, IssueDue: true, NewMergeRequest: true, PushToMergeRequest: true, ReopenMergeRequest: true, CloseMergeRequest: true, ReassignMergeRequest: true, MergeMergeRequest: true, FailedPipeline: true, FixedPipeline: true, SuccessPipeline: true, MovedProject: true, MergeWhenPipelineSucceeds: true, }, } if !reflect.DeepEqual(settings, wantResponse) { t.Errorf("NotificationSettings.UpdateSettingsForProject returned for the response %+v, want %+v", settings, wantResponse) } if !reflect.DeepEqual(settings, wantResponse) { t.Errorf("NotificationSettings.UpdateSettingsForProject send for the request %+v, want %+v", reqBody, options) } } golang-gitlab-gitlab-org-api-client-go-0.123.0/packages.go000066400000000000000000000176471475761473200232030ustar00rootroot00000000000000// // Copyright 2021, Kordian Bruck // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "fmt" "net/http" "time" ) // PackagesService handles communication with the packages related methods // of the GitLab API. // // GitLab API docs: https://docs.gitlab.com/ee/api/packages.html type PackagesService struct { client *Client } // Package represents a GitLab package. // // GitLab API docs: https://docs.gitlab.com/ee/api/packages.html type Package struct { ID int `json:"id"` Name string `json:"name"` Version string `json:"version"` PackageType string `json:"package_type"` Status string `json:"status"` Links *PackageLinks `json:"_links"` CreatedAt *time.Time `json:"created_at"` LastDownloadedAt *time.Time `json:"last_downloaded_at"` Tags []PackageTag `json:"tags"` } func (s Package) String() string { return Stringify(s) } // GroupPackage represents a GitLab group package. // // GitLab API docs: https://docs.gitlab.com/ee/api/packages.html type GroupPackage struct { Package ProjectID int `json:"project_id"` ProjectPath string `json:"project_path"` } func (s GroupPackage) String() string { return Stringify(s) } // PackageLinks holds links for itself and deleting. type PackageLinks struct { WebPath string `json:"web_path"` DeleteAPIPath string `json:"delete_api_path"` } func (s PackageLinks) String() string { return Stringify(s) } // PackageTag holds label information about the package type PackageTag struct { ID int `json:"id"` PackageID int `json:"package_id"` Name string `json:"name"` CreatedAt *time.Time `json:"created_at"` UpdatedAt *time.Time `json:"updated_at"` } func (s PackageTag) String() string { return Stringify(s) } // PackageFile represents one file contained within a package. // // GitLab API docs: https://docs.gitlab.com/ee/api/packages.html type PackageFile struct { ID int `json:"id"` PackageID int `json:"package_id"` CreatedAt *time.Time `json:"created_at"` FileName string `json:"file_name"` Size int `json:"size"` FileMD5 string `json:"file_md5"` FileSHA1 string `json:"file_sha1"` FileSHA256 string `json:"file_sha256"` Pipeline *[]Pipeline `json:"pipelines"` } func (s PackageFile) String() string { return Stringify(s) } // ListProjectPackagesOptions represents the available ListProjectPackages() // options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/packages.html#within-a-project type ListProjectPackagesOptions struct { ListOptions OrderBy *string `url:"order_by,omitempty" json:"order_by,omitempty"` Sort *string `url:"sort,omitempty" json:"sort,omitempty"` PackageType *string `url:"package_type,omitempty" json:"package_type,omitempty"` PackageName *string `url:"package_name,omitempty" json:"package_name,omitempty"` PackageVersion *string `url:"package_version,omitempty" json:"package_version,omitempty"` IncludeVersionless *bool `url:"include_versionless,omitempty" json:"include_versionless,omitempty"` Status *string `url:"status,omitempty" json:"status,omitempty"` } // ListProjectPackages gets a list of packages in a project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/packages.html#within-a-project func (s *PackagesService) ListProjectPackages(pid interface{}, opt *ListProjectPackagesOptions, options ...RequestOptionFunc) ([]*Package, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/packages", PathEscape(project)) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } var ps []*Package resp, err := s.client.Do(req, &ps) if err != nil { return nil, resp, err } return ps, resp, nil } // ListGroupPackagesOptions represents the available ListGroupPackages() // options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/packages.html#within-a-group type ListGroupPackagesOptions struct { ListOptions ExcludeSubGroups *bool `url:"exclude_subgroups,omitempty" json:"exclude_subgroups,omitempty"` OrderBy *string `url:"order_by,omitempty" json:"order_by,omitempty"` Sort *string `url:"sort,omitempty" json:"sort,omitempty"` PackageType *string `url:"package_type,omitempty" json:"package_type,omitempty"` PackageName *string `url:"package_name,omitempty" json:"package_name,omitempty"` IncludeVersionless *bool `url:"include_versionless,omitempty" json:"include_versionless,omitempty"` Status *string `url:"status,omitempty" json:"status,omitempty"` } // ListGroupPackages gets a list of packages in a group. // // GitLab API docs: // https://docs.gitlab.com/ee/api/packages.html#within-a-group func (s *PackagesService) ListGroupPackages(gid interface{}, opt *ListGroupPackagesOptions, options ...RequestOptionFunc) ([]*GroupPackage, *Response, error) { group, err := parseID(gid) if err != nil { return nil, nil, err } u := fmt.Sprintf("groups/%s/packages", PathEscape(group)) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } var ps []*GroupPackage resp, err := s.client.Do(req, &ps) if err != nil { return nil, resp, err } return ps, resp, nil } // ListPackageFilesOptions represents the available ListPackageFiles() // options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/packages.html#list-package-files type ListPackageFilesOptions ListOptions // ListPackageFiles gets a list of files that are within a package // // GitLab API docs: // https://docs.gitlab.com/ee/api/packages.html#list-package-files func (s *PackagesService) ListPackageFiles(pid interface{}, pkg int, opt *ListPackageFilesOptions, options ...RequestOptionFunc) ([]*PackageFile, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf( "projects/%s/packages/%d/package_files", PathEscape(project), pkg, ) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } var pfs []*PackageFile resp, err := s.client.Do(req, &pfs) if err != nil { return nil, resp, err } return pfs, resp, nil } // DeleteProjectPackage deletes a package in a project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/packages.html#delete-a-project-package func (s *PackagesService) DeleteProjectPackage(pid interface{}, pkg int, options ...RequestOptionFunc) (*Response, error) { project, err := parseID(pid) if err != nil { return nil, err } u := fmt.Sprintf("projects/%s/packages/%d", PathEscape(project), pkg) req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } // DeletePackageFile deletes a file in project package // // GitLab API docs: // https://docs.gitlab.com/ee/api/packages.html#delete-a-package-file func (s *PackagesService) DeletePackageFile(pid interface{}, pkg, file int, options ...RequestOptionFunc) (*Response, error) { project, err := parseID(pid) if err != nil { return nil, err } u := fmt.Sprintf("projects/%s/packages/%d/package_files/%d", PathEscape(project), pkg, file) req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } golang-gitlab-gitlab-org-api-client-go-0.123.0/packages_test.go000066400000000000000000000106611475761473200242270ustar00rootroot00000000000000package gitlab import ( "fmt" "net/http" "testing" "time" "github.com/stretchr/testify/require" ) func TestPackagesService_ListProjectPackages(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/3/packages", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprintf(w, ` [ { "id": 3, "name": "Hello/0.1@mycompany/stable", "conan_package_name": "Hello", "version": "0.1", "package_type": "conan", "last_downloaded_at": "2023-01-04T20:00:00.000Z", "_links": { "web_path": "/foo/bar/-/packages/3", "delete_api_path": "https://gitlab.example.com/api/v4/projects/1/packages/3" }, "tags": [ { "id": 1, "package_id": 37, "name": "Some Label", "created_at": "2023-01-04T20:00:00.000Z", "updated_at": "2023-01-04T20:00:00.000Z" } ] } ] `) }) timestamp := time.Date(2023, 1, 4, 20, 0, 0, 0, time.UTC) want := []*Package{{ ID: 3, Name: "Hello/0.1@mycompany/stable", Version: "0.1", PackageType: "conan", LastDownloadedAt: ×tamp, Links: &PackageLinks{ WebPath: "/foo/bar/-/packages/3", DeleteAPIPath: "https://gitlab.example.com/api/v4/projects/1/packages/3", }, Tags: []PackageTag{ { ID: 1, PackageID: 37, Name: "Some Label", CreatedAt: ×tamp, UpdatedAt: ×tamp, }, }, }} ps, resp, err := client.Packages.ListProjectPackages(3, nil) require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, want, ps) ps, resp, err = client.Packages.ListProjectPackages(3.01, nil) require.EqualError(t, err, "invalid ID type 3.01, the ID must be an int or a string") require.Nil(t, resp) require.Nil(t, ps) ps, resp, err = client.Packages.ListProjectPackages(3, nil, errorOption) require.EqualError(t, err, "RequestOptionFunc returns an error") require.Nil(t, resp) require.Nil(t, ps) ps, resp, err = client.Packages.ListProjectPackages(5, nil) require.Error(t, err) require.Nil(t, ps) require.Equal(t, http.StatusNotFound, resp.StatusCode) } func TestPackagesService_ListPackageFiles(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/3/packages/4/package_files", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprintf(w, ` [ { "id": 25, "package_id": 4, "file_name": "my-app-1.5-20181107.152550-1.jar", "size": 2421, "file_md5": "58e6a45a629910c6ff99145a688971ac", "file_sha1": "ebd193463d3915d7e22219f52740056dfd26cbfe", "file_sha256": "a903393463d3915d7e22219f52740056dfd26cbfeff321b" } ] `) }) want := []*PackageFile{{ ID: 25, PackageID: 4, FileName: "my-app-1.5-20181107.152550-1.jar", Size: 2421, FileMD5: "58e6a45a629910c6ff99145a688971ac", FileSHA1: "ebd193463d3915d7e22219f52740056dfd26cbfe", FileSHA256: "a903393463d3915d7e22219f52740056dfd26cbfeff321b", }} ps, resp, err := client.Packages.ListPackageFiles(3, 4, nil) require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, want, ps) ps, resp, err = client.Packages.ListPackageFiles(3.01, 4, nil) require.EqualError(t, err, "invalid ID type 3.01, the ID must be an int or a string") require.Nil(t, resp) require.Nil(t, ps) ps, resp, err = client.Packages.ListPackageFiles(3, 4, nil, errorOption) require.EqualError(t, err, "RequestOptionFunc returns an error") require.Nil(t, resp) require.Nil(t, ps) ps, resp, err = client.Packages.ListPackageFiles(5, 4, nil) require.Error(t, err) require.Nil(t, ps) require.Equal(t, http.StatusNotFound, resp.StatusCode) } func TestPackagesService_DeleteProjectPackage(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/3/packages/4", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodDelete) }) resp, err := client.Packages.DeleteProjectPackage(3, 4, nil) require.NoError(t, err) require.NotNil(t, resp) resp, err = client.Packages.DeleteProjectPackage(3.01, 4, nil) require.EqualError(t, err, "invalid ID type 3.01, the ID must be an int or a string") require.Nil(t, resp) resp, err = client.Packages.DeleteProjectPackage(3, 4, nil, errorOption) require.EqualError(t, err, "RequestOptionFunc returns an error") require.Nil(t, resp) resp, err = client.Packages.DeleteProjectPackage(5, 4, nil) require.Error(t, err) require.Equal(t, http.StatusNotFound, resp.StatusCode) } golang-gitlab-gitlab-org-api-client-go-0.123.0/pages.go000066400000000000000000000072531475761473200225140ustar00rootroot00000000000000// // Copyright 2021, Sander van Harmelen // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "fmt" "net/http" "time" ) type PagesService struct { client *Client } // Pages represents the Pages of a project. // // GitLab API docs: https://docs.gitlab.com/ee/api/pages.html type Pages struct { URL string `json:"url"` IsUniqueDomainEnabled bool `json:"is_unique_domain_enabled"` ForceHTTPS bool `json:"force_https"` Deployments []*PagesDeployment `json:"deployments"` } // PagesDeployment represents a Pages deployment. // // GitLab API docs: https://docs.gitlab.com/ee/api/pages.html type PagesDeployment struct { CreatedAt time.Time `json:"created_at"` URL string `json:"url"` PathPrefix string `json:"path_prefix"` RootDirectory string `json:"root_directory"` } // UnpublishPages unpublished pages. The user must have admin privileges. // // GitLab API docs: // https://docs.gitlab.com/ee/api/pages.html#unpublish-pages func (s *PagesService) UnpublishPages(gid interface{}, options ...RequestOptionFunc) (*Response, error) { page, err := parseID(gid) if err != nil { return nil, err } u := fmt.Sprintf("projects/%s/pages", PathEscape(page)) req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } // GetPages lists Pages settings for a project. The user must have at least // maintainer privileges. // // GitLab API Docs: // https://docs.gitlab.com/ee/api/pages.html#get-pages-settings-for-a-project func (s *PagesService) GetPages(gid interface{}, options ...RequestOptionFunc) (*Pages, *Response, error) { project, err := parseID(gid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/pages", PathEscape(project)) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } p := new(Pages) resp, err := s.client.Do(req, p) if err != nil { return nil, resp, err } return p, resp, nil } // UpdatePages represents the available UpdatePages() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/pages.html#update-pages-settings-for-a-project type UpdatePagesOptions struct { PagesUniqueDomainEnabled *bool `url:"pages_unique_domain_enabled,omitempty" json:"pages_unique_domain_enabled,omitempty"` PagesHTTPSOnly *bool `url:"pages_https_only,omitempty" json:"pages_https_only,omitempty"` } // UpdatePages updates Pages settings for a project. The user must have // administrator privileges. // // GitLab API Docs: // https://docs.gitlab.com/ee/api/pages.html#update-pages-settings-for-a-project func (s *PagesService) UpdatePages(pid interface{}, opt UpdatePagesOptions, options ...RequestOptionFunc) (*Pages, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/pages", PathEscape(project)) req, err := s.client.NewRequest(http.MethodPatch, u, opt, options) if err != nil { return nil, nil, err } p := new(Pages) resp, err := s.client.Do(req, p) if err != nil { return nil, resp, err } return p, resp, nil } golang-gitlab-gitlab-org-api-client-go-0.123.0/pages_domains.go000066400000000000000000000152361475761473200242260ustar00rootroot00000000000000// // Copyright 2021, Sander van Harmelen // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "fmt" "net/http" "time" ) // PagesDomainsService handles communication with the pages domains // related methods of the GitLab API. // // GitLab API docs: https://docs.gitlab.com/ee/api/pages_domains.html type PagesDomainsService struct { client *Client } // PagesDomain represents a pages domain. // // GitLab API docs: https://docs.gitlab.com/ee/api/pages_domains.html type PagesDomain struct { Domain string `json:"domain"` AutoSslEnabled bool `json:"auto_ssl_enabled"` URL string `json:"url"` ProjectID int `json:"project_id"` Verified bool `json:"verified"` VerificationCode string `json:"verification_code"` EnabledUntil *time.Time `json:"enabled_until"` Certificate struct { Subject string `json:"subject"` Expired bool `json:"expired"` Expiration *time.Time `json:"expiration"` Certificate string `json:"certificate"` CertificateText string `json:"certificate_text"` } `json:"certificate"` } // ListPagesDomainsOptions represents the available ListPagesDomains() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/pages_domains.html#list-pages-domains type ListPagesDomainsOptions ListOptions // ListPagesDomains gets a list of project pages domains. // // GitLab API docs: // https://docs.gitlab.com/ee/api/pages_domains.html#list-pages-domains func (s *PagesDomainsService) ListPagesDomains(pid interface{}, opt *ListPagesDomainsOptions, options ...RequestOptionFunc) ([]*PagesDomain, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/pages/domains", PathEscape(project)) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } var pd []*PagesDomain resp, err := s.client.Do(req, &pd) if err != nil { return nil, resp, err } return pd, resp, nil } // ListAllPagesDomains gets a list of all pages domains. // // GitLab API docs: // https://docs.gitlab.com/ee/api/pages_domains.html#list-all-pages-domains func (s *PagesDomainsService) ListAllPagesDomains(options ...RequestOptionFunc) ([]*PagesDomain, *Response, error) { req, err := s.client.NewRequest(http.MethodGet, "pages/domains", nil, options) if err != nil { return nil, nil, err } var pd []*PagesDomain resp, err := s.client.Do(req, &pd) if err != nil { return nil, resp, err } return pd, resp, nil } // GetPagesDomain get a specific pages domain for a project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/pages_domains.html#single-pages-domain func (s *PagesDomainsService) GetPagesDomain(pid interface{}, domain string, options ...RequestOptionFunc) (*PagesDomain, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/pages/domains/%s", PathEscape(project), domain) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } pd := new(PagesDomain) resp, err := s.client.Do(req, pd) if err != nil { return nil, resp, err } return pd, resp, nil } // CreatePagesDomainOptions represents the available CreatePagesDomain() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/pages_domains.html#create-new-pages-domain type CreatePagesDomainOptions struct { Domain *string `url:"domain,omitempty" json:"domain,omitempty"` AutoSslEnabled *bool `url:"auto_ssl_enabled,omitempty" json:"auto_ssl_enabled,omitempty"` Certificate *string `url:"certificate,omitempty" json:"certificate,omitempty"` Key *string `url:"key,omitempty" json:"key,omitempty"` } // CreatePagesDomain creates a new project pages domain. // // GitLab API docs: // https://docs.gitlab.com/ee/api/pages_domains.html#create-new-pages-domain func (s *PagesDomainsService) CreatePagesDomain(pid interface{}, opt *CreatePagesDomainOptions, options ...RequestOptionFunc) (*PagesDomain, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/pages/domains", PathEscape(project)) req, err := s.client.NewRequest(http.MethodPost, u, opt, options) if err != nil { return nil, nil, err } pd := new(PagesDomain) resp, err := s.client.Do(req, pd) if err != nil { return nil, resp, err } return pd, resp, nil } // UpdatePagesDomainOptions represents the available UpdatePagesDomain() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/pages_domains.html#update-pages-domain type UpdatePagesDomainOptions struct { AutoSslEnabled *bool `url:"auto_ssl_enabled,omitempty" json:"auto_ssl_enabled,omitempty"` Certificate *string `url:"certificate,omitempty" json:"certificate,omitempty"` Key *string `url:"key,omitempty" json:"key,omitempty"` } // UpdatePagesDomain updates an existing project pages domain. // // GitLab API docs: // https://docs.gitlab.com/ee/api/pages_domains.html#update-pages-domain func (s *PagesDomainsService) UpdatePagesDomain(pid interface{}, domain string, opt *UpdatePagesDomainOptions, options ...RequestOptionFunc) (*PagesDomain, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/pages/domains/%s", PathEscape(project), domain) req, err := s.client.NewRequest(http.MethodPut, u, opt, options) if err != nil { return nil, nil, err } pd := new(PagesDomain) resp, err := s.client.Do(req, pd) if err != nil { return nil, resp, err } return pd, resp, nil } // DeletePagesDomain deletes an existing prject pages domain. // // GitLab API docs: // https://docs.gitlab.com/ee/api/pages_domains.html#delete-pages-domain func (s *PagesDomainsService) DeletePagesDomain(pid interface{}, domain string, options ...RequestOptionFunc) (*Response, error) { project, err := parseID(pid) if err != nil { return nil, err } u := fmt.Sprintf("projects/%s/pages/domains/%s", PathEscape(project), domain) req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } golang-gitlab-gitlab-org-api-client-go-0.123.0/pages_domains_test.go000066400000000000000000000264731475761473200252720ustar00rootroot00000000000000package gitlab import ( "fmt" "net/http" "testing" "time" "github.com/stretchr/testify/require" ) func TestPagesDomainsService_ListPagesDomains(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/5/pages/domains", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprintf(w, ` [ { "domain": "ssl.domain.example", "url": "https://ssl.domain.example", "auto_ssl_enabled": false, "certificate": { "subject": "/O=Example, Inc./OU=Example Origin CA/CN=Example Origin Certificate", "expired": false, "certificate": "-----BEGIN CERTIFICATE-----\n … \n-----END CERTIFICATE-----", "certificate_text": "Certificate:\n … \n" } } ] `) }) want := []*PagesDomain{{ Domain: "ssl.domain.example", AutoSslEnabled: false, URL: "https://ssl.domain.example", ProjectID: 0, Verified: false, VerificationCode: "", EnabledUntil: nil, Certificate: struct { Subject string `json:"subject"` Expired bool `json:"expired"` Expiration *time.Time `json:"expiration"` Certificate string `json:"certificate"` CertificateText string `json:"certificate_text"` }{ Expired: false, Expiration: nil, Subject: "/O=Example, Inc./OU=Example Origin CA/CN=Example Origin Certificate", Certificate: "-----BEGIN CERTIFICATE-----\n … \n-----END CERTIFICATE-----", CertificateText: "Certificate:\n … \n", }, }} pds, resp, err := client.PagesDomains.ListPagesDomains(5, nil) require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, want, pds) pds, resp, err = client.PagesDomains.ListPagesDomains(5.01, nil) require.EqualError(t, err, "invalid ID type 5.01, the ID must be an int or a string") require.Nil(t, resp) require.Nil(t, pds) pds, resp, err = client.PagesDomains.ListPagesDomains(5, nil, errorOption) require.EqualError(t, err, "RequestOptionFunc returns an error") require.Nil(t, resp) require.Nil(t, pds) pds, resp, err = client.PagesDomains.ListPagesDomains(7, nil) require.Error(t, err) require.Nil(t, pds) require.Equal(t, http.StatusNotFound, resp.StatusCode) } func TestPagesDomainsService_ListAllPagesDomains(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/pages/domains", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprintf(w, ` [ { "domain": "ssl.domain.example", "url": "https://ssl.domain.example", "project_id": 1337, "auto_ssl_enabled": false, "certificate": { "expired": false } } ] `) }) want := []*PagesDomain{{ Domain: "ssl.domain.example", AutoSslEnabled: false, URL: "https://ssl.domain.example", ProjectID: 1337, Verified: false, VerificationCode: "", EnabledUntil: nil, Certificate: struct { Subject string `json:"subject"` Expired bool `json:"expired"` Expiration *time.Time `json:"expiration"` Certificate string `json:"certificate"` CertificateText string `json:"certificate_text"` }{ Expired: false, Expiration: nil, }, }} pds, resp, err := client.PagesDomains.ListAllPagesDomains(nil, nil) require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, want, pds) pds, resp, err = client.PagesDomains.ListAllPagesDomains(nil, errorOption) require.EqualError(t, err, "RequestOptionFunc returns an error") require.Nil(t, resp) require.Nil(t, pds) } func TestPagesDomainsService_ListAllPagesDomains_StatusNotFound(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/pages/domains", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) w.WriteHeader(http.StatusNotFound) }) pds, resp, err := client.PagesDomains.ListAllPagesDomains(nil) require.Error(t, err) require.Nil(t, pds) require.Equal(t, http.StatusNotFound, resp.StatusCode) } func TestPagesDomainsService_GetPagesDomain(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/5/pages/domains/www.domain.example", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprintf(w, ` { "domain": "www.domain.example", "url": "https://ssl.domain.example", "auto_ssl_enabled": false, "certificate": { "subject": "/O=Example, Inc./OU=Example Origin CA/CN=Example Origin Certificate", "expired": false, "certificate": "-----BEGIN CERTIFICATE-----\n … \n-----END CERTIFICATE-----", "certificate_text": "Certificate:\n … \n" } } `) }) want := &PagesDomain{ Domain: "www.domain.example", AutoSslEnabled: false, URL: "https://ssl.domain.example", ProjectID: 0, Verified: false, VerificationCode: "", EnabledUntil: nil, Certificate: struct { Subject string `json:"subject"` Expired bool `json:"expired"` Expiration *time.Time `json:"expiration"` Certificate string `json:"certificate"` CertificateText string `json:"certificate_text"` }{ Expired: false, Expiration: nil, Subject: "/O=Example, Inc./OU=Example Origin CA/CN=Example Origin Certificate", Certificate: "-----BEGIN CERTIFICATE-----\n … \n-----END CERTIFICATE-----", CertificateText: "Certificate:\n … \n", }, } pd, resp, err := client.PagesDomains.GetPagesDomain(5, "www.domain.example", nil) require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, want, pd) pd, resp, err = client.PagesDomains.GetPagesDomain(5.01, "www.domain.example", nil) require.EqualError(t, err, "invalid ID type 5.01, the ID must be an int or a string") require.Nil(t, resp) require.Nil(t, pd) pd, resp, err = client.PagesDomains.GetPagesDomain(5, "www.domain.example", nil, errorOption) require.EqualError(t, err, "RequestOptionFunc returns an error") require.Nil(t, resp) require.Nil(t, pd) pd, resp, err = client.PagesDomains.GetPagesDomain(7, "www.domain.example", nil) require.Error(t, err) require.Nil(t, pd) require.Equal(t, http.StatusNotFound, resp.StatusCode) } func TestPagesDomainsService_CreatePagesDomain(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/5/pages/domains", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) fmt.Fprintf(w, ` { "domain": "ssl.domain.example", "url": "https://ssl.domain.example", "auto_ssl_enabled": false, "certificate": { "subject": "/O=Example, Inc./OU=Example Origin CA/CN=Example Origin Certificate", "expired": false, "certificate": "-----BEGIN CERTIFICATE-----\n … \n-----END CERTIFICATE-----", "certificate_text": "Certificate:\n … \n" } } `) }) want := &PagesDomain{ Domain: "ssl.domain.example", AutoSslEnabled: false, URL: "https://ssl.domain.example", ProjectID: 0, Verified: false, VerificationCode: "", EnabledUntil: nil, Certificate: struct { Subject string `json:"subject"` Expired bool `json:"expired"` Expiration *time.Time `json:"expiration"` Certificate string `json:"certificate"` CertificateText string `json:"certificate_text"` }{ Expired: false, Expiration: nil, Subject: "/O=Example, Inc./OU=Example Origin CA/CN=Example Origin Certificate", Certificate: "-----BEGIN CERTIFICATE-----\n … \n-----END CERTIFICATE-----", CertificateText: "Certificate:\n … \n", }, } pd, resp, err := client.PagesDomains.CreatePagesDomain(5, nil) require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, want, pd) pd, resp, err = client.PagesDomains.CreatePagesDomain(5.01, nil) require.EqualError(t, err, "invalid ID type 5.01, the ID must be an int or a string") require.Nil(t, resp) require.Nil(t, pd) pd, resp, err = client.PagesDomains.CreatePagesDomain(5, nil, errorOption) require.EqualError(t, err, "RequestOptionFunc returns an error") require.Nil(t, resp) require.Nil(t, pd) pd, resp, err = client.PagesDomains.CreatePagesDomain(7, nil) require.Error(t, err) require.Nil(t, pd) require.Equal(t, http.StatusNotFound, resp.StatusCode) } func TestPagesDomainsService_UpdatePagesDomain(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/5/pages/domains/ssl.domain.example", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPut) fmt.Fprintf(w, ` { "domain": "ssl.domain.example", "url": "https://ssl.domain.example", "auto_ssl_enabled": false, "certificate": { "subject": "/O=Example, Inc./OU=Example Origin CA/CN=Example Origin Certificate", "expired": false, "certificate": "-----BEGIN CERTIFICATE-----\n … \n-----END CERTIFICATE-----", "certificate_text": "Certificate:\n … \n" } } `) }) want := &PagesDomain{ Domain: "ssl.domain.example", AutoSslEnabled: false, URL: "https://ssl.domain.example", ProjectID: 0, Verified: false, VerificationCode: "", EnabledUntil: nil, Certificate: struct { Subject string `json:"subject"` Expired bool `json:"expired"` Expiration *time.Time `json:"expiration"` Certificate string `json:"certificate"` CertificateText string `json:"certificate_text"` }{ Expired: false, Expiration: nil, Subject: "/O=Example, Inc./OU=Example Origin CA/CN=Example Origin Certificate", Certificate: "-----BEGIN CERTIFICATE-----\n … \n-----END CERTIFICATE-----", CertificateText: "Certificate:\n … \n", }, } pd, resp, err := client.PagesDomains.UpdatePagesDomain(5, "ssl.domain.example", nil) require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, want, pd) pd, resp, err = client.PagesDomains.UpdatePagesDomain(5.01, "ssl.domain.example", nil) require.EqualError(t, err, "invalid ID type 5.01, the ID must be an int or a string") require.Nil(t, resp) require.Nil(t, pd) pd, resp, err = client.PagesDomains.UpdatePagesDomain(5, "ssl.domain.example", nil, errorOption) require.EqualError(t, err, "RequestOptionFunc returns an error") require.Nil(t, resp) require.Nil(t, pd) pd, resp, err = client.PagesDomains.UpdatePagesDomain(7, "ssl.domain.example", nil) require.Error(t, err) require.Nil(t, pd) require.Equal(t, http.StatusNotFound, resp.StatusCode) } func TestPagesDomainsService_DeletePagesDomain(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/5/pages/domains/ssl.domain.example", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodDelete) }) resp, err := client.PagesDomains.DeletePagesDomain(5, "ssl.domain.example", nil) require.NoError(t, err) require.NotNil(t, resp) resp, err = client.PagesDomains.DeletePagesDomain(5.01, "ssl.domain.example", nil) require.EqualError(t, err, "invalid ID type 5.01, the ID must be an int or a string") require.Nil(t, resp) resp, err = client.PagesDomains.DeletePagesDomain(5, "ssl.domain.example", nil, errorOption) require.EqualError(t, err, "RequestOptionFunc returns an error") require.Nil(t, resp) resp, err = client.PagesDomains.DeletePagesDomain(7, "ssl.domain.example", nil) require.Error(t, err) require.Equal(t, http.StatusNotFound, resp.StatusCode) } golang-gitlab-gitlab-org-api-client-go-0.123.0/pages_test.go000066400000000000000000000061041475761473200235450ustar00rootroot00000000000000// // Copyright 2021, Sander van Harmelen // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "fmt" "net/http" "testing" "time" "github.com/stretchr/testify/require" ) func TestUnpublishPages(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/2/pages", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodDelete) }) _, err := client.Pages.UnpublishPages(2) if err != nil { t.Errorf("Pages.UnpublishPages returned error: %v", err) } } func TestGetPages(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/2/pages", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, ` { "url": "https://ssl.domain.example", "deployments": [ { "created_at": "2021-04-27T21:27:38.584Z", "url": "https://ssl.domain.example/", "path_prefix": "", "root_directory": null } ], "is_unique_domain_enabled": false, "force_https": false } `) }) want := &Pages{ URL: "https://ssl.domain.example", IsUniqueDomainEnabled: false, ForceHTTPS: false, Deployments: []*PagesDeployment{ { CreatedAt: time.Date(2021, time.April, 27, 21, 27, 38, 584000000, time.UTC), URL: "https://ssl.domain.example/", PathPrefix: "", RootDirectory: "", }, }, } p, resp, err := client.Pages.GetPages(2) require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, want, p) } func TestUpdatePages(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/2/pages", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPatch) fmt.Fprint(w, ` { "url": "https://ssl.domain.example", "deployments": [ { "created_at": "2021-04-27T21:27:38.584Z", "url": "https://ssl.domain.example/", "path_prefix": "", "root_directory": null } ], "is_unique_domain_enabled": true, "force_https": false } `) }) want := &Pages{ URL: "https://ssl.domain.example", IsUniqueDomainEnabled: true, ForceHTTPS: false, Deployments: []*PagesDeployment{ { CreatedAt: time.Date(2021, time.April, 27, 21, 27, 38, 584000000, time.UTC), URL: "https://ssl.domain.example/", PathPrefix: "", RootDirectory: "", }, }, } p, resp, err := client.Pages.UpdatePages(2, UpdatePagesOptions{ PagesUniqueDomainEnabled: Ptr(true), PagesHTTPSOnly: Ptr(false), }) require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, want, p) } golang-gitlab-gitlab-org-api-client-go-0.123.0/personal_access_tokens.go000066400000000000000000000173701475761473200261450ustar00rootroot00000000000000// // Copyright 2022, Ryan Glab // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "fmt" "net/http" "time" ) // PersonalAccessTokensService handles communication with the personal access // tokens related methods of the GitLab API. // // GitLab API docs: https://docs.gitlab.com/ee/api/personal_access_tokens.html type PersonalAccessTokensService struct { client *Client } // PersonalAccessToken represents a personal access token. // // GitLab API docs: https://docs.gitlab.com/ee/api/personal_access_tokens.html type PersonalAccessToken struct { ID int `json:"id"` Name string `json:"name"` Revoked bool `json:"revoked"` CreatedAt *time.Time `json:"created_at"` Scopes []string `json:"scopes"` UserID int `json:"user_id"` LastUsedAt *time.Time `json:"last_used_at,omitempty"` Active bool `json:"active"` ExpiresAt *ISOTime `json:"expires_at"` Token string `json:"token,omitempty"` } func (p PersonalAccessToken) String() string { return Stringify(p) } // ListPersonalAccessTokensOptions represents the available // ListPersonalAccessTokens() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/personal_access_tokens.html#list-personal-access-tokens type ListPersonalAccessTokensOptions struct { ListOptions CreatedAfter *ISOTime `url:"created_after,omitempty" json:"created_after,omitempty"` CreatedBefore *ISOTime `url:"created_before,omitempty" json:"created_before,omitempty"` LastUsedAfter *ISOTime `url:"last_used_after,omitempty" json:"last_used_after,omitempty"` LastUsedBefore *ISOTime `url:"last_used_before,omitempty" json:"last_used_before,omitempty"` Revoked *bool `url:"revoked,omitempty" json:"revoked,omitempty"` Search *string `url:"search,omitempty" json:"search,omitempty"` State *string `url:"state,omitempty" json:"state,omitempty"` UserID *int `url:"user_id,omitempty" json:"user_id,omitempty"` } // ListPersonalAccessTokens gets a list of all personal access tokens. // // GitLab API docs: // https://docs.gitlab.com/ee/api/personal_access_tokens.html#list-personal-access-tokens func (s *PersonalAccessTokensService) ListPersonalAccessTokens(opt *ListPersonalAccessTokensOptions, options ...RequestOptionFunc) ([]*PersonalAccessToken, *Response, error) { req, err := s.client.NewRequest(http.MethodGet, "personal_access_tokens", opt, options) 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 } // GetSinglePersonalAccessTokenByID get a single personal access token by its ID. // // GitLab API docs: // https://docs.gitlab.com/ee/api/personal_access_tokens.html#using-a-personal-access-token-id func (s *PersonalAccessTokensService) GetSinglePersonalAccessTokenByID(token int, options ...RequestOptionFunc) (*PersonalAccessToken, *Response, error) { u := fmt.Sprintf("personal_access_tokens/%d", token) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } pat := new(PersonalAccessToken) resp, err := s.client.Do(req, pat) if err != nil { return nil, resp, err } return pat, resp, nil } // GetSinglePersonalAccessToken get a single personal access token by using // passing the token in a header. // // GitLab API docs: // https://docs.gitlab.com/ee/api/personal_access_tokens.html#using-a-request-header func (s *PersonalAccessTokensService) GetSinglePersonalAccessToken(options ...RequestOptionFunc) (*PersonalAccessToken, *Response, error) { u := "personal_access_tokens/self" req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } pat := new(PersonalAccessToken) resp, err := s.client.Do(req, pat) if err != nil { return nil, resp, err } return pat, resp, nil } // RotatePersonalAccessTokenOptions represents the available RotatePersonalAccessToken() // options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/personal_access_tokens.html#rotate-a-personal-access-token type RotatePersonalAccessTokenOptions struct { ExpiresAt *ISOTime `url:"expires_at,omitempty" json:"expires_at,omitempty"` } // RotatePersonalAccessToken is a backwards-compat shim for RotatePersonalAccessTokenByID. func (s *PersonalAccessTokensService) RotatePersonalAccessToken(token int, opt *RotatePersonalAccessTokenOptions, options ...RequestOptionFunc) (*PersonalAccessToken, *Response, error) { return s.RotatePersonalAccessTokenByID(token, opt, options...) } // RotatePersonalAccessTokenByID revokes a token and returns a new token that // expires in one week per default. // // GitLab API docs: // https://docs.gitlab.com/ee/api/personal_access_tokens.html#use-a-personal-access-token-id func (s *PersonalAccessTokensService) RotatePersonalAccessTokenByID(token int, opt *RotatePersonalAccessTokenOptions, options ...RequestOptionFunc) (*PersonalAccessToken, *Response, error) { u := fmt.Sprintf("personal_access_tokens/%d/rotate", token) req, err := s.client.NewRequest(http.MethodPost, u, opt, options) if err != nil { return nil, nil, err } pat := new(PersonalAccessToken) resp, err := s.client.Do(req, pat) if err != nil { return nil, resp, err } return pat, resp, nil } // RotatePersonalAccessTokenSelf revokes the currently authenticated token // and returns a new token that expires in one week per default. // // GitLab API docs: // https://docs.gitlab.com/ee/api/personal_access_tokens.html#use-a-request-header func (s *PersonalAccessTokensService) RotatePersonalAccessTokenSelf(opt *RotatePersonalAccessTokenOptions, options ...RequestOptionFunc) (*PersonalAccessToken, *Response, error) { u := "personal_access_tokens/self/rotate" req, err := s.client.NewRequest(http.MethodPost, u, opt, options) if err != nil { return nil, nil, err } pat := new(PersonalAccessToken) resp, err := s.client.Do(req, pat) if err != nil { return nil, resp, err } return pat, resp, nil } // RevokePersonalAccessToken is a backwards-compat shim for RevokePersonalAccessTokenByID. func (s *PersonalAccessTokensService) RevokePersonalAccessToken(token int, options ...RequestOptionFunc) (*Response, error) { return s.RevokePersonalAccessTokenByID(token, options...) } // RevokePersonalAccessTokenByID revokes a personal access token by its ID. // // GitLab API docs: // https://docs.gitlab.com/ee/api/personal_access_tokens.html#using-a-personal-access-token-id-1 func (s *PersonalAccessTokensService) RevokePersonalAccessTokenByID(token int, options ...RequestOptionFunc) (*Response, error) { u := fmt.Sprintf("personal_access_tokens/%d", token) req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } // RevokePersonalAccessTokenSelf revokes the currently authenticated // personal access token. // // GitLab API docs: // https://docs.gitlab.com/ee/api/personal_access_tokens.html#using-a-request-header-1 func (s *PersonalAccessTokensService) RevokePersonalAccessTokenSelf(options ...RequestOptionFunc) (*Response, error) { u := "personal_access_tokens/self" req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } golang-gitlab-gitlab-org-api-client-go-0.123.0/personal_access_tokens_test.go000066400000000000000000000265771475761473200272150ustar00rootroot00000000000000// // Copyright 2022, Ryan Glab // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "net/http" "reflect" "testing" "time" ) func TestListPersonalAccessTokensWithUserFilter(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/personal_access_tokens", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) mustWriteHTTPResponse(t, w, "testdata/list_personal_access_tokens_with_user_filter.json") }) personalAccessTokens, _, err := client.PersonalAccessTokens.ListPersonalAccessTokens( &ListPersonalAccessTokensOptions{UserID: Ptr(1), ListOptions: ListOptions{Page: 1, PerPage: 10}}, ) if err != nil { t.Errorf("PersonalAccessTokens.ListPersonalAccessTokens returned error: %v", err) } createdAt1, err := time.Parse(time.RFC3339, "2020-02-20T14:58:56.238Z") if err != nil { t.Errorf("PersonalAccessTokens.ListPersonalAccessTokens returned error: %v", err) } lastUsedAt1, err := time.Parse(time.RFC3339, "2021-04-20T16:31:39.105Z") if err != nil { t.Errorf("PersonalAccessTokens.ListPersonalAccessTokens returned error: %v", err) } expiresAt1 := ISOTime(time.Date(2022, time.March, 21, 0, 0, 0, 0, time.UTC)) createdAt2, err := time.Parse(time.RFC3339, "2022-03-20T03:56:18.968Z") if err != nil { t.Errorf("PersonalAccessTokens.ListPersonalAccessTokens returned error: %v", err) } expiresAt2 := ISOTime(time.Date(2022, time.March, 20, 0, 0, 0, 0, time.UTC)) want := []*PersonalAccessToken{ { ID: 1, Name: "test 1", Revoked: true, CreatedAt: &createdAt1, Scopes: []string{"api"}, UserID: 1, LastUsedAt: &lastUsedAt1, Active: false, ExpiresAt: &expiresAt1, }, { ID: 2, Name: "test 2", Revoked: false, CreatedAt: &createdAt2, Scopes: []string{"api", "read_user"}, UserID: 1, LastUsedAt: nil, Active: false, ExpiresAt: &expiresAt2, }, } if !reflect.DeepEqual(want, personalAccessTokens) { t.Errorf( "PersonalAccessTokens.ListPersonalAccessTokens returned %+v, want %+v", personalAccessTokens, want, ) } } func TestListPersonalAccessTokensNoUserFilter(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/personal_access_tokens", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) mustWriteHTTPResponse(t, w, "testdata/list_personal_access_tokens_without_user_filter.json") }) personalAccessTokens, _, err := client.PersonalAccessTokens.ListPersonalAccessTokens( &ListPersonalAccessTokensOptions{ListOptions: ListOptions{Page: 1, PerPage: 10}}, ) if err != nil { t.Errorf("PersonalAccessTokens.ListPersonalAccessTokens returned error: %v", err) } createdAt1, err := time.Parse(time.RFC3339, "2020-02-20T14:58:56.238Z") if err != nil { t.Errorf("PersonalAccessTokens.ListPersonalAccessTokens returned error: %v", err) } lastUsedAt1, err := time.Parse(time.RFC3339, "2021-04-20T16:31:39.105Z") if err != nil { t.Errorf("PersonalAccessTokens.ListPersonalAccessTokens returned error: %v", err) } expiresAt1 := ISOTime(time.Date(2022, time.March, 21, 0, 0, 0, 0, time.UTC)) createdAt2, err := time.Parse(time.RFC3339, "2022-03-20T03:56:18.968Z") if err != nil { t.Errorf("PersonalAccessTokens.ListPersonalAccessTokens returned error: %v", err) } expiresAt2 := ISOTime(time.Date(2022, time.March, 20, 0, 0, 0, 0, time.UTC)) want := []*PersonalAccessToken{ { ID: 1, Name: "test 1", Revoked: true, CreatedAt: &createdAt1, Scopes: []string{"api"}, UserID: 1, LastUsedAt: &lastUsedAt1, Active: false, ExpiresAt: &expiresAt1, }, { ID: 2, Name: "test 2", Revoked: false, CreatedAt: &createdAt2, Scopes: []string{"api", "read_user"}, UserID: 2, LastUsedAt: nil, Active: false, ExpiresAt: &expiresAt2, }, } if !reflect.DeepEqual(want, personalAccessTokens) { t.Errorf( "PersonalAccessTokens.ListPersonalAccessTokens returned %+v, want %+v", personalAccessTokens, want, ) } } func TestGetSinglePersonalAccessTokenByID(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/personal_access_tokens/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) mustWriteHTTPResponse(t, w, "testdata/list_personal_access_tokens_single.json") }) token, _, err := client.PersonalAccessTokens.GetSinglePersonalAccessTokenByID(1) if err != nil { t.Errorf("PersonalAccessTokens.RevokePersonalAccessToken returned error: %v", err) } createdAt, err := time.Parse(time.RFC3339, "2020-07-23T14:31:47.729Z") if err != nil { t.Errorf("PersonalAccessTokens.ListPersonalAccessTokens returned error: %v", err) } lastUsedAt, err := time.Parse(time.RFC3339, "2021-10-06T17:58:37.550Z") if err != nil { t.Errorf("PersonalAccessTokens.ListPersonalAccessTokens returned error: %v", err) } want := &PersonalAccessToken{ ID: 1, Name: "Test Token", Revoked: false, CreatedAt: &createdAt, Scopes: []string{"api"}, UserID: 1, LastUsedAt: &lastUsedAt, Active: true, } if !reflect.DeepEqual(want, token) { t.Errorf("PersonalAccessTokens.ListPersonalAccessTokens returned %+v, want %+v", token, want) } } func TestGetSinglePersonalAccessToken(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/personal_access_tokens/self", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) mustWriteHTTPResponse(t, w, "testdata/list_personal_access_tokens_single.json") }) token, _, err := client.PersonalAccessTokens.GetSinglePersonalAccessToken() if err != nil { t.Errorf("PersonalAccessTokens.RevokePersonalAccessToken returned error: %v", err) } createdAt, err := time.Parse(time.RFC3339, "2020-07-23T14:31:47.729Z") if err != nil { t.Errorf("PersonalAccessTokens.ListPersonalAccessTokens returned error: %v", err) } lastUsedAt, err := time.Parse(time.RFC3339, "2021-10-06T17:58:37.550Z") if err != nil { t.Errorf("PersonalAccessTokens.ListPersonalAccessTokens returned error: %v", err) } want := &PersonalAccessToken{ ID: 1, Name: "Test Token", Revoked: false, CreatedAt: &createdAt, Scopes: []string{"api"}, UserID: 1, LastUsedAt: &lastUsedAt, Active: true, } if !reflect.DeepEqual(want, token) { t.Errorf("PersonalAccessTokens.ListPersonalAccessTokens returned %+v, want %+v", token, want) } } func TestRotatePersonalAccessToken(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/personal_access_tokens/42/rotate", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) mustWriteHTTPResponse(t, w, "testdata/rotate_personal_access_token.json") }) createdAt, _ := time.Parse(time.RFC3339, "2023-08-01T15:00:00.000Z") expiration := ISOTime(time.Date(2023, time.August, 15, 0, 0, 0, 0, time.UTC)) opts := &RotatePersonalAccessTokenOptions{ExpiresAt: &expiration} rotatedToken, _, err := client.PersonalAccessTokens.RotatePersonalAccessToken(42, opts) if err != nil { t.Errorf("PersonalAccessTokens.RotatePersonalAccessToken returned error: %v", err) } want := &PersonalAccessToken{ ID: 42, UserID: 1337, Name: "Rotated Token", Scopes: []string{"api"}, ExpiresAt: &expiration, CreatedAt: &createdAt, Active: true, Revoked: false, Token: "s3cr3t", } if !reflect.DeepEqual(want, rotatedToken) { t.Errorf( "PersonalAccessTokens.RotatePersonalAccessToken returned %+v, want %+v", rotatedToken, want, ) } } func TestRotatePersonalAccessTokenByID(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/personal_access_tokens/42/rotate", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) mustWriteHTTPResponse(t, w, "testdata/rotate_personal_access_token.json") }) createdAt, _ := time.Parse(time.RFC3339, "2023-08-01T15:00:00.000Z") expiration := ISOTime(time.Date(2023, time.August, 15, 0, 0, 0, 0, time.UTC)) opts := &RotatePersonalAccessTokenOptions{ExpiresAt: &expiration} rotatedToken, _, err := client.PersonalAccessTokens.RotatePersonalAccessTokenByID(42, opts) if err != nil { t.Errorf("PersonalAccessTokens.RotatePersonalAccessTokenByID returned error: %v", err) } want := &PersonalAccessToken{ ID: 42, UserID: 1337, Name: "Rotated Token", Scopes: []string{"api"}, ExpiresAt: &expiration, CreatedAt: &createdAt, Active: true, Revoked: false, Token: "s3cr3t", } if !reflect.DeepEqual(want, rotatedToken) { t.Errorf( "PersonalAccessTokens.RotatePersonalAccessTokenByID returned %+v, want %+v", rotatedToken, want, ) } } func TestRotatePersonalAccessTokenSelf(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/personal_access_tokens/self/rotate", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) mustWriteHTTPResponse(t, w, "testdata/rotate_personal_access_token.json") }) createdAt, _ := time.Parse(time.RFC3339, "2023-08-01T15:00:00.000Z") expiration := ISOTime(time.Date(2023, time.August, 15, 0, 0, 0, 0, time.UTC)) opts := &RotatePersonalAccessTokenOptions{ExpiresAt: &expiration} rotatedToken, _, err := client.PersonalAccessTokens.RotatePersonalAccessTokenSelf(opts) if err != nil { t.Errorf("PersonalAccessTokens.RotatePersonalAccessTokenSelf returned error: %v", err) } want := &PersonalAccessToken{ ID: 42, UserID: 1337, Name: "Rotated Token", Scopes: []string{"api"}, ExpiresAt: &expiration, CreatedAt: &createdAt, Active: true, Revoked: false, Token: "s3cr3t", } if !reflect.DeepEqual(want, rotatedToken) { t.Errorf("PersonalAccessTokens.RotatePersonalAccessTokenSelf returned %+v, want %+v", rotatedToken, want) } } func TestRevokePersonalAccessToken(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/personal_access_tokens/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodDelete) }) _, err := client.PersonalAccessTokens.RevokePersonalAccessToken(1) if err != nil { t.Errorf("PersonalAccessTokens.RevokePersonalAccessToken returned error: %v", err) } } func TestRevokePersonalAccessTokenByID(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/personal_access_tokens/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodDelete) }) _, err := client.PersonalAccessTokens.RevokePersonalAccessTokenByID(1) if err != nil { t.Errorf("PersonalAccessTokens.RevokePersonalAccessTokenByID returned error: %v", err) } } func TestRevokePersonalAccessTokenSelf(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/personal_access_tokens/self", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodDelete) }) _, err := client.PersonalAccessTokens.RevokePersonalAccessTokenSelf() if err != nil { t.Errorf("PersonalAccessTokens.RevokePersonalAccessTokenSelf returned error: %v", err) } } golang-gitlab-gitlab-org-api-client-go-0.123.0/pipeline_schedules.go000066400000000000000000000311501475761473200252520ustar00rootroot00000000000000// // Copyright 2021, Sander van Harmelen // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "fmt" "net/http" "time" ) // PipelineSchedulesService handles communication with the pipeline // schedules related methods of the GitLab API. // // GitLab API docs: https://docs.gitlab.com/ee/api/pipeline_schedules.html type PipelineSchedulesService struct { client *Client } // PipelineSchedule represents a pipeline schedule. // // GitLab API docs: // https://docs.gitlab.com/ee/api/pipeline_schedules.html type PipelineSchedule struct { ID int `json:"id"` Description string `json:"description"` Ref string `json:"ref"` Cron string `json:"cron"` CronTimezone string `json:"cron_timezone"` NextRunAt *time.Time `json:"next_run_at"` Active bool `json:"active"` CreatedAt *time.Time `json:"created_at"` UpdatedAt *time.Time `json:"updated_at"` Owner *User `json:"owner"` LastPipeline *LastPipeline `json:"last_pipeline"` Variables []*PipelineVariable `json:"variables"` } // LastPipeline represents the last pipeline ran by schedule // this will be returned only for individual schedule get operation type LastPipeline struct { ID int `json:"id"` SHA string `json:"sha"` Ref string `json:"ref"` Status string `json:"status"` WebURL string `json:"web_url"` } // ListPipelineSchedulesOptions represents the available ListPipelineTriggers() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/pipeline_schedules.html#get-all-pipeline-schedules type ListPipelineSchedulesOptions ListOptions // ListPipelineSchedules gets a list of project triggers. // // GitLab API docs: // https://docs.gitlab.com/ee/api/pipeline_schedules.html#get-all-pipeline-schedules func (s *PipelineSchedulesService) ListPipelineSchedules(pid interface{}, opt *ListPipelineSchedulesOptions, options ...RequestOptionFunc) ([]*PipelineSchedule, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/pipeline_schedules", PathEscape(project)) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } var ps []*PipelineSchedule resp, err := s.client.Do(req, &ps) if err != nil { return nil, resp, err } return ps, resp, nil } // GetPipelineSchedule gets a pipeline schedule. // // GitLab API docs: // https://docs.gitlab.com/ee/api/pipeline_schedules.html#get-a-single-pipeline-schedule func (s *PipelineSchedulesService) GetPipelineSchedule(pid interface{}, schedule int, options ...RequestOptionFunc) (*PipelineSchedule, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/pipeline_schedules/%d", PathEscape(project), schedule) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } p := new(PipelineSchedule) resp, err := s.client.Do(req, p) if err != nil { return nil, resp, err } return p, resp, nil } // ListPipelinesTriggeredByScheduleOptions represents the available // ListPipelinesTriggeredBySchedule() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/pipeline_schedules.html#get-all-pipelines-triggered-by-a-pipeline-schedule type ListPipelinesTriggeredByScheduleOptions ListOptions // ListPipelinesTriggeredBySchedule gets all pipelines triggered by a pipeline // schedule. // // GitLab API docs: // https://docs.gitlab.com/ee/api/pipeline_schedules.html#get-all-pipelines-triggered-by-a-pipeline-schedule func (s *PipelineSchedulesService) ListPipelinesTriggeredBySchedule(pid interface{}, schedule int, opt *ListPipelinesTriggeredByScheduleOptions, options ...RequestOptionFunc) ([]*Pipeline, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/pipeline_schedules/%d/pipelines", PathEscape(project), schedule) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } var p []*Pipeline resp, err := s.client.Do(req, &p) if err != nil { return nil, resp, err } return p, resp, nil } // CreatePipelineScheduleOptions represents the available // CreatePipelineSchedule() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/pipeline_schedules.html#create-a-new-pipeline-schedule type CreatePipelineScheduleOptions struct { Description *string `url:"description" json:"description"` Ref *string `url:"ref" json:"ref"` Cron *string `url:"cron" json:"cron"` CronTimezone *string `url:"cron_timezone,omitempty" json:"cron_timezone,omitempty"` Active *bool `url:"active,omitempty" json:"active,omitempty"` } // CreatePipelineSchedule creates a pipeline schedule. // // GitLab API docs: // https://docs.gitlab.com/ee/api/pipeline_schedules.html#create-a-new-pipeline-schedule func (s *PipelineSchedulesService) CreatePipelineSchedule(pid interface{}, opt *CreatePipelineScheduleOptions, options ...RequestOptionFunc) (*PipelineSchedule, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/pipeline_schedules", PathEscape(project)) req, err := s.client.NewRequest(http.MethodPost, u, opt, options) if err != nil { return nil, nil, err } p := new(PipelineSchedule) resp, err := s.client.Do(req, p) if err != nil { return nil, resp, err } return p, resp, nil } // EditPipelineScheduleOptions represents the available // EditPipelineSchedule() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/pipeline_schedules.html#edit-a-pipeline-schedule type EditPipelineScheduleOptions struct { Description *string `url:"description,omitempty" json:"description,omitempty"` Ref *string `url:"ref,omitempty" json:"ref,omitempty"` Cron *string `url:"cron,omitempty" json:"cron,omitempty"` CronTimezone *string `url:"cron_timezone,omitempty" json:"cron_timezone,omitempty"` Active *bool `url:"active,omitempty" json:"active,omitempty"` } // EditPipelineSchedule edits a pipeline schedule. // // GitLab API docs: // https://docs.gitlab.com/ee/api/pipeline_schedules.html#edit-a-pipeline-schedule func (s *PipelineSchedulesService) EditPipelineSchedule(pid interface{}, schedule int, opt *EditPipelineScheduleOptions, options ...RequestOptionFunc) (*PipelineSchedule, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/pipeline_schedules/%d", PathEscape(project), schedule) req, err := s.client.NewRequest(http.MethodPut, u, opt, options) if err != nil { return nil, nil, err } p := new(PipelineSchedule) resp, err := s.client.Do(req, p) if err != nil { return nil, resp, err } return p, resp, nil } // TakeOwnershipOfPipelineSchedule sets the owner of the specified // pipeline schedule to the user issuing the request. // // GitLab API docs: // https://docs.gitlab.com/ee/api/pipeline_schedules.html#take-ownership-of-a-pipeline-schedule func (s *PipelineSchedulesService) TakeOwnershipOfPipelineSchedule(pid interface{}, schedule int, options ...RequestOptionFunc) (*PipelineSchedule, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/pipeline_schedules/%d/take_ownership", PathEscape(project), schedule) req, err := s.client.NewRequest(http.MethodPost, u, nil, options) if err != nil { return nil, nil, err } p := new(PipelineSchedule) resp, err := s.client.Do(req, p) if err != nil { return nil, resp, err } return p, resp, nil } // DeletePipelineSchedule deletes a pipeline schedule. // // GitLab API docs: // https://docs.gitlab.com/ee/api/pipeline_schedules.html#delete-a-pipeline-schedule func (s *PipelineSchedulesService) DeletePipelineSchedule(pid interface{}, schedule int, options ...RequestOptionFunc) (*Response, error) { project, err := parseID(pid) if err != nil { return nil, err } u := fmt.Sprintf("projects/%s/pipeline_schedules/%d", PathEscape(project), schedule) req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } // RunPipelineSchedule triggers a new scheduled pipeline to run immediately. // // Gitlab API docs: // https://docs.gitlab.com/ee/api/pipeline_schedules.html#run-a-scheduled-pipeline-immediately func (s *PipelineSchedulesService) RunPipelineSchedule(pid interface{}, schedule int, options ...RequestOptionFunc) (*Response, error) { project, err := parseID(pid) if err != nil { return nil, err } u := fmt.Sprintf("projects/%s/pipeline_schedules/%d/play", PathEscape(project), schedule) req, err := s.client.NewRequest(http.MethodPost, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } // CreatePipelineScheduleVariableOptions represents the available // CreatePipelineScheduleVariable() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/pipeline_schedules.html#create-a-new-pipeline-schedule type CreatePipelineScheduleVariableOptions struct { Key *string `url:"key" json:"key"` Value *string `url:"value" json:"value"` VariableType *VariableTypeValue `url:"variable_type,omitempty" json:"variable_type,omitempty"` } // CreatePipelineScheduleVariable creates a pipeline schedule variable. // // GitLab API docs: // https://docs.gitlab.com/ee/api/pipeline_schedules.html#create-a-new-pipeline-schedule func (s *PipelineSchedulesService) CreatePipelineScheduleVariable(pid interface{}, schedule int, opt *CreatePipelineScheduleVariableOptions, options ...RequestOptionFunc) (*PipelineVariable, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/pipeline_schedules/%d/variables", PathEscape(project), schedule) req, err := s.client.NewRequest(http.MethodPost, u, opt, options) if err != nil { return nil, nil, err } p := new(PipelineVariable) resp, err := s.client.Do(req, p) if err != nil { return nil, resp, err } return p, resp, nil } // EditPipelineScheduleVariableOptions represents the available // EditPipelineScheduleVariable() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/pipeline_schedules.html#edit-a-pipeline-schedule-variable type EditPipelineScheduleVariableOptions struct { Value *string `url:"value" json:"value"` VariableType *VariableTypeValue `url:"variable_type,omitempty" json:"variable_type,omitempty"` } // EditPipelineScheduleVariable creates a pipeline schedule variable. // // GitLab API docs: // https://docs.gitlab.com/ee/api/pipeline_schedules.html#edit-a-pipeline-schedule-variable func (s *PipelineSchedulesService) EditPipelineScheduleVariable(pid interface{}, schedule int, key string, opt *EditPipelineScheduleVariableOptions, options ...RequestOptionFunc) (*PipelineVariable, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/pipeline_schedules/%d/variables/%s", PathEscape(project), schedule, key) req, err := s.client.NewRequest(http.MethodPut, u, opt, options) if err != nil { return nil, nil, err } p := new(PipelineVariable) resp, err := s.client.Do(req, p) if err != nil { return nil, resp, err } return p, resp, nil } // DeletePipelineScheduleVariable creates a pipeline schedule variable. // // GitLab API docs: // https://docs.gitlab.com/ee/api/pipeline_schedules.html#delete-a-pipeline-schedule-variable func (s *PipelineSchedulesService) DeletePipelineScheduleVariable(pid interface{}, schedule int, key string, options ...RequestOptionFunc) (*PipelineVariable, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/pipeline_schedules/%d/variables/%s", PathEscape(project), schedule, key) req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) if err != nil { return nil, nil, err } p := new(PipelineVariable) resp, err := s.client.Do(req, p) if err != nil { return nil, resp, err } return p, resp, nil } golang-gitlab-gitlab-org-api-client-go-0.123.0/pipeline_schedules_test.go000066400000000000000000000024121475761473200263100ustar00rootroot00000000000000// // Copyright 2021, Sander van Harmelen // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "fmt" "net/http" "testing" ) func TestRunPipelineSchedule(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/pipeline_schedules/1/play", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) w.WriteHeader(http.StatusCreated) fmt.Fprint(w, `{"message": "201 Created"}`) }) res, err := client.PipelineSchedules.RunPipelineSchedule(1, 1) if err != nil { t.Errorf("PipelineTriggers.RunPipelineTrigger returned error: %v", err) } if res.StatusCode != http.StatusCreated { t.Errorf("PipelineSchedules.RunPipelineSchedule returned status %v, want %v", res.StatusCode, http.StatusCreated) } } golang-gitlab-gitlab-org-api-client-go-0.123.0/pipeline_triggers.go000066400000000000000000000170221475761473200251230ustar00rootroot00000000000000// // Copyright 2021, Sander van Harmelen // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "fmt" "net/http" "time" ) // PipelineTriggersService handles Project pipeline triggers. // // GitLab API docs: // https://docs.gitlab.com/ee/api/pipeline_triggers.html type PipelineTriggersService struct { client *Client } // PipelineTrigger represents a project pipeline trigger. // // GitLab API docs: // https://docs.gitlab.com/ee/api/pipeline_triggers.html type PipelineTrigger struct { ID int `json:"id"` Description string `json:"description"` CreatedAt *time.Time `json:"created_at"` DeletedAt *time.Time `json:"deleted_at"` LastUsed *time.Time `json:"last_used"` Token string `json:"token"` UpdatedAt *time.Time `json:"updated_at"` Owner *User `json:"owner"` } // ListPipelineTriggersOptions represents the available ListPipelineTriggers() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/pipeline_triggers.html#list-project-trigger-tokens type ListPipelineTriggersOptions ListOptions // ListPipelineTriggers gets a list of project triggers. // // GitLab API docs: // https://docs.gitlab.com/ee/api/pipeline_triggers.html#list-project-trigger-tokens func (s *PipelineTriggersService) ListPipelineTriggers(pid interface{}, opt *ListPipelineTriggersOptions, options ...RequestOptionFunc) ([]*PipelineTrigger, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/triggers", PathEscape(project)) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } var pt []*PipelineTrigger resp, err := s.client.Do(req, &pt) if err != nil { return nil, resp, err } return pt, resp, nil } // GetPipelineTrigger gets a specific pipeline trigger for a project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/pipeline_triggers.html#get-trigger-token-details func (s *PipelineTriggersService) GetPipelineTrigger(pid interface{}, trigger int, options ...RequestOptionFunc) (*PipelineTrigger, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/triggers/%d", PathEscape(project), trigger) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } pt := new(PipelineTrigger) resp, err := s.client.Do(req, pt) if err != nil { return nil, resp, err } return pt, resp, nil } // AddPipelineTriggerOptions represents the available AddPipelineTrigger() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/pipeline_triggers.html#create-a-trigger-token type AddPipelineTriggerOptions struct { Description *string `url:"description,omitempty" json:"description,omitempty"` } // AddPipelineTrigger adds a pipeline trigger to a specified project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/pipeline_triggers.html#create-a-trigger-token func (s *PipelineTriggersService) AddPipelineTrigger(pid interface{}, opt *AddPipelineTriggerOptions, options ...RequestOptionFunc) (*PipelineTrigger, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/triggers", PathEscape(project)) req, err := s.client.NewRequest(http.MethodPost, u, opt, options) if err != nil { return nil, nil, err } pt := new(PipelineTrigger) resp, err := s.client.Do(req, pt) if err != nil { return nil, resp, err } return pt, resp, nil } // EditPipelineTriggerOptions represents the available EditPipelineTrigger() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/pipeline_triggers.html#update-a-project-trigger-token type EditPipelineTriggerOptions struct { Description *string `url:"description,omitempty" json:"description,omitempty"` } // EditPipelineTrigger edits a trigger for a specified project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/pipeline_triggers.html#update-a-project-trigger-token func (s *PipelineTriggersService) EditPipelineTrigger(pid interface{}, trigger int, opt *EditPipelineTriggerOptions, options ...RequestOptionFunc) (*PipelineTrigger, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/triggers/%d", PathEscape(project), trigger) req, err := s.client.NewRequest(http.MethodPut, u, opt, options) if err != nil { return nil, nil, err } pt := new(PipelineTrigger) resp, err := s.client.Do(req, pt) if err != nil { return nil, resp, err } return pt, resp, nil } // TakeOwnershipOfPipelineTrigger sets the owner of the specified // pipeline trigger to the user issuing the request. // // GitLab API docs: // https://docs.gitlab.com/ee/api/pipeline_triggers.html#take-ownership-of-a-project-trigger func (s *PipelineTriggersService) TakeOwnershipOfPipelineTrigger(pid interface{}, trigger int, options ...RequestOptionFunc) (*PipelineTrigger, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/triggers/%d/take_ownership", PathEscape(project), trigger) req, err := s.client.NewRequest(http.MethodPost, u, nil, options) if err != nil { return nil, nil, err } pt := new(PipelineTrigger) resp, err := s.client.Do(req, pt) if err != nil { return nil, resp, err } return pt, resp, nil } // DeletePipelineTrigger removes a trigger from a project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/pipeline_triggers.html#remove-a-project-trigger-token func (s *PipelineTriggersService) DeletePipelineTrigger(pid interface{}, trigger int, options ...RequestOptionFunc) (*Response, error) { project, err := parseID(pid) if err != nil { return nil, err } u := fmt.Sprintf("projects/%s/triggers/%d", PathEscape(project), trigger) req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } // RunPipelineTriggerOptions represents the available RunPipelineTrigger() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/pipeline_triggers.html#trigger-a-pipeline-with-a-token type RunPipelineTriggerOptions struct { Ref *string `url:"ref" json:"ref"` Token *string `url:"token" json:"token"` Variables map[string]string `url:"variables,omitempty" json:"variables,omitempty"` } // RunPipelineTrigger starts a trigger from a project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/pipeline_triggers.html#trigger-a-pipeline-with-a-token func (s *PipelineTriggersService) RunPipelineTrigger(pid interface{}, opt *RunPipelineTriggerOptions, options ...RequestOptionFunc) (*Pipeline, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/trigger/pipeline", PathEscape(project)) req, err := s.client.NewRequest(http.MethodPost, u, opt, options) if err != nil { return nil, nil, err } pt := new(Pipeline) resp, err := s.client.Do(req, pt) if err != nil { return nil, resp, err } return pt, resp, nil } golang-gitlab-gitlab-org-api-client-go-0.123.0/pipeline_triggers_test.go000066400000000000000000000024611475761473200261630ustar00rootroot00000000000000// // Copyright 2021, Sander van Harmelen // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "fmt" "net/http" "reflect" "testing" ) func TestRunPipeline(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/trigger/pipeline", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) fmt.Fprint(w, `{"id":1, "status":"pending"}`) }) opt := &RunPipelineTriggerOptions{Ref: Ptr("master")} pipeline, _, err := client.PipelineTriggers.RunPipelineTrigger(1, opt) if err != nil { t.Errorf("PipelineTriggers.RunPipelineTrigger returned error: %v", err) } want := &Pipeline{ID: 1, Status: "pending"} if !reflect.DeepEqual(want, pipeline) { t.Errorf("PipelineTriggers.RunPipelineTrigger returned %+v, want %+v", pipeline, want) } } golang-gitlab-gitlab-org-api-client-go-0.123.0/pipelines.go000066400000000000000000000351051475761473200234020ustar00rootroot00000000000000// // Copyright 2021, Igor Varavko // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "fmt" "net/http" "time" ) // PipelinesService handles communication with the repositories related // methods of the GitLab API. // // GitLab API docs: https://docs.gitlab.com/ee/api/pipelines.html type PipelinesService struct { client *Client } // PipelineVariable represents a pipeline variable. // // GitLab API docs: https://docs.gitlab.com/ee/api/pipelines.html type PipelineVariable struct { Key string `json:"key"` Value string `json:"value"` VariableType VariableTypeValue `json:"variable_type"` } // Pipeline represents a GitLab pipeline. // // GitLab API docs: https://docs.gitlab.com/ee/api/pipelines.html type Pipeline struct { ID int `json:"id"` IID int `json:"iid"` ProjectID int `json:"project_id"` Status string `json:"status"` Source string `json:"source"` Ref string `json:"ref"` Name string `json:"name"` SHA string `json:"sha"` BeforeSHA string `json:"before_sha"` Tag bool `json:"tag"` YamlErrors string `json:"yaml_errors"` User *BasicUser `json:"user"` UpdatedAt *time.Time `json:"updated_at"` CreatedAt *time.Time `json:"created_at"` StartedAt *time.Time `json:"started_at"` FinishedAt *time.Time `json:"finished_at"` CommittedAt *time.Time `json:"committed_at"` Duration int `json:"duration"` QueuedDuration int `json:"queued_duration"` Coverage string `json:"coverage"` WebURL string `json:"web_url"` DetailedStatus *DetailedStatus `json:"detailed_status"` } // DetailedStatus contains detailed information about the status of a pipeline. type DetailedStatus struct { Icon string `json:"icon"` Text string `json:"text"` Label string `json:"label"` Group string `json:"group"` Tooltip string `json:"tooltip"` HasDetails bool `json:"has_details"` DetailsPath string `json:"details_path"` Illustration struct { Image string `json:"image"` } `json:"illustration"` Favicon string `json:"favicon"` } func (p Pipeline) String() string { return Stringify(p) } // PipelineTestReport contains a detailed report of a test run. type PipelineTestReport struct { TotalTime float64 `json:"total_time"` TotalCount int `json:"total_count"` SuccessCount int `json:"success_count"` FailedCount int `json:"failed_count"` SkippedCount int `json:"skipped_count"` ErrorCount int `json:"error_count"` TestSuites []*PipelineTestSuites `json:"test_suites"` } // PipelineTestSuites contains test suites results. type PipelineTestSuites struct { Name string `json:"name"` TotalTime float64 `json:"total_time"` TotalCount int `json:"total_count"` SuccessCount int `json:"success_count"` FailedCount int `json:"failed_count"` SkippedCount int `json:"skipped_count"` ErrorCount int `json:"error_count"` TestCases []*PipelineTestCases `json:"test_cases"` } // PipelineTestCases contains test cases details. type PipelineTestCases struct { Status string `json:"status"` Name string `json:"name"` Classname string `json:"classname"` File string `json:"file"` ExecutionTime float64 `json:"execution_time"` SystemOutput interface{} `json:"system_output"` StackTrace string `json:"stack_trace"` AttachmentURL string `json:"attachment_url"` RecentFailures *RecentFailures `json:"recent_failures"` } // RecentFailures contains failures count for the project's default branch. type RecentFailures struct { Count int `json:"count"` BaseBranch string `json:"base_branch"` } func (p PipelineTestReport) String() string { return Stringify(p) } // PipelineInfo shows the basic entities of a pipeline, mostly used as fields // on other assets, like Commit. type PipelineInfo struct { ID int `json:"id"` IID int `json:"iid"` ProjectID int `json:"project_id"` Status string `json:"status"` Source string `json:"source"` Ref string `json:"ref"` SHA string `json:"sha"` Name string `json:"name"` WebURL string `json:"web_url"` UpdatedAt *time.Time `json:"updated_at"` CreatedAt *time.Time `json:"created_at"` } func (p PipelineInfo) String() string { return Stringify(p) } // ListProjectPipelinesOptions represents the available ListProjectPipelines() // options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/pipelines.html#list-project-pipelines type ListProjectPipelinesOptions struct { ListOptions Scope *string `url:"scope,omitempty" json:"scope,omitempty"` Status *BuildStateValue `url:"status,omitempty" json:"status,omitempty"` Source *string `url:"source,omitempty" json:"source,omitempty"` Ref *string `url:"ref,omitempty" json:"ref,omitempty"` SHA *string `url:"sha,omitempty" json:"sha,omitempty"` YamlErrors *bool `url:"yaml_errors,omitempty" json:"yaml_errors,omitempty"` Name *string `url:"name,omitempty" json:"name,omitempty"` Username *string `url:"username,omitempty" json:"username,omitempty"` UpdatedAfter *time.Time `url:"updated_after,omitempty" json:"updated_after,omitempty"` UpdatedBefore *time.Time `url:"updated_before,omitempty" json:"updated_before,omitempty"` OrderBy *string `url:"order_by,omitempty" json:"order_by,omitempty"` Sort *string `url:"sort,omitempty" json:"sort,omitempty"` } // ListProjectPipelines gets a list of project piplines. // // GitLab API docs: // https://docs.gitlab.com/ee/api/pipelines.html#list-project-pipelines func (s *PipelinesService) ListProjectPipelines(pid interface{}, opt *ListProjectPipelinesOptions, options ...RequestOptionFunc) ([]*PipelineInfo, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/pipelines", PathEscape(project)) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } var p []*PipelineInfo resp, err := s.client.Do(req, &p) if err != nil { return nil, resp, err } return p, resp, nil } // GetPipeline gets a single project pipeline. // // GitLab API docs: // https://docs.gitlab.com/ee/api/pipelines.html#get-a-single-pipeline func (s *PipelinesService) GetPipeline(pid interface{}, pipeline int, options ...RequestOptionFunc) (*Pipeline, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/pipelines/%d", PathEscape(project), pipeline) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } p := new(Pipeline) resp, err := s.client.Do(req, p) if err != nil { return nil, resp, err } return p, resp, nil } // GetPipelineVariables gets the variables of a single project pipeline. // // GitLab API docs: // https://docs.gitlab.com/ee/api/pipelines.html#get-variables-of-a-pipeline func (s *PipelinesService) GetPipelineVariables(pid interface{}, pipeline int, options ...RequestOptionFunc) ([]*PipelineVariable, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/pipelines/%d/variables", PathEscape(project), pipeline) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } var p []*PipelineVariable resp, err := s.client.Do(req, &p) if err != nil { return nil, resp, err } return p, resp, nil } // GetPipelineTestReport gets the test report of a single project pipeline. // // GitLab API docs: // https://docs.gitlab.com/ee/api/pipelines.html#get-a-pipelines-test-report func (s *PipelinesService) GetPipelineTestReport(pid interface{}, pipeline int, options ...RequestOptionFunc) (*PipelineTestReport, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/pipelines/%d/test_report", PathEscape(project), pipeline) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } p := new(PipelineTestReport) resp, err := s.client.Do(req, p) if err != nil { return nil, resp, err } return p, resp, nil } // GetLatestPipelineOptions represents the available GetLatestPipeline() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/pipelines.html#get-the-latest-pipeline type GetLatestPipelineOptions struct { Ref *string `url:"ref,omitempty" json:"ref,omitempty"` } // GetLatestPipeline gets the latest pipeline for a specific ref in a project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/pipelines.html#get-the-latest-pipeline func (s *PipelinesService) GetLatestPipeline(pid interface{}, opt *GetLatestPipelineOptions, options ...RequestOptionFunc) (*Pipeline, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/pipelines/latest", PathEscape(project)) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } p := new(Pipeline) resp, err := s.client.Do(req, p) if err != nil { return nil, resp, err } return p, resp, nil } // CreatePipelineOptions represents the available CreatePipeline() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/pipelines.html#create-a-new-pipeline type CreatePipelineOptions struct { Ref *string `url:"ref" json:"ref"` Variables *[]*PipelineVariableOptions `url:"variables,omitempty" json:"variables,omitempty"` } // PipelineVariable represents a pipeline variable. // // GitLab API docs: https://docs.gitlab.com/ee/api/pipelines.html#create-a-new-pipeline type PipelineVariableOptions struct { Key *string `url:"key,omitempty" json:"key,omitempty"` Value *string `url:"value,omitempty" json:"value,omitempty"` VariableType *VariableTypeValue `url:"variable_type,omitempty" json:"variable_type,omitempty"` } // CreatePipeline creates a new project pipeline. // // GitLab API docs: // https://docs.gitlab.com/ee/api/pipelines.html#create-a-new-pipeline func (s *PipelinesService) CreatePipeline(pid interface{}, opt *CreatePipelineOptions, options ...RequestOptionFunc) (*Pipeline, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/pipeline", PathEscape(project)) req, err := s.client.NewRequest(http.MethodPost, u, opt, options) if err != nil { return nil, nil, err } p := new(Pipeline) resp, err := s.client.Do(req, p) if err != nil { return nil, resp, err } return p, resp, nil } // RetryPipelineBuild retries failed builds in a pipeline. // // GitLab API docs: // https://docs.gitlab.com/ee/api/pipelines.html#retry-jobs-in-a-pipeline func (s *PipelinesService) RetryPipelineBuild(pid interface{}, pipeline int, options ...RequestOptionFunc) (*Pipeline, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/pipelines/%d/retry", PathEscape(project), pipeline) req, err := s.client.NewRequest(http.MethodPost, u, nil, options) if err != nil { return nil, nil, err } p := new(Pipeline) resp, err := s.client.Do(req, p) if err != nil { return nil, resp, err } return p, resp, nil } // CancelPipelineBuild cancels a pipeline builds. // // GitLab API docs: // https://docs.gitlab.com/ee/api/pipelines.html#cancel-a-pipelines-jobs func (s *PipelinesService) CancelPipelineBuild(pid interface{}, pipeline int, options ...RequestOptionFunc) (*Pipeline, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/pipelines/%d/cancel", PathEscape(project), pipeline) req, err := s.client.NewRequest(http.MethodPost, u, nil, options) if err != nil { return nil, nil, err } p := new(Pipeline) resp, err := s.client.Do(req, p) if err != nil { return nil, resp, err } return p, resp, nil } // DeletePipeline deletes an existing pipeline. // // GitLab API docs: // https://docs.gitlab.com/ee/api/pipelines.html#delete-a-pipeline func (s *PipelinesService) DeletePipeline(pid interface{}, pipeline int, options ...RequestOptionFunc) (*Response, error) { project, err := parseID(pid) if err != nil { return nil, err } u := fmt.Sprintf("projects/%s/pipelines/%d", PathEscape(project), pipeline) req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } // UpdatePipelineMetadataOptions represents the available UpdatePipelineMetadata() // options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/pipelines.html#update-pipeline-metadata type UpdatePipelineMetadataOptions struct { Name *string `url:"name,omitempty" json:"name,omitempty"` } // UpdatePipelineMetadata You can update the metadata of a pipeline. The metadata // contains the name of the pipeline. // // GitLab API docs: // https://docs.gitlab.com/ee/api/pipelines.html#update-pipeline-metadata func (s *PipelinesService) UpdatePipelineMetadata(pid interface{}, pipeline int, opt *UpdatePipelineMetadataOptions, options ...RequestOptionFunc) (*Pipeline, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/pipelines/%d/metadata", PathEscape(project), pipeline) req, err := s.client.NewRequest(http.MethodPut, u, opt, options) if err != nil { return nil, nil, err } p := new(Pipeline) resp, err := s.client.Do(req, p) if err != nil { return nil, resp, err } return p, resp, nil } golang-gitlab-gitlab-org-api-client-go-0.123.0/pipelines_test.go000066400000000000000000000211771475761473200244450ustar00rootroot00000000000000// // Copyright 2021, Sander van Harmelen // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "fmt" "net/http" "reflect" "testing" "github.com/stretchr/testify/assert" ) func TestListProjectPipelines(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/pipelines", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, `[{"id":1, "name":"test"},{"id":2}]`) }) opt := &ListProjectPipelinesOptions{Ref: Ptr("master")} piplines, _, err := client.Pipelines.ListProjectPipelines(1, opt) if err != nil { t.Errorf("Pipelines.ListProjectPipelines returned error: %v", err) } want := []*PipelineInfo{{ID: 1, Name: "test"}, {ID: 2}} if !reflect.DeepEqual(want, piplines) { t.Errorf("Pipelines.ListProjectPipelines returned %+v, want %+v", piplines, want) } } func TestGetPipeline(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/pipelines/5949167", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, `{"id":1,"status":"success"}`) }) pipeline, _, err := client.Pipelines.GetPipeline(1, 5949167) if err != nil { t.Errorf("Pipelines.GetPipeline returned error: %v", err) } want := &Pipeline{ID: 1, Status: "success"} if !reflect.DeepEqual(want, pipeline) { t.Errorf("Pipelines.GetPipeline returned %+v, want %+v", pipeline, want) } } func TestGetPipelineVariables(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/pipelines/5949167/variables", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, `[{"key":"RUN_NIGHTLY_BUILD","variable_type":"env_var","value":"true"},{"key":"foo","value":"bar"}]`) }) variables, _, err := client.Pipelines.GetPipelineVariables(1, 5949167) if err != nil { t.Errorf("Pipelines.GetPipelineVariables returned error: %v", err) } want := []*PipelineVariable{{Key: "RUN_NIGHTLY_BUILD", Value: "true", VariableType: "env_var"}, {Key: "foo", Value: "bar"}} if !reflect.DeepEqual(want, variables) { t.Errorf("Pipelines.GetPipelineVariables returned %+v, want %+v", variables, want) } } func TestGetPipelineTestReport(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/pipelines/123456/test_report", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) mustWriteHTTPResponse(t, w, "testdata/get_pipeline_testreport.json") }) testreport, _, err := client.Pipelines.GetPipelineTestReport(1, 123456) if err != nil { t.Errorf("Pipelines.GetPipelineTestReport returned error: %v", err) } want := &PipelineTestReport{ TotalTime: 61.502, TotalCount: 9, SuccessCount: 5, ErrorCount: 4, TestSuites: []*PipelineTestSuites{ { Name: "Failing", TotalTime: 60.494, TotalCount: 8, SuccessCount: 4, ErrorCount: 4, TestCases: []*PipelineTestCases{ { Status: "error", Name: "Error testcase 1", Classname: "MyClassOne", File: "/path/file.ext", ExecutionTime: 19.987, SystemOutput: "Failed test", StackTrace: "java.lang.Exception: Stack trace\nat java.base/java.lang.Thread.dumpStack(Thread.java:1383)", AttachmentURL: "http://foo.bar", RecentFailures: &RecentFailures{ Count: 10, BaseBranch: "master", }, }, { Status: "error", Name: "Error testcase 2", Classname: "MyClass", ExecutionTime: 19.984, SystemOutput: map[string]interface{}{ "message": "Failed test", "type": "MultipleExceptionError", }, }, { Status: "error", Name: "Error testcase 3", Classname: "MyClass", SystemOutput: []interface{}{ "Failed test a", "Failed test b", }, }, { Status: "success", Name: "Succes full testcase", Classname: "MyClass", ExecutionTime: 19.7799999999999985, }, }, }, { Name: "Succes suite", TotalTime: 1.008, TotalCount: 1, SuccessCount: 1, TestCases: []*PipelineTestCases{{ Status: "success", Name: "Succesfull testcase", Classname: "MyClass", ExecutionTime: 1.008, }}, }, }, } if !reflect.DeepEqual(want, testreport) { t.Errorf("Pipelines.GetPipelineTestReport returned %+v, want %+v", testreport, want) } } func TestGetLatestPipeline(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/pipelines/latest", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) testParams(t, r, "") fmt.Fprint(w, `{"id":1,"status":"success"}`) }) pipeline, _, err := client.Pipelines.GetLatestPipeline(1, nil) assert.NoError(t, err) assert.Equal(t, &Pipeline{ID: 1, Status: "success"}, pipeline) } func TestGetLatestPipeline_WithRef(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/pipelines/latest", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) testParams(t, r, "ref=abc") fmt.Fprint(w, `{"id":1,"status":"success"}`) }) pipeline, _, err := client.Pipelines.GetLatestPipeline(1, &GetLatestPipelineOptions{ Ref: Ptr("abc"), }) assert.NoError(t, err) assert.Equal(t, &Pipeline{ID: 1, Status: "success"}, pipeline) } func TestCreatePipeline(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/pipeline", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) fmt.Fprint(w, `{"id":1, "status":"pending"}`) }) opt := &CreatePipelineOptions{Ref: Ptr("master")} pipeline, _, err := client.Pipelines.CreatePipeline(1, opt) if err != nil { t.Errorf("Pipelines.CreatePipeline returned error: %v", err) } want := &Pipeline{ID: 1, Status: "pending"} if !reflect.DeepEqual(want, pipeline) { t.Errorf("Pipelines.CreatePipeline returned %+v, want %+v", pipeline, want) } } func TestRetryPipelineBuild(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/pipelines/5949167/retry", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) fmt.Fprintln(w, `{"id":1, "status":"pending"}`) }) pipeline, _, err := client.Pipelines.RetryPipelineBuild(1, 5949167) if err != nil { t.Errorf("Pipelines.RetryPipelineBuild returned error: %v", err) } want := &Pipeline{ID: 1, Status: "pending"} if !reflect.DeepEqual(want, pipeline) { t.Errorf("Pipelines.RetryPipelineBuild returned %+v, want %+v", pipeline, want) } } func TestCancelPipelineBuild(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/pipelines/5949167/cancel", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) fmt.Fprintln(w, `{"id":1, "status":"canceled"}`) }) pipeline, _, err := client.Pipelines.CancelPipelineBuild(1, 5949167) if err != nil { t.Errorf("Pipelines.CancelPipelineBuild returned error: %v", err) } want := &Pipeline{ID: 1, Status: "canceled"} if !reflect.DeepEqual(want, pipeline) { t.Errorf("Pipelines.CancelPipelineBuild returned %+v, want %+v", pipeline, want) } } func TestDeletePipeline(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/pipelines/5949167", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodDelete) }) _, err := client.Pipelines.DeletePipeline("1", 5949167) if err != nil { t.Errorf("Pipelines.DeletePipeline returned error: %v", err) } } func TestUpdateMetadata(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/pipelines/234/metadata", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPut) fmt.Fprint(w, `{"id":1, "status":"running"}`) }) opt := &UpdatePipelineMetadataOptions{Name: Ptr("new pipeline title")} pipeline, _, err := client.Pipelines.UpdatePipelineMetadata("1", 234, opt) if err != nil { t.Errorf("Pipelines.UpdatePipelineMetadata returned error: %v", err) } want := &Pipeline{ID: 1, Status: "running"} if !reflect.DeepEqual(want, pipeline) { t.Errorf("Pipelines.UpdatePipelineMetadata returned %+v, want %+v", pipeline, want) } } golang-gitlab-gitlab-org-api-client-go-0.123.0/plan_limits.go000066400000000000000000000102301475761473200237150ustar00rootroot00000000000000// // Copyright 2021, Igor Varavko // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import "net/http" // PlanLimitsService handles communication with the repositories related // methods of the GitLab API. // // GitLab API docs: https://docs.gitlab.com/ee/api/plan_limits.html type PlanLimitsService struct { client *Client } // PlanLimit represents a GitLab pipeline. // // GitLab API docs: https://docs.gitlab.com/ee/api/plan_limits.html type PlanLimit struct { ConanMaxFileSize int `json:"conan_max_file_size,omitempty"` GenericPackagesMaxFileSize int `json:"generic_packages_max_file_size,omitempty"` HelmMaxFileSize int `json:"helm_max_file_size,omitempty"` MavenMaxFileSize int `json:"maven_max_file_size,omitempty"` NPMMaxFileSize int `json:"npm_max_file_size,omitempty"` NugetMaxFileSize int `json:"nuget_max_file_size,omitempty"` PyPiMaxFileSize int `json:"pypi_max_file_size,omitempty"` TerraformModuleMaxFileSize int `json:"terraform_module_max_file_size,omitempty"` } // GetCurrentPlanLimitsOptions represents the available GetCurrentPlanLimits() // options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/plan_limits.html#get-current-plan-limits type GetCurrentPlanLimitsOptions struct { PlanName *string `url:"plan_name,omitempty" json:"plan_name,omitempty"` } // List the current limits of a plan on the GitLab instance. // // GitLab API docs: // https://docs.gitlab.com/ee/api/plan_limits.html#get-current-plan-limits func (s *PlanLimitsService) GetCurrentPlanLimits(opt *GetCurrentPlanLimitsOptions, options ...RequestOptionFunc) (*PlanLimit, *Response, error) { req, err := s.client.NewRequest(http.MethodGet, "application/plan_limits", opt, options) if err != nil { return nil, nil, err } pl := new(PlanLimit) resp, err := s.client.Do(req, pl) if err != nil { return nil, resp, err } return pl, resp, nil } // ChangePlanLimitOptions represents the available ChangePlanLimits() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/plan_limits.html#change-plan-limits type ChangePlanLimitOptions struct { PlanName *string `url:"plan_name,omitempty" json:"plan_name,omitempty"` ConanMaxFileSize *int `url:"conan_max_file_size,omitempty" json:"conan_max_file_size,omitempty"` GenericPackagesMaxFileSize *int `url:"generic_packages_max_file_size,omitempty" json:"generic_packages_max_file_size,omitempty"` HelmMaxFileSize *int `url:"helm_max_file_size,omitempty" json:"helm_max_file_size,omitempty"` MavenMaxFileSize *int `url:"maven_max_file_size,omitempty" json:"maven_max_file_size,omitempty"` NPMMaxFileSize *int `url:"npm_max_file_size,omitempty" json:"npm_max_file_size,omitempty"` NugetMaxFileSize *int `url:"nuget_max_file_size,omitempty" json:"nuget_max_file_size,omitempty"` PyPiMaxFileSize *int `url:"pypi_max_file_size,omitempty" json:"pypi_max_file_size,omitempty"` TerraformModuleMaxFileSize *int `url:"terraform_module_max_file_size,omitempty" json:"terraform_module_max_file_size,omitempty"` } // ChangePlanLimits modifies the limits of a plan on the GitLab instance. // // GitLab API docs: // https://docs.gitlab.com/ee/api/plan_limits.html#change-plan-limits func (s *PlanLimitsService) ChangePlanLimits(opt *ChangePlanLimitOptions, options ...RequestOptionFunc) (*PlanLimit, *Response, error) { req, err := s.client.NewRequest(http.MethodPut, "application/plan_limits", opt, options) if err != nil { return nil, nil, err } pl := new(PlanLimit) resp, err := s.client.Do(req, pl) if err != nil { return nil, resp, err } return pl, resp, nil } golang-gitlab-gitlab-org-api-client-go-0.123.0/plan_limits_test.go000066400000000000000000000062351475761473200247660ustar00rootroot00000000000000// // Copyright 2021, Igor Varavko // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "fmt" "net/http" "reflect" "testing" ) func TestGetCurrentPlanLimits(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/application/plan_limits", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprintf(w, `{ "conan_max_file_size": 3221225472, "generic_packages_max_file_size": 5368709120, "helm_max_file_size": 5242880, "maven_max_file_size": 3221225472, "npm_max_file_size": 524288000, "nuget_max_file_size": 524288000, "pypi_max_file_size": 3221225472, "terraform_module_max_file_size": 1073741824 }`) }) opt := &GetCurrentPlanLimitsOptions{ PlanName: Ptr("default"), } planlimit, _, err := client.PlanLimits.GetCurrentPlanLimits(opt) if err != nil { t.Errorf("PlanLimits.GetCurrentPlanLimits returned error: %v", err) } want := &PlanLimit{ ConanMaxFileSize: 3221225472, GenericPackagesMaxFileSize: 5368709120, HelmMaxFileSize: 5242880, MavenMaxFileSize: 3221225472, NPMMaxFileSize: 524288000, NugetMaxFileSize: 524288000, PyPiMaxFileSize: 3221225472, TerraformModuleMaxFileSize: 1073741824, } if !reflect.DeepEqual(want, planlimit) { t.Errorf("PlanLimits.GetCurrentPlanLimits returned %+v, want %+v", planlimit, want) } } func TestChangePlanLimits(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/application/plan_limits", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPut) fmt.Fprintf(w, `{ "conan_max_file_size": 3221225472, "generic_packages_max_file_size": 5368709120, "helm_max_file_size": 5242880, "maven_max_file_size": 3221225472, "npm_max_file_size": 524288000, "nuget_max_file_size": 524288000, "pypi_max_file_size": 3221225472, "terraform_module_max_file_size": 1073741824 }`) }) opt := &ChangePlanLimitOptions{ PlanName: Ptr("default"), ConanMaxFileSize: Ptr(3221225472), } planlimit, _, err := client.PlanLimits.ChangePlanLimits(opt) if err != nil { t.Errorf("PlanLimits.ChangePlanLimits returned error: %v", err) } want := &PlanLimit{ ConanMaxFileSize: 3221225472, GenericPackagesMaxFileSize: 5368709120, HelmMaxFileSize: 5242880, MavenMaxFileSize: 3221225472, NPMMaxFileSize: 524288000, NugetMaxFileSize: 524288000, PyPiMaxFileSize: 3221225472, TerraformModuleMaxFileSize: 1073741824, } if !reflect.DeepEqual(want, planlimit) { t.Errorf("PlanLimits.ChangePlanLimits returned %+v, want %+v", planlimit, want) } } golang-gitlab-gitlab-org-api-client-go-0.123.0/project_access_tokens.go000066400000000000000000000147151475761473200257700ustar00rootroot00000000000000// // Copyright 2021, Patrick Webster // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "fmt" "net/http" "time" ) // ProjectAccessTokensService handles communication with the // project access tokens related methods of the GitLab API. // // GitLab API docs: https://docs.gitlab.com/ee/api/project_access_tokens.html type ProjectAccessTokensService struct { client *Client } // ProjectAccessToken represents a GitLab project access token. // // GitLab API docs: https://docs.gitlab.com/ee/api/project_access_tokens.html type ProjectAccessToken struct { ID int `json:"id"` UserID int `json:"user_id"` Name string `json:"name"` Scopes []string `json:"scopes"` CreatedAt *time.Time `json:"created_at"` LastUsedAt *time.Time `json:"last_used_at"` ExpiresAt *ISOTime `json:"expires_at"` Active bool `json:"active"` Revoked bool `json:"revoked"` Token string `json:"token"` AccessLevel AccessLevelValue `json:"access_level"` } func (v ProjectAccessToken) String() string { return Stringify(v) } // ListProjectAccessTokensOptions represents the available // ListProjectAccessTokens() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/project_access_tokens.html#list-project-access-tokens type ListProjectAccessTokensOptions ListOptions // ListProjectAccessTokens gets a list of all project access tokens in a // project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/project_access_tokens.html#list-project-access-tokens func (s *ProjectAccessTokensService) ListProjectAccessTokens(pid interface{}, opt *ListProjectAccessTokensOptions, options ...RequestOptionFunc) ([]*ProjectAccessToken, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/access_tokens", PathEscape(project)) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } var pats []*ProjectAccessToken resp, err := s.client.Do(req, &pats) if err != nil { return nil, resp, err } return pats, resp, nil } // GetProjectAccessToken gets a single project access tokens in a project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/project_access_tokens.html#get-a-project-access-token func (s *ProjectAccessTokensService) GetProjectAccessToken(pid interface{}, id int, options ...RequestOptionFunc) (*ProjectAccessToken, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/access_tokens/%d", PathEscape(project), id) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } pat := new(ProjectAccessToken) resp, err := s.client.Do(req, &pat) if err != nil { return nil, resp, err } return pat, resp, nil } // CreateProjectAccessTokenOptions represents the available CreateVariable() // options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/project_access_tokens.html#create-a-project-access-token type CreateProjectAccessTokenOptions struct { Name *string `url:"name,omitempty" json:"name,omitempty"` Scopes *[]string `url:"scopes,omitempty" json:"scopes,omitempty"` AccessLevel *AccessLevelValue `url:"access_level,omitempty" json:"access_level,omitempty"` ExpiresAt *ISOTime `url:"expires_at,omitempty" json:"expires_at,omitempty"` } // CreateProjectAccessToken creates a new project access token. // // GitLab API docs: // https://docs.gitlab.com/ee/api/project_access_tokens.html#create-a-project-access-token func (s *ProjectAccessTokensService) CreateProjectAccessToken(pid interface{}, opt *CreateProjectAccessTokenOptions, options ...RequestOptionFunc) (*ProjectAccessToken, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/access_tokens", PathEscape(project)) req, err := s.client.NewRequest(http.MethodPost, u, opt, options) if err != nil { return nil, nil, err } pat := new(ProjectAccessToken) resp, err := s.client.Do(req, pat) if err != nil { return nil, resp, err } return pat, resp, nil } // RotateProjectAccessTokenOptions represents the available RotateProjectAccessToken() // options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/project_access_tokens.html#rotate-a-project-access-token type RotateProjectAccessTokenOptions struct { ExpiresAt *ISOTime `url:"expires_at,omitempty" json:"expires_at,omitempty"` } // RotateProjectAccessToken revokes a project access token and returns a new // project access token that expires in one week per default. // // GitLab API docs: // https://docs.gitlab.com/ee/api/project_access_tokens.html#rotate-a-project-access-token func (s *ProjectAccessTokensService) RotateProjectAccessToken(pid interface{}, id int, opt *RotateProjectAccessTokenOptions, options ...RequestOptionFunc) (*ProjectAccessToken, *Response, error) { projects, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/access_tokens/%d/rotate", PathEscape(projects), id) req, err := s.client.NewRequest(http.MethodPost, u, opt, options) if err != nil { return nil, nil, err } pat := new(ProjectAccessToken) resp, err := s.client.Do(req, pat) if err != nil { return nil, resp, err } return pat, resp, nil } // RevokeProjectAccessToken revokes a project access token. // // GitLab API docs: // https://docs.gitlab.com/ee/api/project_access_tokens.html#revoke-a-project-access-token func (s *ProjectAccessTokensService) RevokeProjectAccessToken(pid interface{}, id int, options ...RequestOptionFunc) (*Response, error) { project, err := parseID(pid) if err != nil { return nil, err } u := fmt.Sprintf("projects/%s/access_tokens/%d", PathEscape(project), id) req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } golang-gitlab-gitlab-org-api-client-go-0.123.0/project_access_tokens_test.go000066400000000000000000000142451475761473200270250ustar00rootroot00000000000000// // Copyright 2021, Patrick Webster // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "net/http" "reflect" "testing" "time" ) func TestListProjectAccessTokens(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/access_tokens", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) mustWriteHTTPResponse(t, w, "testdata/list_project_access_tokens.json") }) projectAccessTokens, _, err := client.ProjectAccessTokens.ListProjectAccessTokens(1, &ListProjectAccessTokensOptions{Page: 1, PerPage: 20}) if err != nil { t.Errorf("ProjectAccessTokens.ListProjectAccessTokens returned error: %v", err) } time1, err := time.Parse(time.RFC3339, "2021-03-09T21:11:47.271Z") if err != nil { t.Errorf("ProjectAccessTokens.ListProjectAccessTokens returned error: %v", err) } time2, err := time.Parse(time.RFC3339, "2021-03-09T21:11:47.340Z") if err != nil { t.Errorf("ProjectAccessTokens.ListProjectAccessTokens returned error: %v", err) } time3, err := time.Parse(time.RFC3339, "2021-03-10T21:11:47.271Z") if err != nil { t.Errorf("GroupAccessTokens.ListGroupAccessTokens returned error: %v", err) } want := []*ProjectAccessToken{ { ID: 1876, UserID: 2453, Name: "token 10", Scopes: []string{"api", "read_api", "read_repository", "write_repository"}, CreatedAt: &time1, LastUsedAt: &time3, Active: true, Revoked: false, AccessLevel: AccessLevelValue(40), }, { ID: 1877, UserID: 2456, Name: "token 8", Scopes: []string{"api", "read_api", "read_repository", "write_repository"}, CreatedAt: &time2, Active: true, Revoked: false, AccessLevel: AccessLevelValue(30), }, } if !reflect.DeepEqual(want, projectAccessTokens) { t.Errorf("ProjectAccessTokens.ListProjectAccessTokens returned %+v, want %+v", projectAccessTokens, want) } } func TestGetProjectAccessToken(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/access_tokens/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) mustWriteHTTPResponse(t, w, "testdata/get_project_access_token.json") }) projectAccessToken, _, err := client.ProjectAccessTokens.GetProjectAccessToken(1, 1) if err != nil { t.Errorf("ProjectAccessTokens.GetProjectAccessToken returned error: %v", err) } createdAt, err := time.Parse(time.RFC3339, "2021-03-09T21:11:47.271Z") if err != nil { t.Errorf("ProjectAccessTokens.GetProjectAccessToken returned error: %v", err) } want := &ProjectAccessToken{ ID: 1, UserID: 2453, Name: "token 10", Scopes: []string{"api", "read_api", "read_repository", "write_repository"}, CreatedAt: &createdAt, Active: true, Revoked: false, AccessLevel: AccessLevelValue(40), } if !reflect.DeepEqual(want, projectAccessToken) { t.Errorf("ProjectAccessTokens.GetProjectAccessToken returned %+v, want %+v", projectAccessToken, want) } } func TestCreateProjectAccessToken(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/access_tokens", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) mustWriteHTTPResponse(t, w, "testdata/create_project_access_token.json") }) projectAccessToken, _, err := client.ProjectAccessTokens.CreateProjectAccessToken(1, nil) if err != nil { t.Errorf("ProjectAccessTokens.CreateProjectAccessToken returned error: %v", err) } time1, err := time.Parse(time.RFC3339, "2021-03-09T21:11:47.271Z") if err != nil { t.Errorf("ProjectAccessTokens.CreateProjectAccessToken returned error: %v", err) } want := &ProjectAccessToken{ ID: 1876, UserID: 2453, Name: "token 10", Scopes: []string{"api", "read_api", "read_repository", "write_repository"}, ExpiresAt: nil, CreatedAt: &time1, Active: true, Revoked: false, Token: "2UsevZE1x1ZdFZW4MNzH", AccessLevel: AccessLevelValue(40), } if !reflect.DeepEqual(want, projectAccessToken) { t.Errorf("ProjectAccessTokens.CreateProjectAccessToken returned %+v, want %+v", projectAccessToken, want) } } func TestRotateProjectAccessToken(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/access_tokens/42/rotate", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) mustWriteHTTPResponse(t, w, "testdata/rotate_project_access_token.json") }) createdAt, _ := time.Parse(time.RFC3339, "2023-08-01T15:00:00.000Z") expiration := ISOTime(time.Date(2023, time.August, 15, 0, 0, 0, 0, time.UTC)) opts := &RotateProjectAccessTokenOptions{ExpiresAt: &expiration} rotatedToken, _, err := client.ProjectAccessTokens.RotateProjectAccessToken(1, 42, opts) if err != nil { t.Errorf("ProjectAccessTokens.RotateProjectAccessToken returned error: %v", err) } want := &ProjectAccessToken{ ID: 42, UserID: 1337, Name: "Rotated Token", Scopes: []string{"api"}, ExpiresAt: &expiration, CreatedAt: &createdAt, Active: true, Revoked: false, AccessLevel: AccessLevelValue(30), Token: "s3cr3t", } if !reflect.DeepEqual(want, rotatedToken) { t.Errorf("ProjectAccessTokens.RotateProjectAccessTokens returned %+v, want %+v", rotatedToken, want) } } func TestRevokeProjectAccessToken(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/access_tokens/1234", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodDelete) }) _, err := client.ProjectAccessTokens.RevokeProjectAccessToken("1", 1234) if err != nil { t.Errorf("ProjectAccessTokens.RevokeProjectAccessToken returned error: %v", err) } } golang-gitlab-gitlab-org-api-client-go-0.123.0/project_badges.go000066400000000000000000000160461475761473200243700ustar00rootroot00000000000000// // Copyright 2021, Sander van Harmelen // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "fmt" "net/http" ) // ProjectBadge represents a project badge. // // GitLab API docs: // https://docs.gitlab.com/ee/api/project_badges.html#list-all-badges-of-a-project type ProjectBadge struct { ID int `json:"id"` Name string `json:"name"` LinkURL string `json:"link_url"` ImageURL string `json:"image_url"` RenderedLinkURL string `json:"rendered_link_url"` RenderedImageURL string `json:"rendered_image_url"` // Kind represents a project badge kind. Can be empty, when used PreviewProjectBadge(). Kind string `json:"kind"` } // ProjectBadgesService handles communication with the project badges // related methods of the GitLab API. // // GitLab API docs: https://docs.gitlab.com/ee/api/project_badges.html type ProjectBadgesService struct { client *Client } // ListProjectBadgesOptions represents the available ListProjectBadges() // options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/project_badges.html#list-all-badges-of-a-project type ListProjectBadgesOptions struct { ListOptions Name *string `url:"name,omitempty" json:"name,omitempty"` } // ListProjectBadges gets a list of a project's badges and its group badges. // // GitLab API docs: // https://docs.gitlab.com/ee/api/project_badges.html#list-all-badges-of-a-project func (s *ProjectBadgesService) ListProjectBadges(pid interface{}, opt *ListProjectBadgesOptions, options ...RequestOptionFunc) ([]*ProjectBadge, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/badges", PathEscape(project)) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } var pb []*ProjectBadge resp, err := s.client.Do(req, &pb) if err != nil { return nil, resp, err } return pb, resp, nil } // GetProjectBadge gets a project badge. // // GitLab API docs: // https://docs.gitlab.com/ee/api/project_badges.html#get-a-badge-of-a-project func (s *ProjectBadgesService) GetProjectBadge(pid interface{}, badge int, options ...RequestOptionFunc) (*ProjectBadge, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/badges/%d", PathEscape(project), badge) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } pb := new(ProjectBadge) resp, err := s.client.Do(req, pb) if err != nil { return nil, resp, err } return pb, resp, nil } // AddProjectBadgeOptions represents the available AddProjectBadge() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/project_badges.html#add-a-badge-to-a-project type AddProjectBadgeOptions struct { LinkURL *string `url:"link_url,omitempty" json:"link_url,omitempty"` ImageURL *string `url:"image_url,omitempty" json:"image_url,omitempty"` Name *string `url:"name,omitempty" json:"name,omitempty"` } // AddProjectBadge adds a badge to a project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/project_badges.html#add-a-badge-to-a-project func (s *ProjectBadgesService) AddProjectBadge(pid interface{}, opt *AddProjectBadgeOptions, options ...RequestOptionFunc) (*ProjectBadge, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/badges", PathEscape(project)) req, err := s.client.NewRequest(http.MethodPost, u, opt, options) if err != nil { return nil, nil, err } pb := new(ProjectBadge) resp, err := s.client.Do(req, pb) if err != nil { return nil, resp, err } return pb, resp, nil } // EditProjectBadgeOptions represents the available EditProjectBadge() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/project_badges.html#edit-a-badge-of-a-project type EditProjectBadgeOptions struct { LinkURL *string `url:"link_url,omitempty" json:"link_url,omitempty"` ImageURL *string `url:"image_url,omitempty" json:"image_url,omitempty"` Name *string `url:"name,omitempty" json:"name,omitempty"` } // EditProjectBadge updates a badge of a project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/project_badges.html#edit-a-badge-of-a-project func (s *ProjectBadgesService) EditProjectBadge(pid interface{}, badge int, opt *EditProjectBadgeOptions, options ...RequestOptionFunc) (*ProjectBadge, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/badges/%d", PathEscape(project), badge) req, err := s.client.NewRequest(http.MethodPut, u, opt, options) if err != nil { return nil, nil, err } pb := new(ProjectBadge) resp, err := s.client.Do(req, pb) if err != nil { return nil, resp, err } return pb, resp, nil } // DeleteProjectBadge removes a badge from a project. Only project's // badges will be removed by using this endpoint. // // GitLab API docs: // https://docs.gitlab.com/ee/api/project_badges.html#remove-a-badge-from-a-project func (s *ProjectBadgesService) DeleteProjectBadge(pid interface{}, badge int, options ...RequestOptionFunc) (*Response, error) { project, err := parseID(pid) if err != nil { return nil, err } u := fmt.Sprintf("projects/%s/badges/%d", PathEscape(project), badge) req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } // ProjectBadgePreviewOptions represents the available PreviewProjectBadge() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/project_badges.html#preview-a-badge-from-a-project type ProjectBadgePreviewOptions struct { LinkURL *string `url:"link_url,omitempty" json:"link_url,omitempty"` ImageURL *string `url:"image_url,omitempty" json:"image_url,omitempty"` } // PreviewProjectBadge returns how the link_url and image_url final URLs would be after // resolving the placeholder interpolation. // // GitLab API docs: // https://docs.gitlab.com/ee/api/project_badges.html#preview-a-badge-from-a-project func (s *ProjectBadgesService) PreviewProjectBadge(pid interface{}, opt *ProjectBadgePreviewOptions, options ...RequestOptionFunc) (*ProjectBadge, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/badges/render", PathEscape(project)) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } pb := new(ProjectBadge) resp, err := s.client.Do(req, &pb) if err != nil { return nil, resp, err } return pb, resp, nil } golang-gitlab-gitlab-org-api-client-go-0.123.0/project_badges_test.go000066400000000000000000000225541475761473200254300ustar00rootroot00000000000000package gitlab import ( "fmt" "net/http" "testing" "github.com/stretchr/testify/require" ) func TestProjectBadgesService_ListProjectBadges(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/badges", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprintf(w, ` [ { "name": "Coverage", "id": 1, "link_url": "http://example.com/ci_status.svg?project={project_path}&ref={default_branch}", "image_url": "https://shields.io/my/badge", "rendered_link_url": "http://example.com/ci_status.svg?project=example-org/example-project&ref=master", "rendered_image_url": "https://shields.io/my/badge", "kind": "project" } ] `) }) want := []*ProjectBadge{{ ID: 1, Name: "Coverage", LinkURL: "http://example.com/ci_status.svg?project={project_path}&ref={default_branch}", ImageURL: "https://shields.io/my/badge", RenderedLinkURL: "http://example.com/ci_status.svg?project=example-org/example-project&ref=master", RenderedImageURL: "https://shields.io/my/badge", Kind: "project", }} pbs, resp, err := client.ProjectBadges.ListProjectBadges(1, nil, nil) require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, want, pbs) pbs, resp, err = client.ProjectBadges.ListProjectBadges(1.01, nil, nil) require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string") require.Nil(t, resp) require.Nil(t, pbs) pbs, resp, err = client.ProjectBadges.ListProjectBadges(1, nil, errorOption) require.EqualError(t, err, "RequestOptionFunc returns an error") require.Nil(t, resp) require.Nil(t, pbs) pbs, resp, err = client.ProjectBadges.ListProjectBadges(2, nil, nil) require.Error(t, err) require.Nil(t, pbs) require.Equal(t, http.StatusNotFound, resp.StatusCode) } func TestProjectBadgesService_GetProjectBadge(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/badges/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprintf(w, ` { "name": "Coverage", "id": 1, "link_url": "http://example.com/ci_status.svg?project={project_path}&ref={default_branch}", "image_url": "https://shields.io/my/badge", "rendered_link_url": "http://example.com/ci_status.svg?project=example-org/example-project&ref=master", "rendered_image_url": "https://shields.io/my/badge", "kind": "project" } `) }) want := &ProjectBadge{ ID: 1, Name: "Coverage", LinkURL: "http://example.com/ci_status.svg?project={project_path}&ref={default_branch}", ImageURL: "https://shields.io/my/badge", RenderedLinkURL: "http://example.com/ci_status.svg?project=example-org/example-project&ref=master", RenderedImageURL: "https://shields.io/my/badge", Kind: "project", } pb, resp, err := client.ProjectBadges.GetProjectBadge(1, 1, nil, nil) require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, want, pb) pb, resp, err = client.ProjectBadges.GetProjectBadge(1.01, 1, nil, nil) require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string") require.Nil(t, resp) require.Nil(t, pb) pb, resp, err = client.ProjectBadges.GetProjectBadge(1, 1, nil, errorOption) require.EqualError(t, err, "RequestOptionFunc returns an error") require.Nil(t, resp) require.Nil(t, pb) pb, resp, err = client.ProjectBadges.GetProjectBadge(2, 1, nil, nil) require.Error(t, err) require.Nil(t, pb) require.Equal(t, http.StatusNotFound, resp.StatusCode) } func TestProjectBadgesService_AddProjectBadge(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/badges", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) fmt.Fprintf(w, ` { "name": "mybadge", "id": 1, "link_url": "http://example.com/ci_status.svg?project={project_path}&ref={default_branch}", "image_url": "https://shields.io/my/badge", "rendered_link_url": "http://example.com/ci_status.svg?project=example-org/example-project&ref=master", "rendered_image_url": "https://shields.io/my/badge", "kind": "project" } `) }) want := &ProjectBadge{ ID: 1, Name: "mybadge", LinkURL: "http://example.com/ci_status.svg?project={project_path}&ref={default_branch}", ImageURL: "https://shields.io/my/badge", RenderedLinkURL: "http://example.com/ci_status.svg?project=example-org/example-project&ref=master", RenderedImageURL: "https://shields.io/my/badge", Kind: "project", } pb, resp, err := client.ProjectBadges.AddProjectBadge(1, nil, nil) require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, want, pb) pb, resp, err = client.ProjectBadges.AddProjectBadge(1.01, nil, nil) require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string") require.Nil(t, resp) require.Nil(t, pb) pb, resp, err = client.ProjectBadges.AddProjectBadge(1, nil, errorOption) require.EqualError(t, err, "RequestOptionFunc returns an error") require.Nil(t, resp) require.Nil(t, pb) pb, resp, err = client.ProjectBadges.AddProjectBadge(2, nil, nil) require.Error(t, err) require.Nil(t, pb) require.Equal(t, http.StatusNotFound, resp.StatusCode) } func TestProjectBadgesService_EditProjectBadge(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/badges/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPut) fmt.Fprintf(w, ` { "name": "mybadge", "id": 1, "link_url": "http://example.com/ci_status.svg?project={project_path}&ref={default_branch}", "image_url": "https://shields.io/my/badge", "rendered_link_url": "http://example.com/ci_status.svg?project=example-org/example-project&ref=master", "rendered_image_url": "https://shields.io/my/badge", "kind": "project" } `) }) want := &ProjectBadge{ ID: 1, Name: "mybadge", LinkURL: "http://example.com/ci_status.svg?project={project_path}&ref={default_branch}", ImageURL: "https://shields.io/my/badge", RenderedLinkURL: "http://example.com/ci_status.svg?project=example-org/example-project&ref=master", RenderedImageURL: "https://shields.io/my/badge", Kind: "project", } pb, resp, err := client.ProjectBadges.EditProjectBadge(1, 1, nil, nil) require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, want, pb) pb, resp, err = client.ProjectBadges.EditProjectBadge(1.01, 1, nil, nil) require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string") require.Nil(t, resp) require.Nil(t, pb) pb, resp, err = client.ProjectBadges.EditProjectBadge(1, 1, nil, errorOption) require.EqualError(t, err, "RequestOptionFunc returns an error") require.Nil(t, resp) require.Nil(t, pb) pb, resp, err = client.ProjectBadges.EditProjectBadge(2, 1, nil, nil) require.Error(t, err) require.Nil(t, pb) require.Equal(t, http.StatusNotFound, resp.StatusCode) } func TestProjectBadgesService_DeleteProjectBadge(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/badges/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodDelete) }) resp, err := client.ProjectBadges.DeleteProjectBadge(1, 1, nil, nil) require.NoError(t, err) require.NotNil(t, resp) resp, err = client.ProjectBadges.DeleteProjectBadge(1.01, 1, nil, nil) require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string") require.Nil(t, resp) resp, err = client.ProjectBadges.DeleteProjectBadge(1, 1, nil, errorOption) require.EqualError(t, err, "RequestOptionFunc returns an error") require.Nil(t, resp) resp, err = client.ProjectBadges.DeleteProjectBadge(2, 1, nil, nil) require.Error(t, err) require.Equal(t, http.StatusNotFound, resp.StatusCode) } func TestProjectBadgesService_PreviewProjectBadge(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/badges/render", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprintf(w, ` { "link_url": "http://example.com/ci_status.svg?project={project_path}&ref={default_branch}", "image_url": "https://shields.io/my/badge", "rendered_link_url": "http://example.com/ci_status.svg?project=example-org/example-project&ref=master", "rendered_image_url": "https://shields.io/my/badge" } `) }) want := &ProjectBadge{ LinkURL: "http://example.com/ci_status.svg?project={project_path}&ref={default_branch}", ImageURL: "https://shields.io/my/badge", RenderedLinkURL: "http://example.com/ci_status.svg?project=example-org/example-project&ref=master", RenderedImageURL: "https://shields.io/my/badge", } pb, resp, err := client.ProjectBadges.PreviewProjectBadge(1, nil, nil) require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, want, pb) pb, resp, err = client.ProjectBadges.PreviewProjectBadge(1.01, nil, nil) require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string") require.Nil(t, resp) require.Nil(t, pb) pb, resp, err = client.ProjectBadges.PreviewProjectBadge(1, nil, errorOption) require.EqualError(t, err, "RequestOptionFunc returns an error") require.Nil(t, resp) require.Nil(t, pb) pb, resp, err = client.ProjectBadges.PreviewProjectBadge(2, nil, nil) require.Error(t, err) require.Nil(t, pb) require.Equal(t, http.StatusNotFound, resp.StatusCode) } golang-gitlab-gitlab-org-api-client-go-0.123.0/project_clusters.go000066400000000000000000000211221475761473200247760ustar00rootroot00000000000000// // Copyright 2021, Matej Velikonja // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "fmt" "net/http" "time" ) // ProjectClustersService handles communication with the // project clusters related methods of the GitLab API. // // GitLab API docs: // https://docs.gitlab.com/ee/api/project_clusters.html type ProjectClustersService struct { client *Client } // ProjectCluster represents a GitLab Project Cluster. // // GitLab API docs: https://docs.gitlab.com/ee/api/project_clusters.html type ProjectCluster struct { ID int `json:"id"` Name string `json:"name"` Domain string `json:"domain"` CreatedAt *time.Time `json:"created_at"` ProviderType string `json:"provider_type"` PlatformType string `json:"platform_type"` EnvironmentScope string `json:"environment_scope"` ClusterType string `json:"cluster_type"` User *User `json:"user"` PlatformKubernetes *PlatformKubernetes `json:"platform_kubernetes"` ManagementProject *ManagementProject `json:"management_project"` Project *Project `json:"project"` } func (v ProjectCluster) String() string { return Stringify(v) } // PlatformKubernetes represents a GitLab Project Cluster PlatformKubernetes. type PlatformKubernetes struct { APIURL string `json:"api_url"` Token string `json:"token"` CaCert string `json:"ca_cert"` Namespace string `json:"namespace"` AuthorizationType string `json:"authorization_type"` } // ManagementProject represents a GitLab Project Cluster management_project. type ManagementProject struct { ID int `json:"id"` Description string `json:"description"` Name string `json:"name"` NameWithNamespace string `json:"name_with_namespace"` Path string `json:"path"` PathWithNamespace string `json:"path_with_namespace"` CreatedAt *time.Time `json:"created_at"` } // ListClusters gets a list of all clusters in a project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/project_clusters.html#list-project-clusters func (s *ProjectClustersService) ListClusters(pid interface{}, options ...RequestOptionFunc) ([]*ProjectCluster, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/clusters", PathEscape(project)) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } var pcs []*ProjectCluster resp, err := s.client.Do(req, &pcs) if err != nil { return nil, resp, err } return pcs, resp, nil } // GetCluster gets a cluster. // // GitLab API docs: // https://docs.gitlab.com/ee/api/project_clusters.html#get-a-single-project-cluster func (s *ProjectClustersService) GetCluster(pid interface{}, cluster int, options ...RequestOptionFunc) (*ProjectCluster, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/clusters/%d", PathEscape(project), cluster) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } pc := new(ProjectCluster) resp, err := s.client.Do(req, &pc) if err != nil { return nil, resp, err } return pc, resp, nil } // AddClusterOptions represents the available AddCluster() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/project_clusters.html#add-existing-cluster-to-project type AddClusterOptions struct { Name *string `url:"name,omitempty" json:"name,omitempty"` Domain *string `url:"domain,omitempty" json:"domain,omitempty"` Enabled *bool `url:"enabled,omitempty" json:"enabled,omitempty"` Managed *bool `url:"managed,omitempty" json:"managed,omitempty"` EnvironmentScope *string `url:"environment_scope,omitempty" json:"environment_scope,omitempty"` PlatformKubernetes *AddPlatformKubernetesOptions `url:"platform_kubernetes_attributes,omitempty" json:"platform_kubernetes_attributes,omitempty"` ManagementProjectID *string `url:"management_project_id,omitempty" json:"management_project_id,omitempty"` } // AddPlatformKubernetesOptions represents the available PlatformKubernetes options for adding. type AddPlatformKubernetesOptions struct { APIURL *string `url:"api_url,omitempty" json:"api_url,omitempty"` Token *string `url:"token,omitempty" json:"token,omitempty"` CaCert *string `url:"ca_cert,omitempty" json:"ca_cert,omitempty"` Namespace *string `url:"namespace,omitempty" json:"namespace,omitempty"` AuthorizationType *string `url:"authorization_type,omitempty" json:"authorization_type,omitempty"` } // AddCluster adds an existing cluster to the project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/project_clusters.html#add-existing-cluster-to-project func (s *ProjectClustersService) AddCluster(pid interface{}, opt *AddClusterOptions, options ...RequestOptionFunc) (*ProjectCluster, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/clusters/user", PathEscape(project)) req, err := s.client.NewRequest(http.MethodPost, u, opt, options) if err != nil { return nil, nil, err } pc := new(ProjectCluster) resp, err := s.client.Do(req, pc) if err != nil { return nil, resp, err } return pc, resp, nil } // EditClusterOptions represents the available EditCluster() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/project_clusters.html#edit-project-cluster type EditClusterOptions struct { Name *string `url:"name,omitempty" json:"name,omitempty"` Domain *string `url:"domain,omitempty" json:"domain,omitempty"` EnvironmentScope *string `url:"environment_scope,omitempty" json:"environment_scope,omitempty"` ManagementProjectID *string `url:"management_project_id,omitempty" json:"management_project_id,omitempty"` PlatformKubernetes *EditPlatformKubernetesOptions `url:"platform_kubernetes_attributes,omitempty" json:"platform_kubernetes_attributes,omitempty"` } // EditPlatformKubernetesOptions represents the available PlatformKubernetes options for editing. type EditPlatformKubernetesOptions struct { APIURL *string `url:"api_url,omitempty" json:"api_url,omitempty"` Token *string `url:"token,omitempty" json:"token,omitempty"` CaCert *string `url:"ca_cert,omitempty" json:"ca_cert,omitempty"` Namespace *string `url:"namespace,omitempty" json:"namespace,omitempty"` } // EditCluster updates an existing project cluster. // // GitLab API docs: // https://docs.gitlab.com/ee/api/project_clusters.html#edit-project-cluster func (s *ProjectClustersService) EditCluster(pid interface{}, cluster int, opt *EditClusterOptions, options ...RequestOptionFunc) (*ProjectCluster, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/clusters/%d", PathEscape(project), cluster) req, err := s.client.NewRequest(http.MethodPut, u, opt, options) if err != nil { return nil, nil, err } pc := new(ProjectCluster) resp, err := s.client.Do(req, pc) if err != nil { return nil, resp, err } return pc, resp, nil } // DeleteCluster deletes an existing project cluster. // // GitLab API docs: // https://docs.gitlab.com/ee/api/project_clusters.html#delete-project-cluster func (s *ProjectClustersService) DeleteCluster(pid interface{}, cluster int, options ...RequestOptionFunc) (*Response, error) { project, err := parseID(pid) if err != nil { return nil, err } u := fmt.Sprintf("projects/%s/clusters/%d", PathEscape(project), cluster) req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } golang-gitlab-gitlab-org-api-client-go-0.123.0/project_clusters_test.go000066400000000000000000000225061475761473200260440ustar00rootroot00000000000000// // Copyright 2021, Sander van Harmelen // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "fmt" "net/http" "testing" ) func TestListClusters(t *testing.T) { mux, client := setup(t) pid := 1234 mux.HandleFunc("/api/v4/projects/1234/clusters", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) response := `[ { "id":18, "name":"cluster-1", "domain":"example.com", "created_at":"2019-01-02T20:18:12.563Z", "provider_type":"user", "platform_type":"kubernetes", "environment_scope":"*", "cluster_type":"project_type", "user": { "id":1, "name":"Administrator", "username":"root", "state":"active", "avatar_url":"https://www.gravatar.com/avatar/4249f4df72b..", "web_url":"https://gitlab.example.com/root" }, "platform_kubernetes": { "api_url":"https://104.197.68.152", "namespace":"cluster-1-namespace", "authorization_type":"rbac", "ca_cert":"-----BEGIN CERTIFICATE-----\r\nhFiK1L61owwDQYJKoZIhvcNAQELBQAw\r\nLzEtMCsGA1UEAxMkZDA1YzQ1YjctNzdiMS00NDY0LThjNmEtMTQ0ZDJkZjM4ZDBj\r\nMB4XDTE4MTIyNzIwMDM1MVoXDTIzMTIyNjIxMDM1MVowLzEtMCsGA1UEAxMkZDA1\r\nYzQ1YjctNzdiMS00NDY0LThjNmEtMTQ0ZDJkZjM.......-----END CERTIFICATE-----" } } ]` fmt.Fprint(w, response) }) clusters, _, err := client.ProjectCluster.ListClusters(pid) if err != nil { t.Errorf("ProjectClusters.ListClusters returned error: %v", err) } if len(clusters) != 1 { t.Errorf("expected 1 cluster; got %d", len(clusters)) } if clusters[0].ID != 18 { t.Errorf("expected clusterID 1; got %d", clusters[0].ID) } if clusters[0].Domain != "example.com" { t.Errorf("expected cluster domain example.com; got %q", clusters[0].Domain) } } func TestGetCluster(t *testing.T) { mux, client := setup(t) pid := 1234 mux.HandleFunc("/api/v4/projects/1234/clusters/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) response := `{ "id":18, "name":"cluster-1", "domain":"example.com", "created_at":"2019-01-02T20:18:12.563Z", "provider_type":"user", "platform_type":"kubernetes", "environment_scope":"*", "cluster_type":"project_type", "user": { "id":1, "name":"Administrator", "username":"root", "state":"active", "avatar_url":"https://www.gravatar.com/avatar/4249f4df72b..", "web_url":"https://gitlab.example.com/root" }, "platform_kubernetes": { "api_url":"https://104.197.68.152", "namespace":"cluster-1-namespace", "authorization_type":"rbac", "ca_cert":"-----BEGIN CERTIFICATE-----\r\nhFiK1L61owwDQYJKoZIhvcNAQELBQAw\r\nLzEtMCsGA1UEAxMkZDA1YzQ1YjctNzdiMS00NDY0LThjNmEtMTQ0ZDJkZjM4ZDBj\r\nMB4XDTE4MTIyNzIwMDM1MVoXDTIzMTIyNjIxMDM1MVowLzEtMCsGA1UEAxMkZDA1\r\nYzQ1YjctNzdiMS00NDY0LThjNmEtMTQ0ZDJkZjM.......-----END CERTIFICATE-----" }, "project": { "id":26, "description":"", "name":"project-with-clusters-api", "name_with_namespace":"Administrator / project-with-clusters-api", "path":"project-with-clusters-api", "path_with_namespace":"root/project-with-clusters-api", "created_at":"2019-01-02T20:13:32.600Z", "default_branch":null, "tag_list":[], "ssh_url_to_repo":"ssh://gitlab.example.com/root/project-with-clusters-api.git", "http_url_to_repo":"https://gitlab.example.com/root/project-with-clusters-api.git", "web_url":"https://gitlab.example.com/root/project-with-clusters-api", "readme_url":null, "avatar_url":null, "star_count":0, "forks_count":0, "last_activity_at":"2019-01-02T20:13:32.600Z", "namespace": { "id":1, "name":"root", "path":"root", "kind":"user", "full_path":"root", "parent_id":null } } }` fmt.Fprint(w, response) }) cluster, _, err := client.ProjectCluster.GetCluster(pid, 1) if err != nil { t.Errorf("ProjectClusters.ListClusters returned error: %v", err) } if cluster.ID != 18 { t.Errorf("expected clusterID 18; got %d", cluster.ID) } if cluster.Domain != "example.com" { t.Errorf("expected cluster domain example.com; got %q", cluster.Domain) } } func TestAddCluster(t *testing.T) { mux, client := setup(t) pid := 1234 mux.HandleFunc("/api/v4/projects/1234/clusters/user", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) response := `{ "id":24, "name":"cluster-5", "domain":"example.com", "created_at":"2019-01-03T21:53:40.610Z", "provider_type":"user", "platform_type":"kubernetes", "environment_scope":"*", "cluster_type":"project_type", "user": { "id":1, "name":"Administrator", "username":"root", "state":"active", "avatar_url":"https://www.gravatar.com/avatar/4249f4df72b..", "web_url":"https://gitlab.example.com/root" }, "platform_kubernetes": { "api_url":"https://35.111.51.20", "namespace":"cluster-5-namespace", "authorization_type":"rbac", "ca_cert":"-----BEGIN CERTIFICATE-----\r\nhFiK1L61owwDQYJKoZIhvcNAQELBQAw\r\nLzEtMCsGA1UEAxMkZDA1YzQ1YjctNzdiMS00NDY0LThjNmEtMTQ0ZDJkZjM4ZDBj\r\nMB4XDTE4MTIyNzIwMDM1MVoXDTIzMTIyNjIxMDM1MVowLzEtMCsGA1UEAxMkZDA1\r\nYzQ1YjctNzdiMS00NDY0LThjNmEtMTQ0ZDJkZjM.......-----END CERTIFICATE-----" }, "project": { "id":26, "description":"", "name":"project-with-clusters-api", "name_with_namespace":"Administrator / project-with-clusters-api", "path":"project-with-clusters-api", "path_with_namespace":"root/project-with-clusters-api", "created_at":"2019-01-02T20:13:32.600Z", "default_branch":null, "tag_list":[], "ssh_url_to_repo":"ssh:://gitlab.example.com/root/project-with-clusters-api.git", "http_url_to_repo":"https://gitlab.example.com/root/project-with-clusters-api.git", "web_url":"https://gitlab.example.com/root/project-with-clusters-api", "readme_url":null, "avatar_url":null, "star_count":0, "forks_count":0, "last_activity_at":"2019-01-02T20:13:32.600Z", "namespace": { "id":1, "name":"root", "path":"root", "kind":"user", "full_path":"root", "parent_id":null } } }` fmt.Fprint(w, response) }) cluster, _, err := client.ProjectCluster.AddCluster(pid, &AddClusterOptions{}) if err != nil { t.Errorf("ProjectClusters.AddCluster returned error: %v", err) } if cluster.ID != 24 { t.Errorf("expected ClusterID 24; got %d", cluster.ID) } } func TestEditCluster(t *testing.T) { mux, client := setup(t) pid := 1234 mux.HandleFunc("/api/v4/projects/1234/clusters/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPut) response := `{ "id":24, "name":"new-cluster-name", "domain":"example.com", "created_at":"2019-01-03T21:53:40.610Z", "provider_type":"user", "platform_type":"kubernetes", "environment_scope":"*", "cluster_type":"project_type", "user": { "id":1, "name":"Administrator", "username":"root", "state":"active", "avatar_url":"https://www.gravatar.com/avatar/4249f4df72b..", "web_url":"https://gitlab.example.com/root" }, "platform_kubernetes": { "api_url":"https://new-api-url.com", "namespace":"cluster-5-namespace", "authorization_type":"rbac", "ca_cert":null }, "project": { "id":26, "description":"", "name":"project-with-clusters-api", "name_with_namespace":"Administrator / project-with-clusters-api", "path":"project-with-clusters-api", "path_with_namespace":"root/project-with-clusters-api", "created_at":"2019-01-02T20:13:32.600Z", "default_branch":null, "tag_list":[], "ssh_url_to_repo":"ssh:://gitlab.example.com/root/project-with-clusters-api.git", "http_url_to_repo":"https://gitlab.example.com/root/project-with-clusters-api.git", "web_url":"https://gitlab.example.com/root/project-with-clusters-api", "readme_url":null, "avatar_url":null, "star_count":0, "forks_count":0, "last_activity_at":"2019-01-02T20:13:32.600Z", "namespace": { "id":1, "name":"root", "path":"root", "kind":"user", "full_path":"root", "parent_id":null } } }` fmt.Fprint(w, response) }) cluster, _, err := client.ProjectCluster.EditCluster(pid, 1, &EditClusterOptions{}) if err != nil { t.Errorf("ProjectClusters.EditCluster returned error: %v", err) } if cluster.ID != 24 { t.Errorf("expected ClusterID 24; got %d", cluster.ID) } } func TestDeleteCluster(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1234/clusters/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodDelete) w.WriteHeader(http.StatusAccepted) }) resp, err := client.ProjectCluster.DeleteCluster(1234, 1) if err != nil { t.Errorf("ProjectCluster.DeleteCluster returned error: %v", err) } want := http.StatusAccepted got := resp.StatusCode if got != want { t.Errorf("ProjectCluster.DeleteCluster returned %d, want %d", got, want) } } golang-gitlab-gitlab-org-api-client-go-0.123.0/project_feature_flags.go000066400000000000000000000207001475761473200257420ustar00rootroot00000000000000package gitlab import ( "fmt" "net/http" "time" ) // ProjectFeatureFlagService handles operations on gitlab project feature // flags using the following api: // // GitLab API docs: https://docs.gitlab.com/ee/api/feature_flags.html type ProjectFeatureFlagService struct { client *Client } // ProjectFeatureFlag represents a GitLab project iteration. // // GitLab API docs: https://docs.gitlab.com/ee/api/feature_flags.html type ProjectFeatureFlag struct { Name string `json:"name"` Description string `json:"description"` Active bool `json:"active"` Version string `json:"version"` CreatedAt *time.Time `json:"created_at"` UpdatedAt *time.Time `json:"updated_at"` Scopes []*ProjectFeatureFlagScope `json:"scopes"` Strategies []*ProjectFeatureFlagStrategy `json:"strategies"` } // ProjectFeatureFlagScope defines the scopes of a feature flag // // GitLab API docs: https://docs.gitlab.com/ee/api/feature_flags.html type ProjectFeatureFlagScope struct { ID int `json:"id"` EnvironmentScope string `json:"environment_scope"` } // ProjectFeatureFlagStrategy defines the strategy used for a feature flag // // GitLab API docs: https://docs.gitlab.com/ee/api/feature_flags.html type ProjectFeatureFlagStrategy struct { ID int `json:"id"` Name string `json:"name"` Parameters *ProjectFeatureFlagStrategyParameter `json:"parameters"` Scopes []*ProjectFeatureFlagScope `json:"scopes"` } // ProjectFeatureFlagStrategyParameter is used in updating and creating feature flags // // GitLab API docs: https://docs.gitlab.com/ee/api/feature_flags.html type ProjectFeatureFlagStrategyParameter struct { GroupID string `json:"groupId,omitempty"` UserIDs string `json:"userIds,omitempty"` Percentage string `json:"percentage,omitempty"` // Following fields aren't documented in Gitlab API docs, // but are present in Gitlab API since 13.5. // Docs: https://docs.getunleash.io/reference/activation-strategies#gradual-rollout Rollout string `json:"rollout,omitempty"` Stickiness string `json:"stickiness,omitempty"` } func (i ProjectFeatureFlag) String() string { return Stringify(i) } // ListProjectFeatureFlagOptions contains the options for ListProjectFeatureFlags // // GitLab API docs: // https://docs.gitlab.com/ee/api/feature_flags.html#list-feature-flags-for-a-project type ListProjectFeatureFlagOptions struct { ListOptions Scope *string `url:"scope,omitempty" json:"scope,omitempty"` } // ListProjectFeatureFlags returns a list with the feature flags of a project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/feature_flags.html#list-feature-flags-for-a-project func (s *ProjectFeatureFlagService) ListProjectFeatureFlags(pid interface{}, opt *ListProjectFeatureFlagOptions, options ...RequestOptionFunc) ([]*ProjectFeatureFlag, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/feature_flags", PathEscape(project)) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } var pffs []*ProjectFeatureFlag resp, err := s.client.Do(req, &pffs) if err != nil { return nil, resp, err } return pffs, resp, nil } // GetProjectFeatureFlag gets a single feature flag for the specified project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/feature_flags.html#get-a-single-feature-flag func (s *ProjectFeatureFlagService) GetProjectFeatureFlag(pid interface{}, name string, options ...RequestOptionFunc) (*ProjectFeatureFlag, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/feature_flags/%s", PathEscape(project), name) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } flag := new(ProjectFeatureFlag) resp, err := s.client.Do(req, flag) if err != nil { return nil, resp, err } return flag, resp, nil } // CreateProjectFeatureFlagOptions represents the available // CreateProjectFeatureFlag() options. // // Gitlab API docs: // https://docs.gitlab.com/ee/api/feature_flags.html#create-a-feature-flag type CreateProjectFeatureFlagOptions struct { Name *string `url:"name,omitempty" json:"name,omitempty"` Description *string `url:"description,omitempty" json:"description,omitempty"` Version *string `url:"version,omitempty" json:"version,omitempty"` Active *bool `url:"active,omitempty" json:"active,omitempty"` Strategies *[]*FeatureFlagStrategyOptions `url:"strategies,omitempty" json:"strategies,omitempty"` } // FeatureFlagStrategyOptions represents the available feature flag strategy // options. // // Gitlab API docs: // https://docs.gitlab.com/ee/api/feature_flags.html#create-a-feature-flag type FeatureFlagStrategyOptions struct { ID *int `url:"id,omitempty" json:"id,omitempty"` Name *string `url:"name,omitempty" json:"name,omitempty"` Parameters *ProjectFeatureFlagStrategyParameter `url:"parameters,omitempty" json:"parameters,omitempty"` Scopes *[]*ProjectFeatureFlagScope `url:"scopes,omitempty" json:"scopes,omitempty"` } // ProjectFeatureFlagScopeOptions represents the available feature flag scope // options. // // Gitlab API docs: // https://docs.gitlab.com/ee/api/feature_flags.html#create-a-feature-flag type ProjectFeatureFlagScopeOptions struct { ID *int `url:"id,omitempty" json:"id,omitempty"` EnvironmentScope *string `url:"id,omitempty" json:"environment_scope,omitempty"` } // CreateProjectFeatureFlag creates a feature flag // // Gitlab API docs: // https://docs.gitlab.com/ee/api/feature_flags.html#create-a-feature-flag func (s *ProjectFeatureFlagService) CreateProjectFeatureFlag(pid interface{}, opt *CreateProjectFeatureFlagOptions, options ...RequestOptionFunc) (*ProjectFeatureFlag, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/feature_flags", PathEscape(project), ) req, err := s.client.NewRequest(http.MethodPost, u, opt, options) if err != nil { return nil, nil, err } flag := new(ProjectFeatureFlag) resp, err := s.client.Do(req, flag) if err != nil { return flag, resp, err } return flag, resp, nil } // UpdateProjectFeatureFlagOptions represents the available // UpdateProjectFeatureFlag() options. // // Gitlab API docs: // https://docs.gitlab.com/ee/api/feature_flags.html#update-a-feature-flag type UpdateProjectFeatureFlagOptions struct { Name *string `url:"name,omitempty" json:"name,omitempty"` Description *string `url:"description,omitempty" json:"description,omitempty"` Active *bool `url:"active,omitempty" json:"active,omitempty"` Strategies *[]*FeatureFlagStrategyOptions `url:"strategies,omitempty" json:"strategies,omitempty"` } // UpdateProjectFeatureFlag updates a feature flag // // Gitlab API docs: // https://docs.gitlab.com/ee/api/feature_flags.html#update-a-feature-flag func (s *ProjectFeatureFlagService) UpdateProjectFeatureFlag(pid interface{}, name string, opt *UpdateProjectFeatureFlagOptions, options ...RequestOptionFunc) (*ProjectFeatureFlag, *Response, error) { group, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/feature_flags/%s", PathEscape(group), name, ) req, err := s.client.NewRequest(http.MethodPut, u, opt, options) if err != nil { return nil, nil, err } flag := new(ProjectFeatureFlag) resp, err := s.client.Do(req, flag) if err != nil { return flag, resp, err } return flag, resp, nil } // DeleteProjectFeatureFlag deletes a feature flag // // Gitlab API docs: // https://docs.gitlab.com/ee/api/feature_flags.html#delete-a-feature-flag func (s *ProjectFeatureFlagService) DeleteProjectFeatureFlag(pid interface{}, name string, options ...RequestOptionFunc) (*Response, error) { project, err := parseID(pid) if err != nil { return nil, err } u := fmt.Sprintf("projects/%s/feature_flags/%s", PathEscape(project), name) req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } golang-gitlab-gitlab-org-api-client-go-0.123.0/project_feature_flags_test.go000066400000000000000000000150311475761473200270020ustar00rootroot00000000000000package gitlab import ( "net/http" "testing" "time" "github.com/stretchr/testify/assert" ) func TestListProjectFeatureFlags(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/333/feature_flags", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) mustWriteHTTPResponse(t, w, "testdata/list_project_feature_flags.json") }) actual, _, err := client.ProjectFeatureFlags.ListProjectFeatureFlags(333, &ListProjectFeatureFlagOptions{}) if err != nil { t.Errorf("ProjectFeatureFlags.ListProjectFeatureFlags returned error: %v", err) return } createdAt1 := time.Date(2019, 11, 4, 8, 13, 51, 0, time.UTC) updatedAt1 := time.Date(2019, 11, 4, 8, 13, 11, 0, time.UTC) createdAt2 := time.Date(2019, 11, 4, 8, 13, 10, 0, time.UTC) updatedAt2 := time.Date(2019, 11, 4, 8, 13, 10, 0, time.UTC) expected := []*ProjectFeatureFlag{ { Name: "merge_train", Description: "This feature is about merge train", Active: true, Version: "new_version_flag", CreatedAt: &createdAt1, UpdatedAt: &updatedAt1, Scopes: []*ProjectFeatureFlagScope{}, Strategies: []*ProjectFeatureFlagStrategy{ { ID: 1, Name: "userWithId", Parameters: &ProjectFeatureFlagStrategyParameter{ UserIDs: "user1", }, Scopes: []*ProjectFeatureFlagScope{ { ID: 1, EnvironmentScope: "production", }, }, }, }, }, { Name: "new_live_trace", Description: "This is a new live trace feature", Active: true, Version: "new_version_flag", CreatedAt: &createdAt2, UpdatedAt: &updatedAt2, Scopes: []*ProjectFeatureFlagScope{}, Strategies: []*ProjectFeatureFlagStrategy{ { ID: 2, Name: "default", Parameters: &ProjectFeatureFlagStrategyParameter{}, Scopes: []*ProjectFeatureFlagScope{ { ID: 2, EnvironmentScope: "staging", }, }, }, }, }, } assert.Equal(t, len(expected), len(actual)) for i := range expected { assert.Equal(t, expected[i], actual[i]) } } func TestGetProjectFeatureFlag(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/feature_flags/testing", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) mustWriteHTTPResponse(t, w, "testdata/get_project_feature_flag.json") }) actual, resp, err := client.ProjectFeatureFlags.GetProjectFeatureFlag(1, "testing") if err != nil { t.Fatalf("ProjectFeatureFlags.GetProjectFeatureFlag returned error: %v, response %v", err, resp) } date := time.Date(2020, 0o5, 13, 19, 56, 33, 0, time.UTC) expected := &ProjectFeatureFlag{ Name: "awesome_feature", Active: true, Version: "new_version_flag", CreatedAt: &date, UpdatedAt: &date, Scopes: []*ProjectFeatureFlagScope{}, Strategies: []*ProjectFeatureFlagStrategy{ { ID: 36, Name: "default", Parameters: &ProjectFeatureFlagStrategyParameter{}, Scopes: []*ProjectFeatureFlagScope{ { ID: 37, EnvironmentScope: "production", }, }, }, { ID: 24, Name: "flexibleRollout", Parameters: &ProjectFeatureFlagStrategyParameter{ GroupID: "default", Rollout: "50", Stickiness: "default", }, Scopes: []*ProjectFeatureFlagScope{ { ID: 52, EnvironmentScope: "*", }, }, }, }, } assert.Equal(t, expected, actual) } func TestCreateProjectFeatureFlag(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/feature_flags/testing", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPut) mustWriteHTTPResponse(t, w, "testdata/create_project_feature_flag.json") }) actual, _, err := client.ProjectFeatureFlags.UpdateProjectFeatureFlag(1, "testing", &UpdateProjectFeatureFlagOptions{}) if err != nil { t.Errorf("ProjectFeatureFlags.UpdateProjectFeatureFlag returned error: %v", err) return } createdAt := time.Date(2020, 5, 13, 19, 56, 33, 0, time.UTC) updatedAt := time.Date(2020, 5, 13, 19, 56, 33, 0, time.UTC) expected := &ProjectFeatureFlag{ Name: "awesome_feature", Active: true, Version: "new_version_flag", CreatedAt: &createdAt, UpdatedAt: &updatedAt, Scopes: []*ProjectFeatureFlagScope{}, Strategies: []*ProjectFeatureFlagStrategy{ { ID: 36, Name: "default", Parameters: &ProjectFeatureFlagStrategyParameter{}, Scopes: []*ProjectFeatureFlagScope{ { ID: 37, EnvironmentScope: "production", }, }, }, }, } assert.Equal(t, expected, actual) } func TestUpdateProjectFeatureFlag(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/feature_flags/testing", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPut) mustWriteHTTPResponse(t, w, "testdata/update_project_feature_flag.json") }) actual, _, err := client.ProjectFeatureFlags.UpdateProjectFeatureFlag(1, "testing", &UpdateProjectFeatureFlagOptions{}) if err != nil { t.Errorf("ProjectFeatureFlags.UpdateProjectFeatureFlag returned error: %v", err) return } createdAt := time.Date(2020, 5, 13, 20, 10, 32, 0, time.UTC) updatedAt := time.Date(2020, 5, 13, 20, 10, 32, 0, time.UTC) expected := &ProjectFeatureFlag{ Name: "awesome_feature", Active: true, Version: "new_version_flag", CreatedAt: &createdAt, UpdatedAt: &updatedAt, Scopes: []*ProjectFeatureFlagScope{}, Strategies: []*ProjectFeatureFlagStrategy{ { ID: 38, Name: "gradualRolloutUserId", Parameters: &ProjectFeatureFlagStrategyParameter{ GroupID: "default", Percentage: "25", }, Scopes: []*ProjectFeatureFlagScope{ { ID: 40, EnvironmentScope: "staging", }, }, }, { ID: 37, Name: "default", Parameters: &ProjectFeatureFlagStrategyParameter{}, Scopes: []*ProjectFeatureFlagScope{ { ID: 39, EnvironmentScope: "production", }, }, }, }, } assert.Equal(t, expected, actual) } func TestDeleteProjectFeatureFlag(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/feature_flags/testing", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodDelete) }) _, err := client.ProjectFeatureFlags.DeleteProjectFeatureFlag(1, "testing") if err != nil { t.Errorf("ProjectFeatureFlags.DeleteProjectFeatureFlag returned error: %v", err) return } } golang-gitlab-gitlab-org-api-client-go-0.123.0/project_import_export.go000066400000000000000000000153211475761473200260510ustar00rootroot00000000000000// // Copyright 2021, Sander van Harmelen // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "bytes" "fmt" "io" "net/http" "time" ) // ProjectImportExportService handles communication with the project // import/export related methods of the GitLab API. // // GitLab API docs: // https://docs.gitlab.com/ee/api/project_import_export.html type ProjectImportExportService struct { client *Client } // ImportStatus represents a project import status. // // GitLab API docs: // https://docs.gitlab.com/ee/api/project_import_export.html#import-status type ImportStatus struct { ID int `json:"id"` Description string `json:"description"` Name string `json:"name"` NameWithNamespace string `json:"name_with_namespace"` Path string `json:"path"` PathWithNamespace string `json:"path_with_namespace"` CreateAt *time.Time `json:"create_at"` ImportStatus string `json:"import_status"` ImportType string `json:"import_type"` CorrelationID string `json:"correlation_id"` ImportError string `json:"import_error"` } func (s ImportStatus) String() string { return Stringify(s) } // ExportStatus represents a project export status. // // GitLab API docs: // https://docs.gitlab.com/ee/api/project_import_export.html#export-status type ExportStatus struct { ID int `json:"id"` Description string `json:"description"` Name string `json:"name"` NameWithNamespace string `json:"name_with_namespace"` Path string `json:"path"` PathWithNamespace string `json:"path_with_namespace"` CreatedAt *time.Time `json:"created_at"` ExportStatus string `json:"export_status"` Message string `json:"message"` Links struct { APIURL string `json:"api_url"` WebURL string `json:"web_url"` } `json:"_links"` } func (s ExportStatus) String() string { return Stringify(s) } // ScheduleExportOptions represents the available ScheduleExport() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/project_import_export.html#schedule-an-export type ScheduleExportOptions struct { Description *string `url:"description,omitempty" json:"description,omitempty"` Upload struct { URL *string `url:"url,omitempty" json:"url,omitempty"` HTTPMethod *string `url:"http_method,omitempty" json:"http_method,omitempty"` } `url:"upload,omitempty" json:"upload,omitempty"` } // ScheduleExport schedules a project export. // // GitLab API docs: // https://docs.gitlab.com/ee/api/project_import_export.html#schedule-an-export func (s *ProjectImportExportService) ScheduleExport(pid interface{}, opt *ScheduleExportOptions, options ...RequestOptionFunc) (*Response, error) { project, err := parseID(pid) if err != nil { return nil, err } u := fmt.Sprintf("projects/%s/export", PathEscape(project)) req, err := s.client.NewRequest(http.MethodPost, u, opt, options) if err != nil { return nil, err } return s.client.Do(req, nil) } // ExportStatus get the status of export. // // GitLab API docs: // https://docs.gitlab.com/ee/api/project_import_export.html#export-status func (s *ProjectImportExportService) ExportStatus(pid interface{}, options ...RequestOptionFunc) (*ExportStatus, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/export", PathEscape(project)) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } es := new(ExportStatus) resp, err := s.client.Do(req, es) if err != nil { return nil, resp, err } return es, resp, nil } // ExportDownload download the finished export. // // GitLab API docs: // https://docs.gitlab.com/ee/api/project_import_export.html#export-download func (s *ProjectImportExportService) ExportDownload(pid interface{}, options ...RequestOptionFunc) ([]byte, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/export/download", PathEscape(project)) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } var b bytes.Buffer resp, err := s.client.Do(req, &b) if err != nil { return nil, resp, err } return b.Bytes(), resp, err } // ImportFileOptions represents the available ImportFile() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/project_import_export.html#import-a-file type ImportFileOptions struct { Namespace *string `url:"namespace,omitempty" json:"namespace,omitempty"` Name *string `url:"name,omitempty" json:"name,omitempty"` Path *string `url:"path,omitempty" json:"path,omitempty"` Overwrite *bool `url:"overwrite,omitempty" json:"overwrite,omitempty"` OverrideParams *CreateProjectOptions `url:"override_params,omitempty" json:"override_params,omitempty"` } // Import a project from an archive file. // // GitLab API docs: // https://docs.gitlab.com/ee/api/project_import_export.html#import-a-file func (s *ProjectImportExportService) ImportFromFile(archive io.Reader, opt *ImportFileOptions, options ...RequestOptionFunc) (*ImportStatus, *Response, error) { req, err := s.client.UploadRequest( http.MethodPost, "projects/import", archive, "archive.tar.gz", UploadFile, opt, options, ) if err != nil { return nil, nil, err } is := new(ImportStatus) resp, err := s.client.Do(req, is) if err != nil { return nil, resp, err } return is, resp, nil } // ImportStatus get the status of an import. // // GitLab API docs: // https://docs.gitlab.com/ee/api/project_import_export.html#import-status func (s *ProjectImportExportService) ImportStatus(pid interface{}, options ...RequestOptionFunc) (*ImportStatus, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/import", PathEscape(project)) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } is := new(ImportStatus) resp, err := s.client.Do(req, is) if err != nil { return nil, resp, err } return is, resp, nil } golang-gitlab-gitlab-org-api-client-go-0.123.0/project_import_export_test.go000066400000000000000000000154501475761473200271130ustar00rootroot00000000000000package gitlab import ( "bytes" "fmt" "net/http" "testing" "github.com/stretchr/testify/require" ) func TestProjectImportExportService_ScheduleExport(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/export", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) w.WriteHeader(http.StatusAccepted) }) resp, err := client.ProjectImportExport.ScheduleExport(1, nil, nil) require.NoError(t, err) require.NotNil(t, resp) resp, err = client.ProjectImportExport.ScheduleExport(1.01, nil, nil) require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string") require.Nil(t, resp) resp, err = client.ProjectImportExport.ScheduleExport(1, nil, errorOption) require.EqualError(t, err, "RequestOptionFunc returns an error") require.Nil(t, resp) resp, err = client.ProjectImportExport.ScheduleExport(2, nil, nil) require.Error(t, err) require.Equal(t, http.StatusNotFound, resp.StatusCode) } func TestProjectImportExportService_ExportStatus(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/export", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprintf(w, ` { "id": 1, "description": "Itaque perspiciatis minima aspernatur corporis consequatur.", "name": "Gitlab Test", "name_with_namespace": "Gitlab Org / Gitlab Test", "path": "gitlab-test", "path_with_namespace": "gitlab-org/gitlab-test", "export_status": "finished", "_links": { "api_url": "https://gitlab.example.com/api/v4/projects/1/export/download", "web_url": "https://gitlab.example.com/gitlab-org/gitlab-test/download_export" } } `) }) want := &ExportStatus{ ID: 1, Description: "Itaque perspiciatis minima aspernatur corporis consequatur.", Name: "Gitlab Test", NameWithNamespace: "Gitlab Org / Gitlab Test", Path: "gitlab-test", PathWithNamespace: "gitlab-org/gitlab-test", ExportStatus: "finished", Message: "", Links: struct { APIURL string `json:"api_url"` WebURL string `json:"web_url"` }{ APIURL: "https://gitlab.example.com/api/v4/projects/1/export/download", WebURL: "https://gitlab.example.com/gitlab-org/gitlab-test/download_export", }, } es, resp, err := client.ProjectImportExport.ExportStatus(1, nil, nil) require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, want, es) es, resp, err = client.ProjectImportExport.ExportStatus(1.01, nil, nil) require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string") require.Nil(t, resp) require.Nil(t, es) es, resp, err = client.ProjectImportExport.ExportStatus(1, nil, errorOption) require.EqualError(t, err, "RequestOptionFunc returns an error") require.Nil(t, resp) require.Nil(t, es) es, resp, err = client.ProjectImportExport.ExportStatus(2, nil, nil) require.Error(t, err) require.Nil(t, es) require.Equal(t, http.StatusNotFound, resp.StatusCode) } func TestProjectImportExportService_ExportDownload(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/export/download", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprintf(w, "file.tar.gz") }) want := []byte("file.tar.gz") es, resp, err := client.ProjectImportExport.ExportDownload(1, nil, nil) require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, want, es) es, resp, err = client.ProjectImportExport.ExportDownload(1.01, nil, nil) require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string") require.Nil(t, resp) require.Nil(t, es) es, resp, err = client.ProjectImportExport.ExportDownload(1, nil, errorOption) require.EqualError(t, err, "RequestOptionFunc returns an error") require.Nil(t, resp) require.Nil(t, es) es, resp, err = client.ProjectImportExport.ExportDownload(2, nil, nil) require.Error(t, err) require.Nil(t, es) require.Equal(t, http.StatusNotFound, resp.StatusCode) } func TestProjectImportExportService_ImportFile(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/import", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) fmt.Fprintf(w, ` { "id": 1, "description": null, "name": "api-project", "name_with_namespace": "Administrator / api-project", "path": "api-project", "path_with_namespace": "root/api-project", "import_status": "scheduled", "correlation_id": "mezklWso3Za" } `) }) want := &ImportStatus{ ID: 1, Description: "", Name: "api-project", NameWithNamespace: "Administrator / api-project", Path: "api-project", PathWithNamespace: "root/api-project", ImportStatus: "scheduled", CorrelationID: "mezklWso3Za", } file := bytes.NewBufferString("dummy") es, resp, err := client.ProjectImportExport.ImportFromFile(file, nil, nil) require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, want, es) es, resp, err = client.ProjectImportExport.ImportFromFile(file, nil, errorOption) require.EqualError(t, err, "RequestOptionFunc returns an error") require.Nil(t, resp) require.Nil(t, es) } func TestProjectImportExportService_ImportStatus(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/import", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprintf(w, ` { "id": 1, "description": "Itaque perspiciatis minima aspernatur corporis consequatur.", "name": "Gitlab Test", "name_with_namespace": "Gitlab Org / Gitlab Test", "path": "gitlab-test", "path_with_namespace": "gitlab-org/gitlab-test", "import_status": "started", "correlation_id": "mezklWso3Za" } `) }) want := &ImportStatus{ ID: 1, Description: "Itaque perspiciatis minima aspernatur corporis consequatur.", Name: "Gitlab Test", NameWithNamespace: "Gitlab Org / Gitlab Test", Path: "gitlab-test", PathWithNamespace: "gitlab-org/gitlab-test", ImportStatus: "started", CorrelationID: "mezklWso3Za", } es, resp, err := client.ProjectImportExport.ImportStatus(1, nil) require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, want, es) es, resp, err = client.ProjectImportExport.ImportStatus(1.01, nil) require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string") require.Nil(t, resp) require.Nil(t, es) es, resp, err = client.ProjectImportExport.ImportStatus(1, errorOption) require.EqualError(t, err, "RequestOptionFunc returns an error") require.Nil(t, resp) require.Nil(t, es) es, resp, err = client.ProjectImportExport.ImportStatus(2, nil) require.Error(t, err) require.Nil(t, es) require.Equal(t, http.StatusNotFound, resp.StatusCode) } golang-gitlab-gitlab-org-api-client-go-0.123.0/project_iterations.go000066400000000000000000000054161475761473200253230ustar00rootroot00000000000000// // Copyright 2022, Daniel Steinke // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "fmt" "net/http" "time" ) // IterationsAPI handles communication with the project iterations related // methods of the GitLab API // // GitLab API docs: https://docs.gitlab.com/ee/api/iterations.html type ProjectIterationsService struct { client *Client } // ProjectIteration represents a GitLab project iteration. // // GitLab API docs: https://docs.gitlab.com/ee/api/iterations.html type ProjectIteration struct { ID int `json:"id"` IID int `json:"iid"` Sequence int `json:"sequence"` GroupID int `json:"group_id"` Title string `json:"title"` Description string `json:"description"` State int `json:"state"` CreatedAt *time.Time `json:"created_at"` UpdatedAt *time.Time `json:"updated_at"` DueDate *ISOTime `json:"due_date"` StartDate *ISOTime `json:"start_date"` WebURL string `json:"web_url"` } func (i ProjectIteration) String() string { return Stringify(i) } // ListProjectIterationsOptions contains the available ListProjectIterations() // options // // GitLab API docs: // https://docs.gitlab.com/ee/api/iterations.html#list-project-iterations type ListProjectIterationsOptions struct { ListOptions State *string `url:"state,omitempty" json:"state,omitempty"` Search *string `url:"search,omitempty" json:"search,omitempty"` IncludeAncestors *bool `url:"include_ancestors,omitempty" json:"include_ancestors,omitempty"` } // ListProjectIterations returns a list of projects iterations. // // GitLab API docs: // https://docs.gitlab.com/ee/api/iterations.html#list-project-iterations func (i *ProjectIterationsService) ListProjectIterations(pid interface{}, opt *ListProjectIterationsOptions, options ...RequestOptionFunc) ([]*ProjectIteration, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/iterations", PathEscape(project)) req, err := i.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } var pis []*ProjectIteration resp, err := i.client.Do(req, &pis) if err != nil { return nil, resp, err } return pis, resp, nil } golang-gitlab-gitlab-org-api-client-go-0.123.0/project_iterations_test.go000066400000000000000000000023101475761473200263500ustar00rootroot00000000000000package gitlab import ( "fmt" "net/http" "reflect" "testing" ) func TestListProjectIterations(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/42/iterations", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprintf(w, `[ { "id": 53, "iid": 13, "sequence": 1, "group_id": 5, "title": "Iteration II", "description": "Ipsum Lorem ipsum", "state": 2, "web_url": "http://gitlab.example.com/groups/my-group/-/iterations/13" } ]`) }) iterations, _, err := client.ProjectIterations.ListProjectIterations(42, &ListProjectIterationsOptions{}) if err != nil { t.Errorf("GroupIterations.ListGroupIterations returned error: %v", err) } want := []*ProjectIteration{{ ID: 53, IID: 13, Sequence: 1, GroupID: 5, Title: "Iteration II", Description: "Ipsum Lorem ipsum", State: 2, WebURL: "http://gitlab.example.com/groups/my-group/-/iterations/13", }} if !reflect.DeepEqual(want, iterations) { t.Errorf("ProjectIterations.ListProjectIterations returned %+v, want %+v", iterations, want) } } golang-gitlab-gitlab-org-api-client-go-0.123.0/project_managed_licenses.go000066400000000000000000000132001475761473200264110ustar00rootroot00000000000000// // Copyright 2021, Andrea Perizzato // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "fmt" "net/http" ) // ManagedLicensesService handles communication with the managed licenses // methods of the GitLab API. // // GitLab API docs: https://docs.gitlab.com/ee/api/managed_licenses.html type ManagedLicensesService struct { client *Client } // ManagedLicense represents a managed license. // // GitLab API docs: https://docs.gitlab.com/ee/api/managed_licenses.html type ManagedLicense struct { ID int `json:"id"` Name string `json:"name"` ApprovalStatus LicenseApprovalStatusValue `json:"approval_status"` } // ListManagedLicenses returns a list of managed licenses from a project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/managed_licenses.html#list-managed-licenses func (s *ManagedLicensesService) ListManagedLicenses(pid interface{}, options ...RequestOptionFunc) ([]*ManagedLicense, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/managed_licenses", PathEscape(project)) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } var mls []*ManagedLicense resp, err := s.client.Do(req, &mls) if err != nil { return nil, resp, err } return mls, resp, nil } // GetManagedLicense returns an existing managed license. // // GitLab API docs: // https://docs.gitlab.com/ee/api/managed_licenses.html#show-an-existing-managed-license func (s *ManagedLicensesService) GetManagedLicense(pid, mlid interface{}, options ...RequestOptionFunc) (*ManagedLicense, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } license, err := parseID(mlid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/managed_licenses/%s", PathEscape(project), PathEscape(license)) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } ml := new(ManagedLicense) resp, err := s.client.Do(req, ml) if err != nil { return nil, resp, err } return ml, resp, nil } // AddManagedLicenseOptions represents the available AddManagedLicense() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/managed_licenses.html#create-a-new-managed-license type AddManagedLicenseOptions struct { Name *string `url:"name,omitempty" json:"name,omitempty"` ApprovalStatus *LicenseApprovalStatusValue `url:"approval_status,omitempty" json:"approval_status,omitempty"` } // AddManagedLicense adds a managed license to a project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/managed_licenses.html#create-a-new-managed-license func (s *ManagedLicensesService) AddManagedLicense(pid interface{}, opt *AddManagedLicenseOptions, options ...RequestOptionFunc) (*ManagedLicense, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/managed_licenses", PathEscape(project)) req, err := s.client.NewRequest(http.MethodPost, u, opt, options) if err != nil { return nil, nil, err } ml := new(ManagedLicense) resp, err := s.client.Do(req, ml) if err != nil { return nil, resp, err } return ml, resp, nil } // DeleteManagedLicense deletes a managed license with a given ID. // // GitLab API docs: // https://docs.gitlab.com/ee/api/managed_licenses.html#delete-a-managed-license func (s *ManagedLicensesService) DeleteManagedLicense(pid, mlid interface{}, options ...RequestOptionFunc) (*Response, error) { project, err := parseID(pid) if err != nil { return nil, err } license, err := parseID(mlid) if err != nil { return nil, err } u := fmt.Sprintf("projects/%s/managed_licenses/%s", PathEscape(project), PathEscape(license)) req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } // EditManagedLicenceOptions represents the available EditManagedLicense() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/managed_licenses.html#edit-an-existing-managed-license type EditManagedLicenceOptions struct { ApprovalStatus *LicenseApprovalStatusValue `url:"approval_status,omitempty" json:"approval_status,omitempty"` } // EditManagedLicense updates an existing managed license with a new approval // status. // // GitLab API docs: // https://docs.gitlab.com/ee/api/managed_licenses.html#edit-an-existing-managed-license func (s *ManagedLicensesService) EditManagedLicense(pid, mlid interface{}, opt *EditManagedLicenceOptions, options ...RequestOptionFunc) (*ManagedLicense, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } license, err := parseID(mlid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/managed_licenses/%s", PathEscape(project), PathEscape(license)) req, err := s.client.NewRequest(http.MethodPatch, u, opt, options) if err != nil { return nil, nil, err } ml := new(ManagedLicense) resp, err := s.client.Do(req, ml) if err != nil { return nil, resp, err } return ml, resp, nil } golang-gitlab-gitlab-org-api-client-go-0.123.0/project_managed_licenses_test.go000066400000000000000000000102421475761473200274530ustar00rootroot00000000000000// // Copyright 2021, Andrea Perizzato // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "net/http" "reflect" "testing" ) func TestListManagedLicenses(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/managed_licenses", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) mustWriteHTTPResponse(t, w, "testdata/list_project_managed_licenses.json") }) licenses, _, err := client.ManagedLicenses.ListManagedLicenses(1) if err != nil { t.Errorf("ManagedLicenses.ListManagedLicenses returned error: %v", err) } want := []*ManagedLicense{ { ID: 1, Name: "MIT", ApprovalStatus: LicenseApproved, }, { ID: 3, Name: "ISC", ApprovalStatus: LicenseBlacklisted, }, } if !reflect.DeepEqual(want, licenses) { t.Errorf("ManagedLicenses.ListManagedLicenses returned %+v, want %+v", licenses, want) } } func TestGetManagedLicenses(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/managed_licenses/3", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) mustWriteHTTPResponse(t, w, "testdata/get_project_managed_license.json") }) license, _, err := client.ManagedLicenses.GetManagedLicense(1, 3) if err != nil { t.Errorf("ManagedLicenses.GetManagedLicense returned error: %v", err) } want := &ManagedLicense{ ID: 3, Name: "ISC", ApprovalStatus: LicenseBlacklisted, } if !reflect.DeepEqual(want, license) { t.Errorf("ManagedLicenses.GetManagedLicense returned %+v, want %+v", license, want) } } func TestAddManagedLicenses(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/managed_licenses", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) testBody(t, r, `{"name":"MIT","approval_status":"approved"}`) mustWriteHTTPResponse(t, w, "testdata/add_project_managed_license.json") }) ops := AddManagedLicenseOptions{ Name: Ptr("MIT"), ApprovalStatus: Ptr(LicenseApproved), } license, _, err := client.ManagedLicenses.AddManagedLicense(1, &ops) if err != nil { t.Errorf("ManagedLicenses.AddManagedLicense returned error: %v", err) } want := &ManagedLicense{ ID: 123, Name: "MIT", ApprovalStatus: LicenseApproved, } if !reflect.DeepEqual(want, license) { t.Errorf("ManagedLicenses.AddManagedLicense returned %+v, want %+v", license, want) } } func TestDeleteManagedLicenses(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/managed_licenses/3", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodDelete) }) _, err := client.ManagedLicenses.DeleteManagedLicense(1, 3) if err != nil { t.Errorf("ManagedLicenses.RemoveManagedLicense returned error: %v", err) } } func TestEditManagedLicenses(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/managed_licenses/3", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPatch) testBody(t, r, `{"approval_status":"blacklisted"}`) mustWriteHTTPResponse(t, w, "testdata/edit_project_managed_license.json") }) ops := EditManagedLicenceOptions{ ApprovalStatus: Ptr(LicenseBlacklisted), } license, _, err := client.ManagedLicenses.EditManagedLicense(1, 3, &ops) if err != nil { t.Errorf("ManagedLicenses.EditManagedLicense returned error: %v", err) } want := &ManagedLicense{ ID: 3, Name: "CUSTOM", ApprovalStatus: LicenseBlacklisted, } if !reflect.DeepEqual(want, license) { t.Errorf("ManagedLicenses.EditManagedLicense returned %+v, want %+v", license, want) } } golang-gitlab-gitlab-org-api-client-go-0.123.0/project_markdown_uploads.go000066400000000000000000000144301475761473200265070ustar00rootroot00000000000000// // Copyright 2024, Sander van Harmelen // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "bytes" "fmt" "io" "net/http" "time" ) // ProjectMarkdownUploadsService handles communication with the project markdown uploads // related methods of the GitLab API. // // Gitlab API docs: https://docs.gitlab.com/ee/api/project_markdown_uploads.html type ProjectMarkdownUploadsService struct { client *Client } // ProjectMarkdownUploadedFile represents a single project markdown uploaded file. // // Gitlab API docs: https://docs.gitlab.com/ee/api/project_markdown_uploads.html type ProjectMarkdownUploadedFile struct { ID int `json:"id"` Alt string `json:"alt"` URL string `json:"url"` FullPath string `json:"full_path"` Markdown string `json:"markdown"` } // ProjectMarkdownUpload represents a single project markdown upload. // // Gitlab API docs: https://docs.gitlab.com/ee/api/project_markdown_uploads.html type ProjectMarkdownUpload struct { ID int `json:"id"` Size int `json:"size"` Filename string `json:"filename"` CreatedAt *time.Time `json:"created_at"` UploadedBy *User `json:"uploaded_by"` } // Gets a string representation of a ProjectMarkdownUpload. // // GitLab API docs: https://docs.gitlab.com/ee/api/project_markdown_uploads.html func (m ProjectMarkdownUpload) String() string { return Stringify(m) } // UploadProjectMarkdown uploads a markdown file to a project. // // GitLab docs: // https://docs.gitlab.com/ee/api/project_markdown_uploads.html#upload-a-file func (s *ProjectMarkdownUploadsService) UploadProjectMarkdown(pid interface{}, content io.Reader, filename string, options ...RequestOptionFunc) (*ProjectMarkdownUploadedFile, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/uploads", PathEscape(project)) req, err := s.client.UploadRequest( http.MethodPost, u, content, filename, UploadFile, nil, options, ) if err != nil { return nil, nil, err } f := new(ProjectMarkdownUploadedFile) resp, err := s.client.Do(req, f) if err != nil { return nil, resp, err } return f, resp, nil } // ListProjectMarkdownUploads gets all markdown uploads for a project. // // GitLab API Docs: // https://docs.gitlab.com/ee/api/project_markdown_uploads.html#list-uploads func (s *ProjectMarkdownUploadsService) ListProjectMarkdownUploads(pid interface{}, options ...RequestOptionFunc) ([]*ProjectMarkdownUpload, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/uploads", PathEscape(project)) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } var uploads []*ProjectMarkdownUpload resp, err := s.client.Do(req, &uploads) if err != nil { return nil, resp, err } return uploads, resp, err } // DownloadProjectMarkdownUploadByID downloads a specific upload by ID. // // GitLab API Docs: // https://docs.gitlab.com/ee/api/project_markdown_uploads.html#download-an-uploaded-file-by-id func (s *ProjectMarkdownUploadsService) DownloadProjectMarkdownUploadByID(pid interface{}, uploadID int, options ...RequestOptionFunc) ([]byte, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/uploads/%d", PathEscape(project), uploadID) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } var f bytes.Buffer resp, err := s.client.Do(req, &f) if err != nil { return nil, resp, err } return f.Bytes(), resp, err } // DownloadProjectMarkdownUploadBySecretAndFilename downloads a specific upload // by secret and filename. // // GitLab API Docs: // https://docs.gitlab.com/ee/api/project_markdown_uploads.html#download-an-uploaded-file-by-secret-and-filename func (s *ProjectMarkdownUploadsService) DownloadProjectMarkdownUploadBySecretAndFilename(pid interface{}, secret string, filename string, options ...RequestOptionFunc) ([]byte, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/uploads/%s/%s", PathEscape(project), PathEscape(secret), PathEscape(filename)) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } var f bytes.Buffer resp, err := s.client.Do(req, &f) if err != nil { return nil, resp, err } return f.Bytes(), resp, err } // DeleteProjectMarkdownUploadByID deletes an upload by ID. // // GitLab API Docs: // https://docs.gitlab.com/ee/api/project_markdown_uploads.html#delete-an-uploaded-file-by-id func (s *ProjectMarkdownUploadsService) DeleteProjectMarkdownUploadByID(pid interface{}, uploadID int, options ...RequestOptionFunc) (*Response, error) { project, err := parseID(pid) if err != nil { return nil, err } u := fmt.Sprintf("projects/%s/uploads/%d", PathEscape(project), uploadID) req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } // DeleteProjectMarkdownUploadBySecretAndFilename deletes an upload // by secret and filename. // // GitLab API Docs: // https://docs.gitlab.com/ee/api/project_markdown_uploads.html#delete-an-uploaded-file-by-secret-and-filename func (s *ProjectMarkdownUploadsService) DeleteProjectMarkdownUploadBySecretAndFilename(pid interface{}, secret string, filename string, options ...RequestOptionFunc) (*Response, error) { project, err := parseID(pid) if err != nil { return nil, err } u := fmt.Sprintf("projects/%s/uploads/%s/%s", PathEscape(project), PathEscape(secret), PathEscape(filename)) req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } golang-gitlab-gitlab-org-api-client-go-0.123.0/project_markdown_uploads_test.go000066400000000000000000000142051475761473200275460ustar00rootroot00000000000000package gitlab import ( "fmt" "net/http" "os" "strings" "testing" "time" "github.com/stretchr/testify/require" ) func TestProjectMarkdownUploads_UploadProjectMarkdown(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/uploads", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) if !strings.Contains(r.Header.Get("Content-Type"), "multipart/form-data;") { t.Fatalf("Projects.UploadFile request content-type %+v want multipart/form-data;", r.Header.Get("Content-Type")) } if r.ContentLength == -1 { t.Fatalf("Projects.UploadFile request content-length is -1") } fmt.Fprint(w, ` { "id": 5, "alt": "dk", "url": "/uploads/66dbcd21ec5d24ed6ea225176098d52b/dk.png", "full_path": "/-/project/1234/uploads/66dbcd21ec5d24ed6ea225176098d52b/dk.png", "markdown": "![dk](/uploads/66dbcd21ec5d24ed6ea225176098d52b/dk.png)" } `) }) want := &ProjectMarkdownUploadedFile{ ID: 5, Alt: "dk", URL: "/uploads/66dbcd21ec5d24ed6ea225176098d52b/dk.png", FullPath: "/-/project/1234/uploads/66dbcd21ec5d24ed6ea225176098d52b/dk.png", Markdown: "![dk](/uploads/66dbcd21ec5d24ed6ea225176098d52b/dk.png)", } b := strings.NewReader("dummy") upload, resp, err := client.ProjectMarkdownUploads.UploadProjectMarkdown(1, b, "test.txt") require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, want, upload) } func TestProjectMarkdownUploads_UploadProjectMarkdown_Retry(t *testing.T) { mux, client := setup(t) tf, _ := os.CreateTemp(os.TempDir(), "test") defer os.Remove(tf.Name()) isFirstRequest := true mux.HandleFunc("/api/v4/projects/1/uploads", func(w http.ResponseWriter, r *http.Request) { if isFirstRequest { http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) isFirstRequest = false return } if !strings.Contains(r.Header.Get("Content-Type"), "multipart/form-data;") { t.Fatalf("Projects.UploadFile request content-type %+v want multipart/form-data;", r.Header.Get("Content-Type")) } if r.ContentLength == -1 { t.Fatalf("Projects.UploadFile request content-length is -1") } fmt.Fprint(w, ` { "id": 5, "alt": "dk", "url": "/uploads/66dbcd21ec5d24ed6ea225176098d52b/dk.png", "full_path": "/-/project/1234/uploads/66dbcd21ec5d24ed6ea225176098d52b/dk.png", "markdown": "![dk](/uploads/66dbcd21ec5d24ed6ea225176098d52b/dk.png)" } `) }) want := &ProjectMarkdownUploadedFile{ ID: 5, Alt: "dk", URL: "/uploads/66dbcd21ec5d24ed6ea225176098d52b/dk.png", FullPath: "/-/project/1234/uploads/66dbcd21ec5d24ed6ea225176098d52b/dk.png", Markdown: "![dk](/uploads/66dbcd21ec5d24ed6ea225176098d52b/dk.png)", } b := strings.NewReader("dummy") upload, resp, err := client.ProjectMarkdownUploads.UploadProjectMarkdown(1, b, "test.txt") require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, want, upload) } func TestProjectMarkdownUploads_ListProjectMarkdownUploads(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/uploads", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, ` [ { "id": 1, "size": 1024, "filename": "image.png", "created_at":"2024-06-20T15:53:03.000Z", "uploaded_by": { "id": 18, "name" : "Alexandra Bashirian", "username" : "eileen.lowe" } }, { "id": 2, "size": 512, "filename": "other-image.png", "created_at":"2024-06-19T15:53:03.000Z", "uploaded_by": null } ] `) }) created1 := time.Date(2024, 6, 20, 15, 53, 3, 0, time.UTC) created2 := time.Date(2024, 6, 19, 15, 53, 3, 0, time.UTC) want := []*ProjectMarkdownUpload{ { ID: 1, Size: 1024, Filename: "image.png", CreatedAt: &created1, UploadedBy: &User{ ID: 18, Name: "Alexandra Bashirian", Username: "eileen.lowe", }, }, { ID: 2, Size: 512, Filename: "other-image.png", CreatedAt: &created2, }, } uploads, resp, err := client.ProjectMarkdownUploads.ListProjectMarkdownUploads(1) require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, want, uploads) } func TestProjectMarkdownUploads_DownloadProjectMarkdownUploadByID(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/uploads/2", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, strings.TrimSpace(` bar = baz `)) }) want := []byte("bar = baz") bytes, resp, err := client.ProjectMarkdownUploads.DownloadProjectMarkdownUploadByID(1, 2) require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, want, bytes) } func TestProjectMarkdownUploads_DownloadProjectMarkdownUploadBySecretAndFilename(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/uploads/secret/filename", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, strings.TrimSpace(` bar = baz `)) }) want := []byte("bar = baz") bytes, resp, err := client.ProjectMarkdownUploads.DownloadProjectMarkdownUploadBySecretAndFilename(1, "secret", "filename") require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, want, bytes) } func TestProjectMarkdownUploads_DeleteProjectMarkdownUploadByID(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/uploads/2", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodDelete) w.WriteHeader(204) }) resp, err := client.ProjectMarkdownUploads.DeleteProjectMarkdownUploadByID(1, 2) require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, 204, resp.StatusCode) } func TestProjectMarkdownUploads_DeleteProjectMarkdownUploadBySecretAndFilename(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/uploads/secret/filename", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodDelete) w.WriteHeader(204) }) resp, err := client.ProjectMarkdownUploads.DeleteProjectMarkdownUploadBySecretAndFilename(1, "secret", "filename") require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, 204, resp.StatusCode) } golang-gitlab-gitlab-org-api-client-go-0.123.0/project_members.go000066400000000000000000000203771475761473200245770ustar00rootroot00000000000000// // Copyright 2021, Sander van Harmelen // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "fmt" "net/http" "time" ) // ProjectMembersService handles communication with the project members // related methods of the GitLab API. // // GitLab API docs: https://docs.gitlab.com/ee/api/members.html type ProjectMembersService struct { client *Client } // ProjectMember represents a project member. // // GitLab API docs: // https://docs.gitlab.com/ee/api/members.html type ProjectMember struct { ID int `json:"id"` Username string `json:"username"` Email string `json:"email"` Name string `json:"name"` State string `json:"state"` CreatedAt *time.Time `json:"created_at"` ExpiresAt *ISOTime `json:"expires_at"` AccessLevel AccessLevelValue `json:"access_level"` WebURL string `json:"web_url"` AvatarURL string `json:"avatar_url"` MemberRole *MemberRole `json:"member_role"` } // ListProjectMembersOptions represents the available ListProjectMembers() and // ListAllProjectMembers() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/members.html#list-all-members-of-a-group-or-project type ListProjectMembersOptions struct { ListOptions Query *string `url:"query,omitempty" json:"query,omitempty"` UserIDs *[]int `url:"user_ids[],omitempty" json:"user_ids,omitempty"` } // ListProjectMembers gets a list of a project's team members viewable by the // authenticated user. Returns only direct members and not inherited members // through ancestors groups. // // GitLab API docs: // https://docs.gitlab.com/ee/api/members.html#list-all-members-of-a-group-or-project func (s *ProjectMembersService) ListProjectMembers(pid interface{}, opt *ListProjectMembersOptions, options ...RequestOptionFunc) ([]*ProjectMember, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/members", PathEscape(project)) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } var pm []*ProjectMember resp, err := s.client.Do(req, &pm) if err != nil { return nil, resp, err } return pm, resp, nil } // ListAllProjectMembers gets a list of a project's team members viewable by the // authenticated user. Returns a list including inherited members through // ancestor groups. // // GitLab API docs: // https://docs.gitlab.com/ee/api/members.html#list-all-members-of-a-group-or-project-including-inherited-and-invited-members func (s *ProjectMembersService) ListAllProjectMembers(pid interface{}, opt *ListProjectMembersOptions, options ...RequestOptionFunc) ([]*ProjectMember, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/members/all", PathEscape(project)) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } var pm []*ProjectMember resp, err := s.client.Do(req, &pm) if err != nil { return nil, resp, err } return pm, resp, nil } // GetProjectMember gets a project team member. // // GitLab API docs: // https://docs.gitlab.com/ee/api/members.html#get-a-member-of-a-group-or-project func (s *ProjectMembersService) GetProjectMember(pid interface{}, user int, options ...RequestOptionFunc) (*ProjectMember, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/members/%d", PathEscape(project), user) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } pm := new(ProjectMember) resp, err := s.client.Do(req, pm) if err != nil { return nil, resp, err } return pm, resp, nil } // GetInheritedProjectMember gets a project team member, including inherited // // GitLab API docs: // https://docs.gitlab.com/ee/api/members.html#get-a-member-of-a-group-or-project-including-inherited-and-invited-members func (s *ProjectMembersService) GetInheritedProjectMember(pid interface{}, user int, options ...RequestOptionFunc) (*ProjectMember, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/members/all/%d", PathEscape(project), user) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } pm := new(ProjectMember) resp, err := s.client.Do(req, pm) if err != nil { return nil, resp, err } return pm, resp, nil } // AddProjectMemberOptions represents the available AddProjectMember() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/members.html#add-a-member-to-a-group-or-project type AddProjectMemberOptions struct { UserID interface{} `url:"user_id,omitempty" json:"user_id,omitempty"` AccessLevel *AccessLevelValue `url:"access_level,omitempty" json:"access_level,omitempty"` ExpiresAt *string `url:"expires_at,omitempty" json:"expires_at"` MemberRoleID *int `url:"member_role_id,omitempty" json:"member_role_id,omitempty"` } // AddProjectMember adds a user to a project team. This is an idempotent // method and can be called multiple times with the same parameters. Adding // team membership to a user that is already a member does not affect the // existing membership. // // GitLab API docs: // https://docs.gitlab.com/ee/api/members.html#add-a-member-to-a-group-or-project func (s *ProjectMembersService) AddProjectMember(pid interface{}, opt *AddProjectMemberOptions, options ...RequestOptionFunc) (*ProjectMember, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/members", PathEscape(project)) req, err := s.client.NewRequest(http.MethodPost, u, opt, options) if err != nil { return nil, nil, err } pm := new(ProjectMember) resp, err := s.client.Do(req, pm) if err != nil { return nil, resp, err } return pm, resp, nil } // EditProjectMemberOptions represents the available EditProjectMember() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/members.html#edit-a-member-of-a-group-or-project type EditProjectMemberOptions struct { AccessLevel *AccessLevelValue `url:"access_level,omitempty" json:"access_level,omitempty"` ExpiresAt *string `url:"expires_at,omitempty" json:"expires_at,omitempty"` MemberRoleID *int `url:"member_role_id,omitempty" json:"member_role_id,omitempty"` } // EditProjectMember updates a project team member to a specified access level.. // // GitLab API docs: // https://docs.gitlab.com/ee/api/members.html#edit-a-member-of-a-group-or-project func (s *ProjectMembersService) EditProjectMember(pid interface{}, user int, opt *EditProjectMemberOptions, options ...RequestOptionFunc) (*ProjectMember, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/members/%d", PathEscape(project), user) req, err := s.client.NewRequest(http.MethodPut, u, opt, options) if err != nil { return nil, nil, err } pm := new(ProjectMember) resp, err := s.client.Do(req, pm) if err != nil { return nil, resp, err } return pm, resp, nil } // DeleteProjectMember removes a user from a project team. // // GitLab API docs: // https://docs.gitlab.com/ee/api/members.html#remove-a-member-from-a-group-or-project func (s *ProjectMembersService) DeleteProjectMember(pid interface{}, user int, options ...RequestOptionFunc) (*Response, error) { project, err := parseID(pid) if err != nil { return nil, err } u := fmt.Sprintf("projects/%s/members/%d", PathEscape(project), user) req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } golang-gitlab-gitlab-org-api-client-go-0.123.0/project_members_test.go000066400000000000000000000316131475761473200256310ustar00rootroot00000000000000package gitlab import ( "fmt" "net/http" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestProjectMembersService_ListProjectMembers(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/members", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprintf(w, ` [ { "id": 1, "username": "venkatesh_thalluri", "name": "Venkatesh Thalluri", "state": "active", "avatar_url": "https://www.gravatar.com/avatar/c2525a7f58ae3776070e44c106c48e15?s=80&d=identicon", "web_url": "http://192.168.1.8:3000/root", "access_level": 30, "group_saml_identity": null } ] `) }) want := []*ProjectMember{{ ID: 1, Username: "venkatesh_thalluri", Email: "", Name: "Venkatesh Thalluri", State: "active", AccessLevel: 30, WebURL: "http://192.168.1.8:3000/root", AvatarURL: "https://www.gravatar.com/avatar/c2525a7f58ae3776070e44c106c48e15?s=80&d=identicon", }} pms, resp, err := client.ProjectMembers.ListProjectMembers(1, nil, nil) require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, want, pms) pms, resp, err = client.ProjectMembers.ListProjectMembers(1.01, nil, nil) require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string") require.Nil(t, resp) require.Nil(t, pms) pms, resp, err = client.ProjectMembers.ListProjectMembers(1, nil, errorOption) require.EqualError(t, err, "RequestOptionFunc returns an error") require.Nil(t, resp) require.Nil(t, pms) pms, resp, err = client.ProjectMembers.ListProjectMembers(2, nil, nil) require.Error(t, err) require.Nil(t, pms) require.Equal(t, http.StatusNotFound, resp.StatusCode) } func TestProjectMembersService_ListAllProjectMembers(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/members/all", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprintf(w, ` [ { "id": 1, "username": "venkatesh_thalluri", "name": "Venkatesh Thalluri", "state": "active", "avatar_url": "https://www.gravatar.com/avatar/c2525a7f58ae3776070e44c106c48e15?s=80&d=identicon", "web_url": "http://192.168.1.8:3000/root", "access_level": 30, "group_saml_identity": null } ] `) }) want := []*ProjectMember{{ ID: 1, Username: "venkatesh_thalluri", Email: "", Name: "Venkatesh Thalluri", State: "active", AccessLevel: 30, WebURL: "http://192.168.1.8:3000/root", AvatarURL: "https://www.gravatar.com/avatar/c2525a7f58ae3776070e44c106c48e15?s=80&d=identicon", }} pms, resp, err := client.ProjectMembers.ListAllProjectMembers(1, nil, nil) require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, want, pms) pms, resp, err = client.ProjectMembers.ListAllProjectMembers(1.01, nil, nil) require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string") require.Nil(t, resp) require.Nil(t, pms) pms, resp, err = client.ProjectMembers.ListAllProjectMembers(1, nil, errorOption) require.EqualError(t, err, "RequestOptionFunc returns an error") require.Nil(t, resp) require.Nil(t, pms) pms, resp, err = client.ProjectMembers.ListAllProjectMembers(2, nil, nil) require.Error(t, err) require.Nil(t, pms) require.Equal(t, http.StatusNotFound, resp.StatusCode) } func TestProjectMembersService_GetProjectMember(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/members/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprintf(w, ` { "id": 1, "username": "venkatesh_thalluri", "name": "Venkatesh Thalluri", "state": "active", "avatar_url": "https://www.gravatar.com/avatar/c2525a7f58ae3776070e44c106c48e15?s=80&d=identicon", "web_url": "http://192.168.1.8:3000/root", "access_level": 30, "email": "venkatesh.thalluri@example.com", "expires_at": null, "group_saml_identity": null } `) }) want := &ProjectMember{ ID: 1, Username: "venkatesh_thalluri", Email: "venkatesh.thalluri@example.com", Name: "Venkatesh Thalluri", State: "active", ExpiresAt: nil, AccessLevel: 30, WebURL: "http://192.168.1.8:3000/root", AvatarURL: "https://www.gravatar.com/avatar/c2525a7f58ae3776070e44c106c48e15?s=80&d=identicon", } pm, resp, err := client.ProjectMembers.GetProjectMember(1, 1, nil, nil) require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, want, pm) pm, resp, err = client.ProjectMembers.GetProjectMember(1.01, 1, nil, nil) require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string") require.Nil(t, resp) require.Nil(t, pm) pm, resp, err = client.ProjectMembers.GetProjectMember(1, 1, nil, errorOption) require.EqualError(t, err, "RequestOptionFunc returns an error") require.Nil(t, resp) require.Nil(t, pm) pm, resp, err = client.ProjectMembers.GetProjectMember(2, 1, nil, nil) require.Error(t, err) require.Nil(t, pm) require.Equal(t, http.StatusNotFound, resp.StatusCode) } func TestProjectMembersService_GetInheritedProjectMember(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/members/all/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprintf(w, ` { "id": 1, "username": "venkatesh_thalluri", "name": "Venkatesh Thalluri", "state": "active", "avatar_url": "https://www.gravatar.com/avatar/c2525a7f58ae3776070e44c106c48e15?s=80&d=identicon", "web_url": "http://192.168.1.8:3000/root", "access_level": 30, "email": "venkatesh.thalluri@example.com", "expires_at": null, "group_saml_identity": null } `) }) want := &ProjectMember{ ID: 1, Username: "venkatesh_thalluri", Email: "venkatesh.thalluri@example.com", Name: "Venkatesh Thalluri", State: "active", ExpiresAt: nil, AccessLevel: 30, WebURL: "http://192.168.1.8:3000/root", AvatarURL: "https://www.gravatar.com/avatar/c2525a7f58ae3776070e44c106c48e15?s=80&d=identicon", } pm, resp, err := client.ProjectMembers.GetInheritedProjectMember(1, 1, nil, nil) require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, want, pm) pm, resp, err = client.ProjectMembers.GetInheritedProjectMember(1.01, 1, nil, nil) require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string") require.Nil(t, resp) require.Nil(t, pm) pm, resp, err = client.ProjectMembers.GetInheritedProjectMember(1, 1, nil, errorOption) require.EqualError(t, err, "RequestOptionFunc returns an error") require.Nil(t, resp) require.Nil(t, pm) pm, resp, err = client.ProjectMembers.GetInheritedProjectMember(2, 1, nil, nil) require.Error(t, err) require.Nil(t, pm) require.Equal(t, http.StatusNotFound, resp.StatusCode) } func TestProjectMembersService_AddProjectMember(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/members", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) fmt.Fprintf(w, ` { "id": 1, "username": "venkatesh_thalluri", "name": "Venkatesh Thalluri", "state": "active", "avatar_url": "https://www.gravatar.com/avatar/c2525a7f58ae3776070e44c106c48e15?s=80&d=identicon", "web_url": "http://192.168.1.8:3000/root", "access_level": 30, "email": "venkatesh.thalluri@example.com", "expires_at": null, "group_saml_identity": null } `) }) want := &ProjectMember{ ID: 1, Username: "venkatesh_thalluri", Email: "venkatesh.thalluri@example.com", Name: "Venkatesh Thalluri", State: "active", ExpiresAt: nil, AccessLevel: 30, WebURL: "http://192.168.1.8:3000/root", AvatarURL: "https://www.gravatar.com/avatar/c2525a7f58ae3776070e44c106c48e15?s=80&d=identicon", } pm, resp, err := client.ProjectMembers.AddProjectMember(1, nil, nil) require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, want, pm) pm, resp, err = client.ProjectMembers.AddProjectMember(1.01, nil, nil) require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string") require.Nil(t, resp) require.Nil(t, pm) pm, resp, err = client.ProjectMembers.AddProjectMember(1, nil, errorOption) require.EqualError(t, err, "RequestOptionFunc returns an error") require.Nil(t, resp) require.Nil(t, pm) pm, resp, err = client.ProjectMembers.AddProjectMember(2, nil, nil) require.Error(t, err) require.Nil(t, pm) require.Equal(t, http.StatusNotFound, resp.StatusCode) } func TestProjectMembersService_EditProjectMember(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/members/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPut) fmt.Fprintf(w, ` { "id": 1, "username": "venkatesh_thalluri", "name": "Venkatesh Thalluri", "state": "active", "avatar_url": "https://www.gravatar.com/avatar/c2525a7f58ae3776070e44c106c48e15?s=80&d=identicon", "web_url": "http://192.168.1.8:3000/root", "access_level": 30, "email": "venkatesh.thalluri@example.com", "expires_at": null, "group_saml_identity": null } `) }) want := &ProjectMember{ ID: 1, Username: "venkatesh_thalluri", Email: "venkatesh.thalluri@example.com", Name: "Venkatesh Thalluri", State: "active", ExpiresAt: nil, AccessLevel: 30, WebURL: "http://192.168.1.8:3000/root", AvatarURL: "https://www.gravatar.com/avatar/c2525a7f58ae3776070e44c106c48e15?s=80&d=identicon", } pm, resp, err := client.ProjectMembers.EditProjectMember(1, 1, nil, nil) require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, want, pm) pm, resp, err = client.ProjectMembers.EditProjectMember(1.01, 1, nil, nil) require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string") require.Nil(t, resp) require.Nil(t, pm) pm, resp, err = client.ProjectMembers.EditProjectMember(1, 1, nil, errorOption) require.EqualError(t, err, "RequestOptionFunc returns an error") require.Nil(t, resp) require.Nil(t, pm) pm, resp, err = client.ProjectMembers.EditProjectMember(2, 1, nil, nil) require.Error(t, err) require.Nil(t, pm) require.Equal(t, http.StatusNotFound, resp.StatusCode) } func TestProjectMembersService_DeleteProjectMember(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/members/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodDelete) fmt.Fprintf(w, ` { "id": 1, "username": "venkatesh_thalluri", "name": "Venkatesh Thalluri", "state": "active", "avatar_url": "https://www.gravatar.com/avatar/c2525a7f58ae3776070e44c106c48e15?s=80&d=identicon", "web_url": "http://192.168.1.8:3000/root", "access_level": 30, "email": "venkatesh.thalluri@example.com", "expires_at": null, "group_saml_identity": null } `) }) resp, err := client.ProjectMembers.DeleteProjectMember(1, 1, nil, nil) require.NoError(t, err) require.NotNil(t, resp) resp, err = client.ProjectMembers.DeleteProjectMember(1.01, 1, nil, nil) require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string") require.Nil(t, resp) resp, err = client.ProjectMembers.DeleteProjectMember(1, 1, nil, errorOption) require.EqualError(t, err, "RequestOptionFunc returns an error") require.Nil(t, resp) resp, err = client.ProjectMembers.DeleteProjectMember(2, 1, nil, nil) require.Error(t, err) require.Equal(t, http.StatusNotFound, resp.StatusCode) } func TestProjectMembersService_CustomRole(t *testing.T) { mux, client := setup(t) path := fmt.Sprintf("/%sprojects/1/members/2", apiVersionPath) mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, ` { "id":1, "username":"test", "name":"testName", "access_level":30, "member_role":{ "id":1, "group_id":2, "name":"TestingCustomRole", "description":"", "base_access_level":30, "admin_cicd_variables":null, "admin_group_member":null, "admin_merge_request":null, "admin_push_rules":null, "admin_terraform_state":null, "admin_vulnerability":null, "archive_project":null, "manage_group_access_tokens":null, "manage_project_access_tokens":null, "read_code":true, "read_dependency":null, "read_vulnerability":null, "remove_group":null, "remove_project":null } } `) }) want := &ProjectMember{ ID: 1, Username: "test", Name: "testName", AccessLevel: AccessLevelValue(30), MemberRole: &MemberRole{ ID: 1, GroupID: 2, Name: "TestingCustomRole", Description: "", BaseAccessLevel: AccessLevelValue(30), ReadCode: true, }, } member, _, err := client.ProjectMembers.GetProjectMember(1, 2) assert.NoError(t, err) assert.Equal(t, want, member) } golang-gitlab-gitlab-org-api-client-go-0.123.0/project_mirror.go000066400000000000000000000150541475761473200244530ustar00rootroot00000000000000// // Copyright 2021, Sander van Harmelen // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "fmt" "net/http" "time" ) // ProjectMirrorService handles communication with the project mirror // related methods of the GitLab API. // // GitLAb API docs: https://docs.gitlab.com/ee/api/remote_mirrors.html type ProjectMirrorService struct { client *Client } // ProjectMirror represents a project mirror configuration. // // GitLAb API docs: https://docs.gitlab.com/ee/api/remote_mirrors.html type ProjectMirror struct { Enabled bool `json:"enabled"` ID int `json:"id"` LastError string `json:"last_error"` LastSuccessfulUpdateAt *time.Time `json:"last_successful_update_at"` LastUpdateAt *time.Time `json:"last_update_at"` LastUpdateStartedAt *time.Time `json:"last_update_started_at"` MirrorBranchRegex string `json:"mirror_branch_regex"` OnlyProtectedBranches bool `json:"only_protected_branches"` KeepDivergentRefs bool `json:"keep_divergent_refs"` UpdateStatus string `json:"update_status"` URL string `json:"url"` } // ListProjectMirrorOptions represents the available ListProjectMirror() options. type ListProjectMirrorOptions ListOptions // ListProjectMirror gets a list of mirrors configured on the project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/remote_mirrors.html#list-a-projects-remote-mirrors func (s *ProjectMirrorService) ListProjectMirror(pid interface{}, opt *ListProjectMirrorOptions, options ...RequestOptionFunc) ([]*ProjectMirror, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/remote_mirrors", PathEscape(project)) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } var pm []*ProjectMirror resp, err := s.client.Do(req, &pm) if err != nil { return nil, resp, err } return pm, resp, nil } // GetProjectMirror gets a single mirror configured on the project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/remote_mirrors.html#get-a-single-projects-remote-mirror func (s *ProjectMirrorService) GetProjectMirror(pid interface{}, mirror int, options ...RequestOptionFunc) (*ProjectMirror, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/remote_mirrors/%d", PathEscape(project), mirror) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } pm := new(ProjectMirror) resp, err := s.client.Do(req, &pm) if err != nil { return nil, resp, err } return pm, resp, nil } // AddProjectMirrorOptions contains the properties requires to create // a new project mirror. // // GitLab API docs: // https://docs.gitlab.com/ee/api/remote_mirrors.html#create-a-push-mirror type AddProjectMirrorOptions struct { URL *string `url:"url,omitempty" json:"url,omitempty"` Enabled *bool `url:"enabled,omitempty" json:"enabled,omitempty"` KeepDivergentRefs *bool `url:"keep_divergent_refs,omitempty" json:"keep_divergent_refs,omitempty"` OnlyProtectedBranches *bool `url:"only_protected_branches,omitempty" json:"only_protected_branches,omitempty"` MirrorBranchRegex *string `url:"mirror_branch_regex,omitempty" json:"mirror_branch_regex,omitempty"` } // AddProjectMirror creates a new mirror on the project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/remote_mirrors.html#create-a-push-mirror func (s *ProjectMirrorService) AddProjectMirror(pid interface{}, opt *AddProjectMirrorOptions, options ...RequestOptionFunc) (*ProjectMirror, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/remote_mirrors", PathEscape(project)) req, err := s.client.NewRequest(http.MethodPost, u, opt, options) if err != nil { return nil, nil, err } pm := new(ProjectMirror) resp, err := s.client.Do(req, pm) if err != nil { return nil, resp, err } return pm, resp, nil } // EditProjectMirrorOptions contains the properties requires to edit // an existing project mirror. // // GitLab API docs: // https://docs.gitlab.com/ee/api/remote_mirrors.html#update-a-remote-mirrors-attributes type EditProjectMirrorOptions struct { Enabled *bool `url:"enabled,omitempty" json:"enabled,omitempty"` KeepDivergentRefs *bool `url:"keep_divergent_refs,omitempty" json:"keep_divergent_refs,omitempty"` OnlyProtectedBranches *bool `url:"only_protected_branches,omitempty" json:"only_protected_branches,omitempty"` MirrorBranchRegex *string `url:"mirror_branch_regex,omitempty" json:"mirror_branch_regex,omitempty"` } // EditProjectMirror updates a project team member to a specified access level.. // // GitLab API docs: // https://docs.gitlab.com/ee/api/remote_mirrors.html#update-a-remote-mirrors-attributes func (s *ProjectMirrorService) EditProjectMirror(pid interface{}, mirror int, opt *EditProjectMirrorOptions, options ...RequestOptionFunc) (*ProjectMirror, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/remote_mirrors/%d", PathEscape(project), mirror) req, err := s.client.NewRequest(http.MethodPut, u, opt, options) if err != nil { return nil, nil, err } pm := new(ProjectMirror) resp, err := s.client.Do(req, pm) if err != nil { return nil, resp, err } return pm, resp, nil } // DeleteProjectMirror deletes a project mirror. // // GitLab API docs: // https://docs.gitlab.com/ee/api/remote_mirrors.html#delete-a-remote-mirror func (s *ProjectMirrorService) DeleteProjectMirror(pid interface{}, mirror int, options ...RequestOptionFunc) (*Response, error) { project, err := parseID(pid) if err != nil { return nil, err } u := fmt.Sprintf("projects/%s/remote_mirrors/%d", PathEscape(project), mirror) req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } golang-gitlab-gitlab-org-api-client-go-0.123.0/project_mirror_test.go000066400000000000000000000134251475761473200255120ustar00rootroot00000000000000package gitlab import ( "fmt" "net/http" "testing" "github.com/stretchr/testify/require" ) func TestProjectMirrorService_ListProjectMirror(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/42/remote_mirrors", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprintf(w, ` [ { "enabled": true, "id": 101486, "last_error": null, "only_protected_branches": true, "keep_divergent_refs": true, "update_status": "finished", "url": "https://*****:*****@gitlab.com/gitlab-org/security/gitlab.git" } ] `) }) want := []*ProjectMirror{{ Enabled: true, ID: 101486, LastError: "", OnlyProtectedBranches: true, KeepDivergentRefs: true, UpdateStatus: "finished", URL: "https://*****:*****@gitlab.com/gitlab-org/security/gitlab.git", }} pms, resp, err := client.ProjectMirrors.ListProjectMirror(42, nil, nil) require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, want, pms) pms, resp, err = client.ProjectMirrors.ListProjectMirror(42.01, nil, nil) require.EqualError(t, err, "invalid ID type 42.01, the ID must be an int or a string") require.Nil(t, resp) require.Nil(t, pms) pms, resp, err = client.ProjectMirrors.ListProjectMirror(42, nil, errorOption) require.EqualError(t, err, "RequestOptionFunc returns an error") require.Nil(t, resp) require.Nil(t, pms) pms, resp, err = client.ProjectMirrors.ListProjectMirror(43, nil, nil) require.Error(t, err) require.Nil(t, pms) require.Equal(t, http.StatusNotFound, resp.StatusCode) } func TestProjectMirrorService_GetProjectMirror(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/42/remote_mirrors/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprintf(w, ` { "enabled": true, "id": 101486, "last_error": null, "only_protected_branches": true, "keep_divergent_refs": true, "update_status": "finished", "url": "https://*****:*****@gitlab.com/gitlab-org/security/gitlab.git" } `) }) want := &ProjectMirror{ Enabled: true, ID: 101486, LastError: "", OnlyProtectedBranches: true, KeepDivergentRefs: true, UpdateStatus: "finished", URL: "https://*****:*****@gitlab.com/gitlab-org/security/gitlab.git", } pm, resp, err := client.ProjectMirrors.GetProjectMirror(42, 1, nil) require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, want, pm) } func TestProjectMirrorService_AddProjectMirror(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/42/remote_mirrors", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) fmt.Fprintf(w, ` { "enabled": false, "id": 101486, "last_error": null, "last_successful_update_at": null, "last_update_at": null, "last_update_started_at": null, "only_protected_branches": false, "keep_divergent_refs": false, "update_status": "none", "url": "https://*****:*****@example.com/gitlab/example.git" } `) }) want := &ProjectMirror{ Enabled: false, ID: 101486, LastError: "", LastSuccessfulUpdateAt: nil, LastUpdateAt: nil, LastUpdateStartedAt: nil, OnlyProtectedBranches: false, KeepDivergentRefs: false, UpdateStatus: "none", URL: "https://*****:*****@example.com/gitlab/example.git", } pm, resp, err := client.ProjectMirrors.AddProjectMirror(42, nil, nil) require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, want, pm) pm, resp, err = client.ProjectMirrors.AddProjectMirror(42.01, nil, nil) require.EqualError(t, err, "invalid ID type 42.01, the ID must be an int or a string") require.Nil(t, resp) require.Nil(t, pm) pm, resp, err = client.ProjectMirrors.AddProjectMirror(42, nil, errorOption) require.EqualError(t, err, "RequestOptionFunc returns an error") require.Nil(t, resp) require.Nil(t, pm) pm, resp, err = client.ProjectMirrors.AddProjectMirror(43, nil, nil) require.Error(t, err) require.Nil(t, pm) require.Equal(t, http.StatusNotFound, resp.StatusCode) } func TestProjectMirrorService_EditProjectMirror(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/42/remote_mirrors/101486", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPut) fmt.Fprintf(w, ` { "enabled": false, "id": 101486, "last_error": null, "only_protected_branches": true, "keep_divergent_refs": true, "update_status": "finished", "url": "https://*****:*****@gitlab.com/gitlab-org/security/gitlab.git" } `) }) want := &ProjectMirror{ Enabled: false, ID: 101486, LastError: "", OnlyProtectedBranches: true, KeepDivergentRefs: true, UpdateStatus: "finished", URL: "https://*****:*****@gitlab.com/gitlab-org/security/gitlab.git", } pm, resp, err := client.ProjectMirrors.EditProjectMirror(42, 101486, nil, nil) require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, want, pm) pm, resp, err = client.ProjectMirrors.EditProjectMirror(42.01, 101486, nil, nil) require.EqualError(t, err, "invalid ID type 42.01, the ID must be an int or a string") require.Nil(t, resp) require.Nil(t, pm) pm, resp, err = client.ProjectMirrors.EditProjectMirror(42, 101486, nil, errorOption) require.EqualError(t, err, "RequestOptionFunc returns an error") require.Nil(t, resp) require.Nil(t, pm) pm, resp, err = client.ProjectMirrors.EditProjectMirror(43, 101486, nil, nil) require.Error(t, err) require.Nil(t, pm) require.Equal(t, http.StatusNotFound, resp.StatusCode) } golang-gitlab-gitlab-org-api-client-go-0.123.0/project_repository_storage_move.go000066400000000000000000000166441475761473200301400ustar00rootroot00000000000000// // Copyright 2023, Nick Westbury // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "fmt" "net/http" "time" ) // ProjectRepositoryStorageMoveService handles communication with the // repositories related methods of the GitLab API. // // GitLab API docs: // https://docs.gitlab.com/ee/api/project_repository_storage_moves.html type ProjectRepositoryStorageMoveService struct { client *Client } // ProjectRepositoryStorageMove represents the status of a repository move. // // GitLab API docs: // https://docs.gitlab.com/ee/api/project_repository_storage_moves.html type ProjectRepositoryStorageMove struct { ID int `json:"id"` CreatedAt *time.Time `json:"created_at"` State string `json:"state"` SourceStorageName string `json:"source_storage_name"` DestinationStorageName string `json:"destination_storage_name"` Project *RepositoryProject `json:"project"` } type RepositoryProject struct { ID int `json:"id"` Description string `json:"description"` Name string `json:"name"` NameWithNamespace string `json:"name_with_namespace"` Path string `json:"path"` PathWithNamespace string `json:"path_with_namespace"` CreatedAt *time.Time `json:"created_at"` } // RetrieveAllProjectStorageMovesOptions represents the available // RetrieveAllStorageMoves() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/project_repository_storage_moves.html#retrieve-all-project-repository-storage-moves type RetrieveAllProjectStorageMovesOptions ListOptions // RetrieveAllStorageMoves retrieves all project repository storage moves // accessible by the authenticated user. // // GitLab API docs: // https://docs.gitlab.com/ee/api/project_repository_storage_moves.html#retrieve-all-project-repository-storage-moves func (p ProjectRepositoryStorageMoveService) RetrieveAllStorageMoves(opts RetrieveAllProjectStorageMovesOptions, options ...RequestOptionFunc) ([]*ProjectRepositoryStorageMove, *Response, error) { req, err := p.client.NewRequest(http.MethodGet, "project_repository_storage_moves", opts, options) if err != nil { return nil, nil, err } var psms []*ProjectRepositoryStorageMove resp, err := p.client.Do(req, &psms) if err != nil { return nil, resp, err } return psms, resp, err } // RetrieveAllStorageMovesForProject retrieves all repository storage moves for // a single project accessible by the authenticated user. // // GitLab API docs: // https://docs.gitlab.com/ee/api/project_repository_storage_moves.html#retrieve-all-repository-storage-moves-for-a-project func (p ProjectRepositoryStorageMoveService) RetrieveAllStorageMovesForProject(project int, opts RetrieveAllProjectStorageMovesOptions, options ...RequestOptionFunc) ([]*ProjectRepositoryStorageMove, *Response, error) { u := fmt.Sprintf("projects/%d/repository_storage_moves", project) req, err := p.client.NewRequest(http.MethodGet, u, opts, options) if err != nil { return nil, nil, err } var psms []*ProjectRepositoryStorageMove resp, err := p.client.Do(req, &psms) if err != nil { return nil, resp, err } return psms, resp, err } // GetStorageMove gets a single project repository storage move. // // GitLab API docs: // https://docs.gitlab.com/ee/api/project_repository_storage_moves.html#get-a-single-project-repository-storage-move func (p ProjectRepositoryStorageMoveService) GetStorageMove(repositoryStorage int, options ...RequestOptionFunc) (*ProjectRepositoryStorageMove, *Response, error) { u := fmt.Sprintf("project_repository_storage_moves/%d", repositoryStorage) req, err := p.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } psm := new(ProjectRepositoryStorageMove) resp, err := p.client.Do(req, psm) if err != nil { return nil, resp, err } return psm, resp, err } // GetStorageMoveForProject gets a single repository storage move for a project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/project_repository_storage_moves.html#get-a-single-repository-storage-move-for-a-project func (p ProjectRepositoryStorageMoveService) GetStorageMoveForProject(project int, repositoryStorage int, options ...RequestOptionFunc) (*ProjectRepositoryStorageMove, *Response, error) { u := fmt.Sprintf("projects/%d/repository_storage_moves/%d", project, repositoryStorage) req, err := p.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } psm := new(ProjectRepositoryStorageMove) resp, err := p.client.Do(req, psm) if err != nil { return nil, resp, err } return psm, resp, err } // ScheduleStorageMoveForProjectOptions represents the available // ScheduleStorageMoveForProject() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/project_repository_storage_moves.html#schedule-a-repository-storage-move-for-a-project type ScheduleStorageMoveForProjectOptions struct { DestinationStorageName *string `url:"destination_storage_name,omitempty" json:"destination_storage_name,omitempty"` } // ScheduleStorageMoveForProject schedule a repository to be moved for a project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/project_repository_storage_moves.html#schedule-a-repository-storage-move-for-a-project func (p ProjectRepositoryStorageMoveService) ScheduleStorageMoveForProject(project int, opts ScheduleStorageMoveForProjectOptions, options ...RequestOptionFunc) (*ProjectRepositoryStorageMove, *Response, error) { u := fmt.Sprintf("projects/%d/repository_storage_moves", project) req, err := p.client.NewRequest(http.MethodPost, u, opts, options) if err != nil { return nil, nil, err } psm := new(ProjectRepositoryStorageMove) resp, err := p.client.Do(req, psm) if err != nil { return nil, resp, err } return psm, resp, err } // ScheduleAllProjectStorageMovesOptions represents the available // ScheduleAllStorageMoves() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/project_repository_storage_moves.html#schedule-repository-storage-moves-for-all-projects-on-a-storage-shard type ScheduleAllProjectStorageMovesOptions struct { SourceStorageName *string `url:"source_storage_name,omitempty" json:"source_storage_name,omitempty"` DestinationStorageName *string `url:"destination_storage_name,omitempty" json:"destination_storage_name,omitempty"` } // ScheduleAllStorageMoves schedules all repositories to be moved. // // GitLab API docs: // https://docs.gitlab.com/ee/api/project_repository_storage_moves.html#schedule-repository-storage-moves-for-all-projects-on-a-storage-shard func (p ProjectRepositoryStorageMoveService) ScheduleAllStorageMoves(opts ScheduleAllProjectStorageMovesOptions, options ...RequestOptionFunc) (*Response, error) { req, err := p.client.NewRequest(http.MethodPost, "project_repository_storage_moves", opts, options) if err != nil { return nil, err } return p.client.Do(req, nil) } golang-gitlab-gitlab-org-api-client-go-0.123.0/project_repository_storage_move_test.go000066400000000000000000000110451475761473200311650ustar00rootroot00000000000000package gitlab import ( "fmt" "net/http" "testing" "github.com/stretchr/testify/require" ) func TestProjectRepositoryStorageMove_RetrieveAllProjectStorageMoves(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/project_repository_storage_moves", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, `[ { "id":123, "state":"scheduled", "project":{ "id":1, "name":"Test Project" } }, { "id":122, "state":"finished", "project":{ "id":2, "name":"Test Project 2" } }]`) }) opts := RetrieveAllProjectStorageMovesOptions{Page: 1, PerPage: 2} ssms, _, err := client.ProjectRepositoryStorageMove.RetrieveAllStorageMoves(opts) require.NoError(t, err) want := []*ProjectRepositoryStorageMove{ { ID: 123, State: "scheduled", Project: &RepositoryProject{ ID: 1, Name: "Test Project", }, }, { ID: 122, State: "finished", Project: &RepositoryProject{ ID: 2, Name: "Test Project 2", }, }, } require.Equal(t, want, ssms) } func TestProjectRepositoryStorageMove_RetrieveAllStorageMovesForProject(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/repository_storage_moves", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, `[ { "id":123, "state":"scheduled", "project":{ "id":1, "name":"Test Project" } }, { "id":122, "state":"finished", "project":{ "id":1, "name":"Test Project" } }]`) }) opts := RetrieveAllProjectStorageMovesOptions{Page: 1, PerPage: 2} ssms, _, err := client.ProjectRepositoryStorageMove.RetrieveAllStorageMovesForProject(1, opts) require.NoError(t, err) want := []*ProjectRepositoryStorageMove{ { ID: 123, State: "scheduled", Project: &RepositoryProject{ ID: 1, Name: "Test Project", }, }, { ID: 122, State: "finished", Project: &RepositoryProject{ ID: 1, Name: "Test Project", }, }, } require.Equal(t, want, ssms) } func TestProjectRepositoryStorageMove_GetStorageMove(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/project_repository_storage_moves/123", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, ` { "id":123, "state":"scheduled", "project":{ "id":1, "name":"Test Project" } }`) }) ssm, _, err := client.ProjectRepositoryStorageMove.GetStorageMove(123) require.NoError(t, err) want := &ProjectRepositoryStorageMove{ ID: 123, State: "scheduled", Project: &RepositoryProject{ ID: 1, Name: "Test Project", }, } require.Equal(t, want, ssm) } func TestProjectRepositoryStorageMove_GetStorageMoveForProject(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/repository_storage_moves/123", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, ` { "id":123, "state":"scheduled", "project":{ "id":1, "name":"Test Project" } }`) }) ssm, _, err := client.ProjectRepositoryStorageMove.GetStorageMoveForProject(1, 123) require.NoError(t, err) want := &ProjectRepositoryStorageMove{ ID: 123, State: "scheduled", Project: &RepositoryProject{ ID: 1, Name: "Test Project", }, } require.Equal(t, want, ssm) } func TestProjectRepositoryStorageMove_ScheduleStorageMoveForProject(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/repository_storage_moves", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) fmt.Fprint(w, ` { "id":124, "state":"scheduled", "project":{ "id":1, "name":"Test Project" } }`) }) ssm, _, err := client.ProjectRepositoryStorageMove.ScheduleStorageMoveForProject(1, ScheduleStorageMoveForProjectOptions{}) require.NoError(t, err) want := &ProjectRepositoryStorageMove{ ID: 124, State: "scheduled", Project: &RepositoryProject{ ID: 1, Name: "Test Project", }, } require.Equal(t, want, ssm) } func TestProjectRepositoryStorageMove_ScheduleAllStorageMoves(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/project_repository_storage_moves", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) fmt.Fprint(w, `{"message": "202 Accepted"}`) }) _, err := client.ProjectRepositoryStorageMove.ScheduleAllStorageMoves( ScheduleAllProjectStorageMovesOptions{ SourceStorageName: Ptr("default"), }, ) require.NoError(t, err) } golang-gitlab-gitlab-org-api-client-go-0.123.0/project_snippets.go000066400000000000000000000153511475761473200250060ustar00rootroot00000000000000// // Copyright 2021, Sander van Harmelen // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "bytes" "fmt" "net/http" ) // ProjectSnippetsService handles communication with the project snippets // related methods of the GitLab API. // // GitLab API docs: https://docs.gitlab.com/ee/api/project_snippets.html type ProjectSnippetsService struct { client *Client } // ListProjectSnippetsOptions represents the available ListSnippets() options. // // GitLab API docs: https://docs.gitlab.com/ee/api/project_snippets.html#list-snippets type ListProjectSnippetsOptions ListOptions // ListSnippets gets a list of project snippets. // // GitLab API docs: https://docs.gitlab.com/ee/api/project_snippets.html#list-snippets func (s *ProjectSnippetsService) ListSnippets(pid interface{}, opt *ListProjectSnippetsOptions, options ...RequestOptionFunc) ([]*Snippet, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/snippets", PathEscape(project)) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } var ps []*Snippet resp, err := s.client.Do(req, &ps) if err != nil { return nil, resp, err } return ps, resp, nil } // GetSnippet gets a single project snippet // // GitLab API docs: // https://docs.gitlab.com/ee/api/project_snippets.html#single-snippet func (s *ProjectSnippetsService) GetSnippet(pid interface{}, snippet int, options ...RequestOptionFunc) (*Snippet, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/snippets/%d", PathEscape(project), snippet) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } ps := new(Snippet) resp, err := s.client.Do(req, ps) if err != nil { return nil, resp, err } return ps, resp, nil } // CreateProjectSnippetOptions represents the available CreateSnippet() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/project_snippets.html#create-new-snippet type CreateProjectSnippetOptions struct { Title *string `url:"title,omitempty" json:"title,omitempty"` FileName *string `url:"file_name,omitempty" json:"file_name,omitempty"` Description *string `url:"description,omitempty" json:"description,omitempty"` Content *string `url:"content,omitempty" json:"content,omitempty"` Visibility *VisibilityValue `url:"visibility,omitempty" json:"visibility,omitempty"` Files *[]*CreateSnippetFileOptions `url:"files,omitempty" json:"files,omitempty"` } // CreateSnippet creates a new project snippet. The user must have permission // to create new snippets. // // GitLab API docs: // https://docs.gitlab.com/ee/api/project_snippets.html#create-new-snippet func (s *ProjectSnippetsService) CreateSnippet(pid interface{}, opt *CreateProjectSnippetOptions, options ...RequestOptionFunc) (*Snippet, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/snippets", PathEscape(project)) req, err := s.client.NewRequest(http.MethodPost, u, opt, options) if err != nil { return nil, nil, err } ps := new(Snippet) resp, err := s.client.Do(req, ps) if err != nil { return nil, resp, err } return ps, resp, nil } // UpdateProjectSnippetOptions represents the available UpdateSnippet() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/project_snippets.html#update-snippet type UpdateProjectSnippetOptions struct { Title *string `url:"title,omitempty" json:"title,omitempty"` FileName *string `url:"file_name,omitempty" json:"file_name,omitempty"` Description *string `url:"description,omitempty" json:"description,omitempty"` Content *string `url:"content,omitempty" json:"content,omitempty"` Visibility *VisibilityValue `url:"visibility,omitempty" json:"visibility,omitempty"` Files *[]*UpdateSnippetFileOptions `url:"files,omitempty" json:"files,omitempty"` } // UpdateSnippet updates an existing project snippet. The user must have // permission to change an existing snippet. // // GitLab API docs: // https://docs.gitlab.com/ee/api/project_snippets.html#update-snippet func (s *ProjectSnippetsService) UpdateSnippet(pid interface{}, snippet int, opt *UpdateProjectSnippetOptions, options ...RequestOptionFunc) (*Snippet, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/snippets/%d", PathEscape(project), snippet) req, err := s.client.NewRequest(http.MethodPut, u, opt, options) if err != nil { return nil, nil, err } ps := new(Snippet) resp, err := s.client.Do(req, ps) if err != nil { return nil, resp, err } return ps, resp, nil } // DeleteSnippet deletes an existing project snippet. This is an idempotent // function and deleting a non-existent snippet still returns a 200 OK status // code. // // GitLab API docs: // https://docs.gitlab.com/ee/api/project_snippets.html#delete-snippet func (s *ProjectSnippetsService) DeleteSnippet(pid interface{}, snippet int, options ...RequestOptionFunc) (*Response, error) { project, err := parseID(pid) if err != nil { return nil, err } u := fmt.Sprintf("projects/%s/snippets/%d", PathEscape(project), snippet) req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } // SnippetContent returns the raw project snippet as plain text. // // GitLab API docs: // https://docs.gitlab.com/ee/api/project_snippets.html#snippet-content func (s *ProjectSnippetsService) SnippetContent(pid interface{}, snippet int, options ...RequestOptionFunc) ([]byte, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/snippets/%d/raw", PathEscape(project), snippet) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } var b bytes.Buffer resp, err := s.client.Do(req, &b) if err != nil { return nil, resp, err } return b.Bytes(), resp, err } golang-gitlab-gitlab-org-api-client-go-0.123.0/project_snippets_test.go000066400000000000000000000265051475761473200260500ustar00rootroot00000000000000package gitlab import ( "fmt" "net/http" "testing" "time" "github.com/stretchr/testify/require" ) func TestProjectSnippetsService_ListSnippets(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/snippets", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprintf(w, ` [ { "id": 1, "title": "test", "file_name": "add.rb", "description": "Ruby test snippet", "author": { "id": 1, "username": "venkatesh_thalluri", "email": "venky@example.com", "name": "Venkatesh Thalluri", "state": "active" }, "project_id": 1, "web_url": "http://example.com/example/example/snippets/1", "raw_url": "http://example.com/example/example/snippets/1/raw" } ] `) }) want := []*Snippet{{ ID: 1, Title: "test", FileName: "add.rb", Description: "Ruby test snippet", Author: struct { ID int `json:"id"` Username string `json:"username"` Email string `json:"email"` Name string `json:"name"` State string `json:"state"` CreatedAt *time.Time `json:"created_at"` }{ ID: 1, Username: "venkatesh_thalluri", Email: "venky@example.com", Name: "Venkatesh Thalluri", State: "active", }, ProjectID: 1, WebURL: "http://example.com/example/example/snippets/1", RawURL: "http://example.com/example/example/snippets/1/raw", }} ss, resp, err := client.ProjectSnippets.ListSnippets(1, nil, nil) require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, want, ss) ss, resp, err = client.ProjectSnippets.ListSnippets(1.01, nil, nil) require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string") require.Nil(t, resp) require.Nil(t, ss) ss, resp, err = client.ProjectSnippets.ListSnippets(1, nil, nil, errorOption) require.EqualError(t, err, "RequestOptionFunc returns an error") require.Nil(t, resp) require.Nil(t, ss) ss, resp, err = client.ProjectSnippets.ListSnippets(2, nil, nil) require.Error(t, err) require.Nil(t, ss) require.Equal(t, http.StatusNotFound, resp.StatusCode) } func TestProjectSnippetsService_GetSnippet(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/snippets/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprintf(w, ` { "id": 1, "title": "test", "file_name": "add.rb", "description": "Ruby test snippet", "author": { "id": 1, "username": "venkatesh_thalluri", "email": "venky@example.com", "name": "Venkatesh Thalluri", "state": "active" }, "project_id": 1, "web_url": "http://example.com/example/example/snippets/1", "raw_url": "http://example.com/example/example/snippets/1/raw" } `) }) want := &Snippet{ ID: 1, Title: "test", FileName: "add.rb", Description: "Ruby test snippet", Author: struct { ID int `json:"id"` Username string `json:"username"` Email string `json:"email"` Name string `json:"name"` State string `json:"state"` CreatedAt *time.Time `json:"created_at"` }{ ID: 1, Username: "venkatesh_thalluri", Email: "venky@example.com", Name: "Venkatesh Thalluri", State: "active", }, ProjectID: 1, WebURL: "http://example.com/example/example/snippets/1", RawURL: "http://example.com/example/example/snippets/1/raw", } s, resp, err := client.ProjectSnippets.GetSnippet(1, 1, nil, nil) require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, want, s) s, resp, err = client.ProjectSnippets.GetSnippet(1.01, 1, nil, nil) require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string") require.Nil(t, resp) require.Nil(t, s) s, resp, err = client.ProjectSnippets.GetSnippet(1, 1, nil, nil, errorOption) require.EqualError(t, err, "RequestOptionFunc returns an error") require.Nil(t, resp) require.Nil(t, s) s, resp, err = client.ProjectSnippets.GetSnippet(2, 1, nil, nil) require.Error(t, err) require.Nil(t, s) require.Equal(t, http.StatusNotFound, resp.StatusCode) } func TestProjectSnippetsService_CreateSnippet(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/snippets", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) fmt.Fprintf(w, ` { "id": 1, "title": "test", "file_name": "add.rb", "description": "Ruby test snippet", "author": { "id": 1, "username": "venkatesh_thalluri", "email": "venky@example.com", "name": "Venkatesh Thalluri", "state": "active" }, "project_id": 1, "web_url": "http://example.com/example/example/snippets/1", "raw_url": "http://example.com/example/example/snippets/1/raw", "files": [ { "path": "add.rb", "raw_url": "http://example.com/example/example/-/snippets/1/raw/main/add.rb" } ] } `) }) want := &Snippet{ ID: 1, Title: "test", FileName: "add.rb", Description: "Ruby test snippet", Author: struct { ID int `json:"id"` Username string `json:"username"` Email string `json:"email"` Name string `json:"name"` State string `json:"state"` CreatedAt *time.Time `json:"created_at"` }{ ID: 1, Username: "venkatesh_thalluri", Email: "venky@example.com", Name: "Venkatesh Thalluri", State: "active", }, ProjectID: 1, WebURL: "http://example.com/example/example/snippets/1", RawURL: "http://example.com/example/example/snippets/1/raw", Files: []struct { Path string `json:"path"` RawURL string `json:"raw_url"` }{ { Path: "add.rb", RawURL: "http://example.com/example/example/-/snippets/1/raw/main/add.rb", }, }, } s, resp, err := client.ProjectSnippets.CreateSnippet(1, nil, nil, nil) require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, want, s) s, resp, err = client.ProjectSnippets.CreateSnippet(1.01, nil, nil, nil) require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string") require.Nil(t, resp) require.Nil(t, s) s, resp, err = client.ProjectSnippets.CreateSnippet(1, nil, nil, nil, errorOption) require.EqualError(t, err, "RequestOptionFunc returns an error") require.Nil(t, resp) require.Nil(t, s) s, resp, err = client.ProjectSnippets.CreateSnippet(2, nil, nil, nil) require.Error(t, err) require.Nil(t, s) require.Equal(t, http.StatusNotFound, resp.StatusCode) } func TestProjectSnippetsService_UpdateSnippet(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/snippets/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPut) fmt.Fprintf(w, ` { "id": 1, "title": "test", "file_name": "add.rb", "description": "Ruby test snippet", "author": { "id": 1, "username": "venkatesh_thalluri", "email": "venky@example.com", "name": "Venkatesh Thalluri", "state": "active" }, "project_id": 1, "web_url": "http://example.com/example/example/snippets/1", "raw_url": "http://example.com/example/example/snippets/1/raw" } `) }) want := &Snippet{ ID: 1, Title: "test", FileName: "add.rb", Description: "Ruby test snippet", Author: struct { ID int `json:"id"` Username string `json:"username"` Email string `json:"email"` Name string `json:"name"` State string `json:"state"` CreatedAt *time.Time `json:"created_at"` }{ ID: 1, Username: "venkatesh_thalluri", Email: "venky@example.com", Name: "Venkatesh Thalluri", State: "active", }, ProjectID: 1, WebURL: "http://example.com/example/example/snippets/1", RawURL: "http://example.com/example/example/snippets/1/raw", } s, resp, err := client.ProjectSnippets.UpdateSnippet(1, 1, nil, nil, nil) require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, want, s) s, resp, err = client.ProjectSnippets.UpdateSnippet(1.01, 1, nil, nil, nil) require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string") require.Nil(t, resp) require.Nil(t, s) s, resp, err = client.ProjectSnippets.UpdateSnippet(1, 1, nil, nil, nil, errorOption) require.EqualError(t, err, "RequestOptionFunc returns an error") require.Nil(t, resp) require.Nil(t, s) s, resp, err = client.ProjectSnippets.UpdateSnippet(2, 1, nil, nil, nil) require.Error(t, err) require.Nil(t, s) require.Equal(t, http.StatusNotFound, resp.StatusCode) } func TestProjectSnippetsService_DeleteSnippet(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/snippets/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodDelete) fmt.Fprintf(w, ` { "id": 1, "title": "test", "file_name": "add.rb", "description": "Ruby test snippet", "author": { "id": 1, "username": "venkatesh_thalluri", "email": "venky@example.com", "name": "Venkatesh Thalluri", "state": "active" }, "project_id": 1, "web_url": "http://example.com/example/example/snippets/1", "raw_url": "http://example.com/example/example/snippets/1/raw" } `) }) resp, err := client.ProjectSnippets.DeleteSnippet(1, 1, nil, nil, nil) require.NoError(t, err) require.NotNil(t, resp) resp, err = client.ProjectSnippets.DeleteSnippet(1.01, 1, nil, nil, nil) require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string") require.Nil(t, resp) resp, err = client.ProjectSnippets.DeleteSnippet(1, 1, nil, nil, nil, errorOption) require.EqualError(t, err, "RequestOptionFunc returns an error") require.Nil(t, resp) resp, err = client.ProjectSnippets.DeleteSnippet(2, 1, nil, nil, nil) require.Error(t, err) require.Equal(t, http.StatusNotFound, resp.StatusCode) } func TestProjectSnippetsService_SnippetContent(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/snippets/1/raw", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprintf(w, "{"+ "id: 1,"+ "title: test,"+ "file_name: add.rb,"+ "description: Ruby test snippet,"+ "project_id: 1,"+ "web_url: http://example.com/example/example/snippets/1,"+ "raw_url: http://example.com/example/example/snippets/1/raw}") }) want := []byte("{" + "id: 1," + "title: test," + "file_name: add.rb," + "description: Ruby test snippet," + "project_id: 1," + "web_url: http://example.com/example/example/snippets/1," + "raw_url: http://example.com/example/example/snippets/1/raw}") s, resp, err := client.ProjectSnippets.SnippetContent(1, 1, nil, nil, nil) require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, want, s) s, resp, err = client.ProjectSnippets.SnippetContent(1.01, 1, nil, nil, nil) require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string") require.Nil(t, resp) require.Nil(t, s) s, resp, err = client.ProjectSnippets.SnippetContent(1, 1, nil, nil, nil, errorOption) require.EqualError(t, err, "RequestOptionFunc returns an error") require.Nil(t, resp) require.Nil(t, s) s, resp, err = client.ProjectSnippets.SnippetContent(2, 1, nil, nil, nil) require.Error(t, err) require.Nil(t, s) require.Equal(t, http.StatusNotFound, resp.StatusCode) } golang-gitlab-gitlab-org-api-client-go-0.123.0/project_templates.go000066400000000000000000000066171475761473200251440ustar00rootroot00000000000000// // Copyright 2021, Sander van Harmelen // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "fmt" "net/http" ) // ProjectTemplatesService handles communication with the project templates // related methods of the GitLab API. // // GitLab API docs: https://docs.gitlab.com/ee/api/project_templates.html type ProjectTemplatesService struct { client *Client } // ProjectTemplate represents a GitLab ProjectTemplate. // // GitLab API docs: https://docs.gitlab.com/ee/api/project_templates.html type ProjectTemplate struct { Key string `json:"key"` Name string `json:"name"` Nickname string `json:"nickname"` Popular bool `json:"popular"` HTMLURL string `json:"html_url"` SourceURL string `json:"source_url"` Description string `json:"description"` Conditions []string `json:"conditions"` Permissions []string `json:"permissions"` Limitations []string `json:"limitations"` Content string `json:"content"` } func (s ProjectTemplate) String() string { return Stringify(s) } // ListProjectTemplatesOptions represents the available ListSnippets() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/project_templates.html#get-all-templates-of-a-particular-type type ListProjectTemplatesOptions struct { ListOptions ID *int `url:"id,omitempty" json:"id,omitempty"` Type *string `url:"type,omitempty" json:"type,omitempty"` } // ListTemplates gets a list of project templates. // // GitLab API docs: https://docs.gitlab.com/ee/api/project_templates.html#get-all-templates-of-a-particular-type func (s *ProjectTemplatesService) ListTemplates(pid interface{}, templateType string, opt *ListProjectTemplatesOptions, options ...RequestOptionFunc) ([]*ProjectTemplate, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/templates/%s", PathEscape(project), templateType) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } var pt []*ProjectTemplate resp, err := s.client.Do(req, &pt) if err != nil { return nil, resp, err } return pt, resp, nil } // GetProjectTemplate gets a single project template. // // GitLab API docs: // https://docs.gitlab.com/ee/api/project_templates.html#get-one-template-of-a-particular-type func (s *ProjectTemplatesService) GetProjectTemplate(pid interface{}, templateType string, templateName string, options ...RequestOptionFunc) (*ProjectTemplate, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/templates/%s/%s", PathEscape(project), templateType, templateName) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } ptd := new(ProjectTemplate) resp, err := s.client.Do(req, ptd) if err != nil { return nil, resp, err } return ptd, resp, nil } golang-gitlab-gitlab-org-api-client-go-0.123.0/project_templates_test.go000066400000000000000000000026751475761473200262030ustar00rootroot00000000000000package gitlab import ( "fmt" "net/http" "testing" "github.com/stretchr/testify/require" ) func TestProjectTemplatesService_ListTemplates(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/templates/issues", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprintf(w, ` [ { "key": "epl-1.0", "name": "Eclipse Public License 1.0" }, { "key": "lgpl-3.0", "name": "GNU Lesser General Public License v3.0" } ] `) }) want := []*ProjectTemplate{ { Key: "epl-1.0", Name: "Eclipse Public License 1.0", }, { Key: "lgpl-3.0", Name: "GNU Lesser General Public License v3.0", }, } ss, resp, err := client.ProjectTemplates.ListTemplates(1, "issues", nil, nil) require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, want, ss) } func TestProjectTemplatesService_GetProjectTemplate(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/templates/issues/test_issue", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprintf(w, ` { "name": "test_issue", "content": "## Test" } `) }) want := &ProjectTemplate{ Name: "test_issue", Content: "## Test", } ss, resp, err := client.ProjectTemplates.GetProjectTemplate(1, "issues", "test_issue", nil, nil) require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, want, ss) } golang-gitlab-gitlab-org-api-client-go-0.123.0/project_variables.go000066400000000000000000000200701475761473200251030ustar00rootroot00000000000000// // Copyright 2021, Patrick Webster // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "fmt" "net/http" "net/url" ) // ProjectVariablesService handles communication with the // project variables related methods of the GitLab API. // // GitLab API docs: // https://docs.gitlab.com/ee/api/project_level_variables.html type ProjectVariablesService struct { client *Client } // ProjectVariable represents a GitLab Project Variable. // // GitLab API docs: // https://docs.gitlab.com/ee/api/project_level_variables.html type ProjectVariable struct { Key string `json:"key"` Value string `json:"value"` VariableType VariableTypeValue `json:"variable_type"` Protected bool `json:"protected"` Masked bool `json:"masked"` Hidden bool `json:"hidden"` Raw bool `json:"raw"` EnvironmentScope string `json:"environment_scope"` Description string `json:"description"` } func (v ProjectVariable) String() string { return Stringify(v) } // VariableFilter filters available for project variable related functions type VariableFilter struct { EnvironmentScope string `url:"environment_scope, omitempty" json:"environment_scope,omitempty"` } // ListProjectVariablesOptions represents the available options for listing variables // in a project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/project_level_variables.html#list-project-variables type ListProjectVariablesOptions ListOptions // ListVariables gets a list of all variables in a project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/project_level_variables.html#list-project-variables func (s *ProjectVariablesService) ListVariables(pid interface{}, opt *ListProjectVariablesOptions, options ...RequestOptionFunc) ([]*ProjectVariable, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/variables", PathEscape(project)) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } var vs []*ProjectVariable resp, err := s.client.Do(req, &vs) if err != nil { return nil, resp, err } return vs, resp, nil } // GetProjectVariableOptions represents the available GetVariable() // options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/project_level_variables.html#get-a-single-variable type GetProjectVariableOptions struct { Filter *VariableFilter `url:"filter,omitempty" json:"filter,omitempty"` } // GetVariable gets a variable. // // GitLab API docs: // https://docs.gitlab.com/ee/api/project_level_variables.html#get-a-single-variable func (s *ProjectVariablesService) GetVariable(pid interface{}, key string, opt *GetProjectVariableOptions, options ...RequestOptionFunc) (*ProjectVariable, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/variables/%s", PathEscape(project), url.PathEscape(key)) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } v := new(ProjectVariable) resp, err := s.client.Do(req, v) if err != nil { return nil, resp, err } return v, resp, nil } // CreateProjectVariableOptions represents the available CreateVariable() // options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/project_level_variables.html#create-a-variable type CreateProjectVariableOptions struct { Key *string `url:"key,omitempty" json:"key,omitempty"` Value *string `url:"value,omitempty" json:"value,omitempty"` Description *string `url:"description,omitempty" json:"description,omitempty"` EnvironmentScope *string `url:"environment_scope,omitempty" json:"environment_scope,omitempty"` Masked *bool `url:"masked,omitempty" json:"masked,omitempty"` MaskedAndHidden *bool `url:"masked_and_hidden,omitempty" json:"masked_and_hidden,omitempty"` Protected *bool `url:"protected,omitempty" json:"protected,omitempty"` Raw *bool `url:"raw,omitempty" json:"raw,omitempty"` VariableType *VariableTypeValue `url:"variable_type,omitempty" json:"variable_type,omitempty"` } // CreateVariable creates a new project variable. // // GitLab API docs: // https://docs.gitlab.com/ee/api/project_level_variables.html#create-a-variable func (s *ProjectVariablesService) CreateVariable(pid interface{}, opt *CreateProjectVariableOptions, options ...RequestOptionFunc) (*ProjectVariable, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/variables", PathEscape(project)) req, err := s.client.NewRequest(http.MethodPost, u, opt, options) if err != nil { return nil, nil, err } v := new(ProjectVariable) resp, err := s.client.Do(req, v) if err != nil { return nil, resp, err } return v, resp, nil } // UpdateProjectVariableOptions represents the available UpdateVariable() // options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/project_level_variables.html#update-a-variable type UpdateProjectVariableOptions struct { Value *string `url:"value,omitempty" json:"value,omitempty"` Description *string `url:"description,omitempty" json:"description,omitempty"` EnvironmentScope *string `url:"environment_scope,omitempty" json:"environment_scope,omitempty"` Filter *VariableFilter `url:"filter,omitempty" json:"filter,omitempty"` Masked *bool `url:"masked,omitempty" json:"masked,omitempty"` Protected *bool `url:"protected,omitempty" json:"protected,omitempty"` Raw *bool `url:"raw,omitempty" json:"raw,omitempty"` VariableType *VariableTypeValue `url:"variable_type,omitempty" json:"variable_type,omitempty"` } // UpdateVariable updates a project's variable. // // GitLab API docs: // https://docs.gitlab.com/ee/api/project_level_variables.html#update-a-variable func (s *ProjectVariablesService) UpdateVariable(pid interface{}, key string, opt *UpdateProjectVariableOptions, options ...RequestOptionFunc) (*ProjectVariable, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/variables/%s", PathEscape(project), url.PathEscape(key)) req, err := s.client.NewRequest(http.MethodPut, u, opt, options) if err != nil { return nil, nil, err } v := new(ProjectVariable) resp, err := s.client.Do(req, v) if err != nil { return nil, resp, err } return v, resp, nil } // RemoveProjectVariableOptions represents the available RemoveVariable() // options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/project_level_variables.html#delete-a-variable type RemoveProjectVariableOptions struct { Filter *VariableFilter `url:"filter,omitempty" json:"filter,omitempty"` } // RemoveVariable removes a project's variable. // // GitLab API docs: // https://docs.gitlab.com/ee/api/project_level_variables.html#delete-a-variable func (s *ProjectVariablesService) RemoveVariable(pid interface{}, key string, opt *RemoveProjectVariableOptions, options ...RequestOptionFunc) (*Response, error) { project, err := parseID(pid) if err != nil { return nil, err } u := fmt.Sprintf("projects/%s/variables/%s", PathEscape(project), url.PathEscape(key)) req, err := s.client.NewRequest(http.MethodDelete, u, opt, options) if err != nil { return nil, err } return s.client.Do(req, nil) } golang-gitlab-gitlab-org-api-client-go-0.123.0/project_variables_test.go000066400000000000000000000250451475761473200261510ustar00rootroot00000000000000package gitlab import ( "fmt" "net/http" "testing" "github.com/stretchr/testify/require" ) func TestProjectVariablesService_ListVariables(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/variables", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprintf(w, ` [ { "key": "TEST_VARIABLE_1", "variable_type": "env_var", "value": "TEST_1", "description": "test variable 1" } ] `) }) want := []*ProjectVariable{{ Key: "TEST_VARIABLE_1", Value: "TEST_1", VariableType: "env_var", Protected: false, Masked: false, Hidden: false, EnvironmentScope: "", Description: "test variable 1", }} pvs, resp, err := client.ProjectVariables.ListVariables(1, nil, nil) require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, want, pvs) pvs, resp, err = client.ProjectVariables.ListVariables(1.01, nil, nil) require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string") require.Nil(t, resp) require.Nil(t, pvs) pvs, resp, err = client.ProjectVariables.ListVariables(1, nil, nil, errorOption) require.EqualError(t, err, "RequestOptionFunc returns an error") require.Nil(t, resp) require.Nil(t, pvs) pvs, resp, err = client.ProjectVariables.ListVariables(2, nil, nil) require.Error(t, err) require.Nil(t, pvs) require.Equal(t, http.StatusNotFound, resp.StatusCode) } func TestProjectVariablesService_GetVariable(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/variables/TEST_VARIABLE_1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) testParams(t, r, "filter%5Benvironment_scope%5D=prod") fmt.Fprintf(w, ` { "key": "TEST_VARIABLE_1", "variable_type": "env_var", "value": "TEST_1", "protected": false, "masked": true, "hidden": true, "description": "test variable 1" } `) }) want := &ProjectVariable{ Key: "TEST_VARIABLE_1", Value: "TEST_1", VariableType: "env_var", Protected: false, Masked: true, Hidden: true, EnvironmentScope: "", Description: "test variable 1", } pv, resp, err := client.ProjectVariables.GetVariable(1, "TEST_VARIABLE_1", &GetProjectVariableOptions{Filter: &VariableFilter{EnvironmentScope: "prod"}}, nil) require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, want, pv) pv, resp, err = client.ProjectVariables.GetVariable(1.01, "TEST_VARIABLE_1", nil, nil) require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string") require.Nil(t, resp) require.Nil(t, pv) pv, resp, err = client.ProjectVariables.GetVariable(1, "TEST_VARIABLE_1", nil, nil, errorOption) require.EqualError(t, err, "RequestOptionFunc returns an error") require.Nil(t, resp) require.Nil(t, pv) pv, resp, err = client.ProjectVariables.GetVariable(2, "TEST_VARIABLE_1", nil, nil) require.Error(t, err) require.Nil(t, pv) require.Equal(t, http.StatusNotFound, resp.StatusCode) } func TestProjectVariablesService_CreateVariable(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/variables", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) testBody(t, r, `{"description":"new variable"}`) fmt.Fprintf(w, ` { "key": "NEW_VARIABLE", "value": "new value", "protected": false, "variable_type": "env_var", "masked": false, "masked_and_hidden": false, "environment_scope": "*", "description": "new variable" } `) }) want := &ProjectVariable{ Key: "NEW_VARIABLE", Value: "new value", VariableType: "env_var", Protected: false, Masked: false, Hidden: false, EnvironmentScope: "*", Description: "new variable", } pv, resp, err := client.ProjectVariables.CreateVariable(1, &CreateProjectVariableOptions{Description: Ptr("new variable")}, nil) require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, want, pv) pv, resp, err = client.ProjectVariables.CreateVariable(1.01, nil, nil) require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string") require.Nil(t, resp) require.Nil(t, pv) pv, resp, err = client.ProjectVariables.CreateVariable(1, nil, nil, errorOption) require.EqualError(t, err, "RequestOptionFunc returns an error") require.Nil(t, resp) require.Nil(t, pv) pv, resp, err = client.ProjectVariables.CreateVariable(2, nil, nil) require.Error(t, err) require.Nil(t, pv) require.Equal(t, http.StatusNotFound, resp.StatusCode) } func TestProjectVariablesService_CreateVariable_MaskedAndHidden(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/variables", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) testBody(t, r, `{"description":"new variable"}`) fmt.Fprintf(w, ` { "key": "NEW_VARIABLE", "protected": false, "variable_type": "env_var", "masked": true, "hidden": true, "environment_scope": "*", "description": "new variable" } `) }) want := &ProjectVariable{ Key: "NEW_VARIABLE", VariableType: "env_var", Protected: false, Masked: true, Hidden: true, EnvironmentScope: "*", Description: "new variable", } pv, resp, err := client.ProjectVariables.CreateVariable(1, &CreateProjectVariableOptions{Description: Ptr("new variable")}, nil) require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, want, pv) pv, resp, err = client.ProjectVariables.CreateVariable(1.01, nil, nil) require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string") require.Nil(t, resp) require.Nil(t, pv) pv, resp, err = client.ProjectVariables.CreateVariable(1, nil, nil, errorOption) require.EqualError(t, err, "RequestOptionFunc returns an error") require.Nil(t, resp) require.Nil(t, pv) pv, resp, err = client.ProjectVariables.CreateVariable(2, nil, nil) require.Error(t, err) require.Nil(t, pv) require.Equal(t, http.StatusNotFound, resp.StatusCode) } func TestProjectVariablesService_UpdateVariable(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/variables/NEW_VARIABLE", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPut) testBody(t, r, `{"description":"updated description","filter":{"environment_scope":"prod"}}`) fmt.Fprintf(w, ` { "key": "NEW_VARIABLE", "value": "updated value", "protected": false, "variable_type": "env_var", "masked": false, "environment_scope": "*", "description": "updated description" } `) }) want := &ProjectVariable{ Key: "NEW_VARIABLE", Value: "updated value", VariableType: "env_var", Protected: false, Masked: false, Hidden: false, EnvironmentScope: "*", Description: "updated description", } pv, resp, err := client.ProjectVariables.UpdateVariable(1, "NEW_VARIABLE", &UpdateProjectVariableOptions{ Filter: &VariableFilter{EnvironmentScope: "prod"}, Description: Ptr("updated description"), }, nil) require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, want, pv) pv, resp, err = client.ProjectVariables.UpdateVariable(1.01, "NEW_VARIABLE", nil, nil) require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string") require.Nil(t, resp) require.Nil(t, pv) pv, resp, err = client.ProjectVariables.UpdateVariable(1, "NEW_VARIABLE", nil, nil, errorOption) require.EqualError(t, err, "RequestOptionFunc returns an error") require.Nil(t, resp) require.Nil(t, pv) pv, resp, err = client.ProjectVariables.UpdateVariable(2, "NEW_VARIABLE", nil, nil) require.Error(t, err) require.Nil(t, pv) require.Equal(t, http.StatusNotFound, resp.StatusCode) } func TestProjectVariablesService_UpdateVariable_MaskedAndHidden(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/variables/NEW_VARIABLE", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPut) testBody(t, r, `{"description":"updated description","filter":{"environment_scope":"prod"}}`) fmt.Fprintf(w, ` { "key": "NEW_VARIABLE", "value": null, "protected": false, "variable_type": "env_var", "masked": true, "hidden": true, "environment_scope": "*", "description": "updated description" } `) }) want := &ProjectVariable{ Key: "NEW_VARIABLE", VariableType: "env_var", Protected: false, Masked: true, Hidden: true, EnvironmentScope: "*", Description: "updated description", } pv, resp, err := client.ProjectVariables.UpdateVariable(1, "NEW_VARIABLE", &UpdateProjectVariableOptions{ Filter: &VariableFilter{EnvironmentScope: "prod"}, Description: Ptr("updated description"), }, nil) require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, want, pv) pv, resp, err = client.ProjectVariables.UpdateVariable(1.01, "NEW_VARIABLE", nil, nil) require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string") require.Nil(t, resp) require.Nil(t, pv) pv, resp, err = client.ProjectVariables.UpdateVariable(1, "NEW_VARIABLE", nil, nil, errorOption) require.EqualError(t, err, "RequestOptionFunc returns an error") require.Nil(t, resp) require.Nil(t, pv) pv, resp, err = client.ProjectVariables.UpdateVariable(2, "NEW_VARIABLE", nil, nil) require.Error(t, err) require.Nil(t, pv) require.Equal(t, http.StatusNotFound, resp.StatusCode) } func TestProjectVariablesService_RemoveVariable(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/variables/VARIABLE_1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodDelete) testParams(t, r, "filter%5Benvironment_scope%5D=prod") }) resp, err := client.ProjectVariables.RemoveVariable(1, "VARIABLE_1", &RemoveProjectVariableOptions{Filter: &VariableFilter{EnvironmentScope: "prod"}}, nil) require.NoError(t, err) require.NotNil(t, resp) resp, err = client.ProjectVariables.RemoveVariable(1.01, "VARIABLE_1", nil, nil) require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string") require.Nil(t, resp) resp, err = client.ProjectVariables.RemoveVariable(1, "VARIABLE_1", nil, nil, errorOption) require.EqualError(t, err, "RequestOptionFunc returns an error") require.Nil(t, resp) resp, err = client.ProjectVariables.RemoveVariable(2, "VARIABLE_1", nil, nil) require.Error(t, err) require.Equal(t, http.StatusNotFound, resp.StatusCode) } golang-gitlab-gitlab-org-api-client-go-0.123.0/project_vulnerabilities.go000066400000000000000000000127021475761473200263370ustar00rootroot00000000000000// // Copyright 2021, Sander van Harmelen // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "fmt" "net/http" "time" ) // ProjectVulnerabilitiesService handles communication with the projects // vulnerabilities related methods of the GitLab API. // // GitLab API docs: https://docs.gitlab.com/ee/api/project_vulnerabilities.html type ProjectVulnerabilitiesService struct { client *Client } // Project represents a GitLab project vulnerability. // // GitLab API docs: https://docs.gitlab.com/ee/api/project_vulnerabilities.html type ProjectVulnerability struct { AuthorID int `json:"author_id"` Confidence string `json:"confidence"` CreatedAt *time.Time `json:"created_at"` Description string `json:"description"` DismissedAt *time.Time `json:"dismissed_at"` DismissedByID int `json:"dismissed_by_id"` DueDate *time.Time `json:"due_date"` Finding *Finding `json:"finding"` ID int `json:"id"` LastEditedAt *time.Time `json:"last_edited_at"` LastEditedByID int `json:"last_edited_by_id"` Project *Project `json:"project"` ProjectDefaultBranch string `json:"project_default_branch"` ReportType string `json:"report_type"` ResolvedAt *time.Time `json:"resolved_at"` ResolvedByID int `json:"resolved_by_id"` ResolvedOnDefaultBranch bool `json:"resolved_on_default_branch"` Severity string `json:"severity"` StartDate *time.Time `json:"start_date"` State string `json:"state"` Title string `json:"title"` UpdatedAt *time.Time `json:"updated_at"` UpdatedByID int `json:"updated_by_id"` } // Project represents a GitLab project vulnerability finding. // // GitLab API docs: https://docs.gitlab.com/ee/api/project_vulnerabilities.html type Finding struct { Confidence string `json:"confidence"` CreatedAt *time.Time `json:"created_at"` ID int `json:"id"` LocationFingerprint string `json:"location_fingerprint"` MetadataVersion string `json:"metadata_version"` Name string `json:"name"` PrimaryIdentifierID int `json:"primary_identifier_id"` ProjectFingerprint string `json:"project_fingerprint"` ProjectID int `json:"project_id"` RawMetadata string `json:"raw_metadata"` ReportType string `json:"report_type"` ScannerID int `json:"scanner_id"` Severity string `json:"severity"` UpdatedAt *time.Time `json:"updated_at"` UUID string `json:"uuid"` VulnerabilityID int `json:"vulnerability_id"` } // ListProjectVulnerabilitiesOptions represents the available // ListProjectVulnerabilities() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/project_vulnerabilities.html#list-project-vulnerabilities type ListProjectVulnerabilitiesOptions struct { ListOptions } // ListProjectVulnerabilities gets a list of all project vulnerabilities. // // GitLab API docs: // https://docs.gitlab.com/ee/api/project_vulnerabilities.html#list-project-vulnerabilities func (s *ProjectVulnerabilitiesService) ListProjectVulnerabilities(pid interface{}, opt *ListProjectVulnerabilitiesOptions, options ...RequestOptionFunc) ([]*ProjectVulnerability, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/vulnerabilities", PathEscape(project)) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } var p []*ProjectVulnerability resp, err := s.client.Do(req, &p) if err != nil { return nil, resp, err } return p, resp, nil } // CreateVulnerabilityOptions represents the available CreateVulnerability() // options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/project_vulnerabilities.html#new-vulnerability type CreateVulnerabilityOptions struct { FindingID *int `url:"finding_id,omitempty" json:"finding_id,omitempty"` } // CreateVulnerability creates a new vulnerability on the selected project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/project_vulnerabilities.html#new-vulnerability func (s *ProjectVulnerabilitiesService) CreateVulnerability(pid interface{}, opt *CreateVulnerabilityOptions, options ...RequestOptionFunc) (*ProjectVulnerability, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/vulnerabilities", PathEscape(project)) req, err := s.client.NewRequest(http.MethodPost, u, opt, options) if err != nil { return nil, nil, err } p := new(ProjectVulnerability) resp, err := s.client.Do(req, p) if err != nil { return nil, resp, err } return p, resp, nil } golang-gitlab-gitlab-org-api-client-go-0.123.0/project_vulnerabilities_test.go000066400000000000000000000041661475761473200274030ustar00rootroot00000000000000// // Copyright 2021, Sander van Harmelen // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "fmt" "net/http" "reflect" "testing" ) func TestListProjectVulnerabilities(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/vulnerabilities", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, `[{"id":1},{"id":2}]`) }) opt := &ListProjectVulnerabilitiesOptions{ ListOptions: ListOptions{Page: 2, PerPage: 3}, } projectVulnerabilities, _, err := client.ProjectVulnerabilities.ListProjectVulnerabilities(1, opt) if err != nil { t.Errorf("ProjectVulnerabilities.ListProjectVulnerabilities returned error: %v", err) } want := []*ProjectVulnerability{{ID: 1}, {ID: 2}} if !reflect.DeepEqual(want, projectVulnerabilities) { t.Errorf("ProjectVulnerabilities.ListProjectVulnerabilities returned %+v, want %+v", projectVulnerabilities, want) } } func TestCreateVulnerability(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/vulnerabilities", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) fmt.Fprint(w, `{"id":1}`) }) opt := &CreateVulnerabilityOptions{ FindingID: Ptr(1), } projectVulnerability, _, err := client.ProjectVulnerabilities.CreateVulnerability(1, opt) if err != nil { t.Errorf("ProjectVulnerabilities.CreateVulnerability returned error: %v", err) } want := &ProjectVulnerability{ID: 1} if !reflect.DeepEqual(want, projectVulnerability) { t.Errorf("ProjectVulnerabilities.CreateVulnerability returned %+v, want %+v", projectVulnerability, want) } } golang-gitlab-gitlab-org-api-client-go-0.123.0/projects.go000066400000000000000000003422041475761473200232440ustar00rootroot00000000000000// // Copyright 2021, Sander van Harmelen // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "bytes" "encoding/json" "fmt" "io" "net/http" "time" "github.com/hashicorp/go-retryablehttp" ) // ProjectsService handles communication with the repositories related methods // of the GitLab API. // // GitLab API docs: https://docs.gitlab.com/ee/api/projects.html type ProjectsService struct { client *Client } // Project represents a GitLab project. // // GitLab API docs: https://docs.gitlab.com/ee/api/projects.html type Project struct { ID int `json:"id"` Description string `json:"description"` DefaultBranch string `json:"default_branch"` Visibility VisibilityValue `json:"visibility"` SSHURLToRepo string `json:"ssh_url_to_repo"` HTTPURLToRepo string `json:"http_url_to_repo"` WebURL string `json:"web_url"` ReadmeURL string `json:"readme_url"` TagList []string `json:"tag_list"` Topics []string `json:"topics"` Owner *User `json:"owner"` Name string `json:"name"` NameWithNamespace string `json:"name_with_namespace"` Path string `json:"path"` PathWithNamespace string `json:"path_with_namespace"` IssuesEnabled bool `json:"issues_enabled"` OpenIssuesCount int `json:"open_issues_count"` MergeRequestsEnabled bool `json:"merge_requests_enabled"` ApprovalsBeforeMerge int `json:"approvals_before_merge"` JobsEnabled bool `json:"jobs_enabled"` WikiEnabled bool `json:"wiki_enabled"` SnippetsEnabled bool `json:"snippets_enabled"` ResolveOutdatedDiffDiscussions bool `json:"resolve_outdated_diff_discussions"` ContainerExpirationPolicy *ContainerExpirationPolicy `json:"container_expiration_policy,omitempty"` ContainerRegistryEnabled bool `json:"container_registry_enabled"` ContainerRegistryAccessLevel AccessControlValue `json:"container_registry_access_level"` ContainerRegistryImagePrefix string `json:"container_registry_image_prefix,omitempty"` CreatedAt *time.Time `json:"created_at,omitempty"` UpdatedAt *time.Time `json:"updated_at,omitempty"` LastActivityAt *time.Time `json:"last_activity_at,omitempty"` CreatorID int `json:"creator_id"` Namespace *ProjectNamespace `json:"namespace"` Permissions *Permissions `json:"permissions"` MarkedForDeletionAt *ISOTime `json:"marked_for_deletion_at"` EmptyRepo bool `json:"empty_repo"` Archived bool `json:"archived"` AvatarURL string `json:"avatar_url"` LicenseURL string `json:"license_url"` License *ProjectLicense `json:"license"` SharedRunnersEnabled bool `json:"shared_runners_enabled"` GroupRunnersEnabled bool `json:"group_runners_enabled"` RunnerTokenExpirationInterval int `json:"runner_token_expiration_interval"` ForksCount int `json:"forks_count"` StarCount int `json:"star_count"` RunnersToken string `json:"runners_token"` AllowMergeOnSkippedPipeline bool `json:"allow_merge_on_skipped_pipeline"` AllowPipelineTriggerApproveDeployment bool `json:"allow_pipeline_trigger_approve_deployment"` OnlyAllowMergeIfPipelineSucceeds bool `json:"only_allow_merge_if_pipeline_succeeds"` OnlyAllowMergeIfAllDiscussionsAreResolved bool `json:"only_allow_merge_if_all_discussions_are_resolved"` RemoveSourceBranchAfterMerge bool `json:"remove_source_branch_after_merge"` PreventMergeWithoutJiraIssue bool `json:"prevent_merge_without_jira_issue"` PrintingMergeRequestLinkEnabled bool `json:"printing_merge_request_link_enabled"` LFSEnabled bool `json:"lfs_enabled"` RepositoryStorage string `json:"repository_storage"` RequestAccessEnabled bool `json:"request_access_enabled"` MergeMethod MergeMethodValue `json:"merge_method"` CanCreateMergeRequestIn bool `json:"can_create_merge_request_in"` ForkedFromProject *ForkParent `json:"forked_from_project"` Mirror bool `json:"mirror"` MirrorUserID int `json:"mirror_user_id"` MirrorTriggerBuilds bool `json:"mirror_trigger_builds"` OnlyMirrorProtectedBranches bool `json:"only_mirror_protected_branches"` MirrorOverwritesDivergedBranches bool `json:"mirror_overwrites_diverged_branches"` PackagesEnabled bool `json:"packages_enabled"` ServiceDeskEnabled bool `json:"service_desk_enabled"` ServiceDeskAddress string `json:"service_desk_address"` IssuesAccessLevel AccessControlValue `json:"issues_access_level"` ReleasesAccessLevel AccessControlValue `json:"releases_access_level,omitempty"` RepositoryAccessLevel AccessControlValue `json:"repository_access_level"` MergeRequestsAccessLevel AccessControlValue `json:"merge_requests_access_level"` ForkingAccessLevel AccessControlValue `json:"forking_access_level"` WikiAccessLevel AccessControlValue `json:"wiki_access_level"` BuildsAccessLevel AccessControlValue `json:"builds_access_level"` SnippetsAccessLevel AccessControlValue `json:"snippets_access_level"` PagesAccessLevel AccessControlValue `json:"pages_access_level"` OperationsAccessLevel AccessControlValue `json:"operations_access_level"` AnalyticsAccessLevel AccessControlValue `json:"analytics_access_level"` EnvironmentsAccessLevel AccessControlValue `json:"environments_access_level"` FeatureFlagsAccessLevel AccessControlValue `json:"feature_flags_access_level"` InfrastructureAccessLevel AccessControlValue `json:"infrastructure_access_level"` MonitorAccessLevel AccessControlValue `json:"monitor_access_level"` AutocloseReferencedIssues bool `json:"autoclose_referenced_issues"` SuggestionCommitMessage string `json:"suggestion_commit_message"` SquashOption SquashOptionValue `json:"squash_option"` EnforceAuthChecksOnUploads bool `json:"enforce_auth_checks_on_uploads,omitempty"` SharedWithGroups []struct { GroupID int `json:"group_id"` GroupName string `json:"group_name"` GroupFullPath string `json:"group_full_path"` GroupAccessLevel int `json:"group_access_level"` } `json:"shared_with_groups"` Statistics *Statistics `json:"statistics"` Links *Links `json:"_links,omitempty"` ImportURL string `json:"import_url"` ImportType string `json:"import_type"` ImportStatus string `json:"import_status"` ImportError string `json:"import_error"` CIDefaultGitDepth int `json:"ci_default_git_depth"` CIForwardDeploymentEnabled bool `json:"ci_forward_deployment_enabled"` CIForwardDeploymentRollbackAllowed bool `json:"ci_forward_deployment_rollback_allowed"` CISeperateCache bool `json:"ci_separated_caches"` CIJobTokenScopeEnabled bool `json:"ci_job_token_scope_enabled"` CIOptInJWT bool `json:"ci_opt_in_jwt"` CIAllowForkPipelinesToRunInParentProject bool `json:"ci_allow_fork_pipelines_to_run_in_parent_project"` CIRestrictPipelineCancellationRole AccessControlValue `json:"ci_restrict_pipeline_cancellation_role"` PublicJobs bool `json:"public_jobs"` BuildTimeout int `json:"build_timeout"` AutoCancelPendingPipelines string `json:"auto_cancel_pending_pipelines"` CIConfigPath string `json:"ci_config_path"` CustomAttributes []*CustomAttribute `json:"custom_attributes"` ComplianceFrameworks []string `json:"compliance_frameworks"` BuildCoverageRegex string `json:"build_coverage_regex"` IssuesTemplate string `json:"issues_template"` MergeRequestsTemplate string `json:"merge_requests_template"` IssueBranchTemplate string `json:"issue_branch_template"` KeepLatestArtifact bool `json:"keep_latest_artifact"` MergePipelinesEnabled bool `json:"merge_pipelines_enabled"` MergeTrainsEnabled bool `json:"merge_trains_enabled"` RestrictUserDefinedVariables bool `json:"restrict_user_defined_variables"` CIPipelineVariablesMinimumOverrideRole CIPipelineVariablesMinimumOverrideRoleValue `json:"ci_pipeline_variables_minimum_override_role"` MergeCommitTemplate string `json:"merge_commit_template"` SquashCommitTemplate string `json:"squash_commit_template"` AutoDevopsDeployStrategy string `json:"auto_devops_deploy_strategy"` AutoDevopsEnabled bool `json:"auto_devops_enabled"` BuildGitStrategy string `json:"build_git_strategy"` EmailsEnabled bool `json:"emails_enabled"` ExternalAuthorizationClassificationLabel string `json:"external_authorization_classification_label"` RequirementsEnabled bool `json:"requirements_enabled"` RequirementsAccessLevel AccessControlValue `json:"requirements_access_level"` SecurityAndComplianceEnabled bool `json:"security_and_compliance_enabled"` SecurityAndComplianceAccessLevel AccessControlValue `json:"security_and_compliance_access_level"` MergeRequestDefaultTargetSelf bool `json:"mr_default_target_self"` ModelExperimentsAccessLevel AccessControlValue `json:"model_experiments_access_level"` ModelRegistryAccessLevel AccessControlValue `json:"model_registry_access_level"` PreReceiveSecretDetectionEnabled bool `json:"pre_receive_secret_detection_enabled"` // Deprecated: Use EmailsEnabled instead EmailsDisabled bool `json:"emails_disabled"` // Deprecated: This parameter has been renamed to PublicJobs in GitLab 9.0. PublicBuilds bool `json:"public_builds"` } // BasicProject included in other service responses (such as todos). type BasicProject struct { ID int `json:"id"` Description string `json:"description"` Name string `json:"name"` NameWithNamespace string `json:"name_with_namespace"` Path string `json:"path"` PathWithNamespace string `json:"path_with_namespace"` CreatedAt *time.Time `json:"created_at"` } // ContainerExpirationPolicy represents the container expiration policy. type ContainerExpirationPolicy struct { Cadence string `json:"cadence"` KeepN int `json:"keep_n"` OlderThan string `json:"older_than"` NameRegex string `json:"name_regex"` NameRegexDelete string `json:"name_regex_delete"` NameRegexKeep string `json:"name_regex_keep"` Enabled bool `json:"enabled"` NextRunAt *time.Time `json:"next_run_at"` } // ForkParent represents the parent project when this is a fork. type ForkParent struct { ID int `json:"id"` Name string `json:"name"` NameWithNamespace string `json:"name_with_namespace"` Path string `json:"path"` PathWithNamespace string `json:"path_with_namespace"` HTTPURLToRepo string `json:"http_url_to_repo"` WebURL string `json:"web_url"` RepositoryStorage string `json:"repository_storage"` } // GroupAccess represents group access. type GroupAccess struct { AccessLevel AccessLevelValue `json:"access_level"` NotificationLevel NotificationLevelValue `json:"notification_level"` } // Links represents a project web links for self, issues, merge_requests, // repo_branches, labels, events, members. type Links struct { Self string `json:"self"` Issues string `json:"issues"` MergeRequests string `json:"merge_requests"` RepoBranches string `json:"repo_branches"` Labels string `json:"labels"` Events string `json:"events"` Members string `json:"members"` ClusterAgents string `json:"cluster_agents"` } // Permissions represents permissions. type Permissions struct { ProjectAccess *ProjectAccess `json:"project_access"` GroupAccess *GroupAccess `json:"group_access"` } // ProjectAccess represents project access. type ProjectAccess struct { AccessLevel AccessLevelValue `json:"access_level"` NotificationLevel NotificationLevelValue `json:"notification_level"` } // ProjectLicense represent the license for a project. type ProjectLicense struct { Key string `json:"key"` Name string `json:"name"` Nickname string `json:"nickname"` HTMLURL string `json:"html_url"` SourceURL string `json:"source_url"` } // ProjectNamespace represents a project namespace. type ProjectNamespace struct { ID int `json:"id"` Name string `json:"name"` Path string `json:"path"` Kind string `json:"kind"` FullPath string `json:"full_path"` ParentID int `json:"parent_id"` AvatarURL string `json:"avatar_url"` WebURL string `json:"web_url"` } // Repository represents a repository. type Repository struct { Name string `json:"name"` Description string `json:"description"` WebURL string `json:"web_url"` AvatarURL string `json:"avatar_url"` GitSSHURL string `json:"git_ssh_url"` GitHTTPURL string `json:"git_http_url"` Namespace string `json:"namespace"` Visibility VisibilityValue `json:"visibility"` PathWithNamespace string `json:"path_with_namespace"` DefaultBranch string `json:"default_branch"` Homepage string `json:"homepage"` URL string `json:"url"` SSHURL string `json:"ssh_url"` HTTPURL string `json:"http_url"` } // Statistics represents a statistics record for a group or project. type Statistics struct { CommitCount int64 `json:"commit_count"` StorageSize int64 `json:"storage_size"` RepositorySize int64 `json:"repository_size"` WikiSize int64 `json:"wiki_size"` LFSObjectsSize int64 `json:"lfs_objects_size"` JobArtifactsSize int64 `json:"job_artifacts_size"` PipelineArtifactsSize int64 `json:"pipeline_artifacts_size"` PackagesSize int64 `json:"packages_size"` SnippetsSize int64 `json:"snippets_size"` UploadsSize int64 `json:"uploads_size"` ContainerRegistrySize int64 `json:"container_registry_size"` } func (s Project) String() string { return Stringify(s) } // ProjectApprovalRule represents a GitLab project approval rule. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_request_approvals.html#get-project-level-rules type ProjectApprovalRule struct { ID int `json:"id"` Name string `json:"name"` RuleType string `json:"rule_type"` ReportType string `json:"report_type"` EligibleApprovers []*BasicUser `json:"eligible_approvers"` ApprovalsRequired int `json:"approvals_required"` Users []*BasicUser `json:"users"` Groups []*Group `json:"groups"` ContainsHiddenGroups bool `json:"contains_hidden_groups"` ProtectedBranches []*ProtectedBranch `json:"protected_branches"` AppliesToAllProtectedBranches bool `json:"applies_to_all_protected_branches"` } func (s ProjectApprovalRule) String() string { return Stringify(s) } // ListProjectsOptions represents the available ListProjects() options. // // GitLab API docs: https://docs.gitlab.com/ee/api/projects.html#list-all-projects type ListProjectsOptions struct { ListOptions Archived *bool `url:"archived,omitempty" json:"archived,omitempty"` IDAfter *int `url:"id_after,omitempty" json:"id_after,omitempty"` IDBefore *int `url:"id_before,omitempty" json:"id_before,omitempty"` Imported *bool `url:"imported,omitempty" json:"imported,omitempty"` IncludeHidden *bool `url:"include_hidden,omitempty" json:"include_hidden,omitempty"` IncludePendingDelete *bool `url:"include_pending_delete,omitempty" json:"include_pending_delete,omitempty"` LastActivityAfter *time.Time `url:"last_activity_after,omitempty" json:"last_activity_after,omitempty"` LastActivityBefore *time.Time `url:"last_activity_before,omitempty" json:"last_activity_before,omitempty"` Membership *bool `url:"membership,omitempty" json:"membership,omitempty"` MinAccessLevel *AccessLevelValue `url:"min_access_level,omitempty" json:"min_access_level,omitempty"` OrderBy *string `url:"order_by,omitempty" json:"order_by,omitempty"` Owned *bool `url:"owned,omitempty" json:"owned,omitempty"` RepositoryChecksumFailed *bool `url:"repository_checksum_failed,omitempty" json:"repository_checksum_failed,omitempty"` RepositoryStorage *string `url:"repository_storage,omitempty" json:"repository_storage,omitempty"` Search *string `url:"search,omitempty" json:"search,omitempty"` SearchNamespaces *bool `url:"search_namespaces,omitempty" json:"search_namespaces,omitempty"` Simple *bool `url:"simple,omitempty" json:"simple,omitempty"` Sort *string `url:"sort,omitempty" json:"sort,omitempty"` Starred *bool `url:"starred,omitempty" json:"starred,omitempty"` Statistics *bool `url:"statistics,omitempty" json:"statistics,omitempty"` Topic *string `url:"topic,omitempty" json:"topic,omitempty"` Visibility *VisibilityValue `url:"visibility,omitempty" json:"visibility,omitempty"` WikiChecksumFailed *bool `url:"wiki_checksum_failed,omitempty" json:"wiki_checksum_failed,omitempty"` WithCustomAttributes *bool `url:"with_custom_attributes,omitempty" json:"with_custom_attributes,omitempty"` WithIssuesEnabled *bool `url:"with_issues_enabled,omitempty" json:"with_issues_enabled,omitempty"` WithMergeRequestsEnabled *bool `url:"with_merge_requests_enabled,omitempty" json:"with_merge_requests_enabled,omitempty"` WithProgrammingLanguage *string `url:"with_programming_language,omitempty" json:"with_programming_language,omitempty"` } // ListProjects gets a list of projects accessible by the authenticated user. // // GitLab API docs: https://docs.gitlab.com/ee/api/projects.html#list-all-projects func (s *ProjectsService) ListProjects(opt *ListProjectsOptions, options ...RequestOptionFunc) ([]*Project, *Response, error) { req, err := s.client.NewRequest(http.MethodGet, "projects", opt, options) if err != nil { return nil, nil, err } var p []*Project resp, err := s.client.Do(req, &p) if err != nil { return nil, resp, err } return p, resp, nil } // ListUserProjects gets a list of projects for the given user. // // GitLab API docs: // https://docs.gitlab.com/ee/api/projects.html#list-user-projects func (s *ProjectsService) ListUserProjects(uid interface{}, opt *ListProjectsOptions, options ...RequestOptionFunc) ([]*Project, *Response, error) { user, err := parseID(uid) if err != nil { return nil, nil, err } u := fmt.Sprintf("users/%s/projects", user) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } var p []*Project resp, err := s.client.Do(req, &p) if err != nil { return nil, resp, err } return p, resp, nil } // ListUserContributedProjects gets a list of visible projects a given user has contributed to. // // GitLab API docs: // https://docs.gitlab.com/ee/api/projects.html#list-projects-a-user-has-contributed-to func (s *ProjectsService) ListUserContributedProjects(uid interface{}, opt *ListProjectsOptions, options ...RequestOptionFunc) ([]*Project, *Response, error) { user, err := parseID(uid) if err != nil { return nil, nil, err } u := fmt.Sprintf("users/%s/contributed_projects", user) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } var p []*Project resp, err := s.client.Do(req, &p) if err != nil { return nil, resp, err } return p, resp, nil } // ListUserStarredProjects gets a list of projects starred by the given user. // // GitLab API docs: // https://docs.gitlab.com/ee/api/projects.html#list-projects-starred-by-a-user func (s *ProjectsService) ListUserStarredProjects(uid interface{}, opt *ListProjectsOptions, options ...RequestOptionFunc) ([]*Project, *Response, error) { user, err := parseID(uid) if err != nil { return nil, nil, err } u := fmt.Sprintf("users/%s/starred_projects", user) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } var p []*Project resp, err := s.client.Do(req, &p) if err != nil { return nil, resp, err } return p, resp, nil } // ProjectUser represents a GitLab project user. type ProjectUser struct { ID int `json:"id"` Name string `json:"name"` Username string `json:"username"` State string `json:"state"` AvatarURL string `json:"avatar_url"` WebURL string `json:"web_url"` } // ListProjectUserOptions represents the available ListProjectsUsers() options. // // GitLab API docs: https://docs.gitlab.com/ee/api/projects.html#get-project-users type ListProjectUserOptions struct { ListOptions Search *string `url:"search,omitempty" json:"search,omitempty"` } // ListProjectsUsers gets a list of users for the given project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/projects.html#get-project-users func (s *ProjectsService) ListProjectsUsers(pid interface{}, opt *ListProjectUserOptions, options ...RequestOptionFunc) ([]*ProjectUser, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/users", PathEscape(project)) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } var p []*ProjectUser resp, err := s.client.Do(req, &p) if err != nil { return nil, resp, err } return p, resp, nil } // ProjectGroup represents a GitLab project group. type ProjectGroup struct { ID int `json:"id"` Name string `json:"name"` AvatarURL string `json:"avatar_url"` WebURL string `json:"web_url"` FullName string `json:"full_name"` FullPath string `json:"full_path"` } // ListProjectGroupOptions represents the available ListProjectsGroups() options. // // GitLab API docs: https://docs.gitlab.com/ee/api/projects.html#list-a-projects-groups type ListProjectGroupOptions struct { ListOptions Search *string `url:"search,omitempty" json:"search,omitempty"` SharedMinAccessLevel *AccessLevelValue `url:"shared_min_access_level,omitempty" json:"shared_min_access_level,omitempty"` SharedVisiableOnly *bool `url:"shared_visible_only,omitempty" json:"shared_visible_only,omitempty"` SkipGroups *[]int `url:"skip_groups,omitempty" json:"skip_groups,omitempty"` WithShared *bool `url:"with_shared,omitempty" json:"with_shared,omitempty"` } // ListProjectsGroups gets a list of groups for the given project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/projects.html#list-a-projects-groups func (s *ProjectsService) ListProjectsGroups(pid interface{}, opt *ListProjectGroupOptions, options ...RequestOptionFunc) ([]*ProjectGroup, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/groups", PathEscape(project)) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } var p []*ProjectGroup resp, err := s.client.Do(req, &p) if err != nil { return nil, resp, err } return p, resp, nil } // ProjectLanguages is a map of strings because the response is arbitrary // // Gitlab API docs: https://docs.gitlab.com/ee/api/projects.html#languages type ProjectLanguages map[string]float32 // GetProjectLanguages gets a list of languages used by the project // // GitLab API docs: https://docs.gitlab.com/ee/api/projects.html#languages func (s *ProjectsService) GetProjectLanguages(pid interface{}, options ...RequestOptionFunc) (*ProjectLanguages, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/languages", PathEscape(project)) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } p := new(ProjectLanguages) resp, err := s.client.Do(req, p) if err != nil { return nil, resp, err } return p, resp, nil } // GetProjectOptions represents the available GetProject() options. // // GitLab API docs: https://docs.gitlab.com/ee/api/projects.html#get-single-project type GetProjectOptions struct { License *bool `url:"license,omitempty" json:"license,omitempty"` Statistics *bool `url:"statistics,omitempty" json:"statistics,omitempty"` WithCustomAttributes *bool `url:"with_custom_attributes,omitempty" json:"with_custom_attributes,omitempty"` } // GetProject gets a specific project, identified by project ID or // NAMESPACE/PROJECT_NAME, which is owned by the authenticated user. // // GitLab API docs: // https://docs.gitlab.com/ee/api/projects.html#get-single-project func (s *ProjectsService) GetProject(pid interface{}, opt *GetProjectOptions, options ...RequestOptionFunc) (*Project, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s", PathEscape(project)) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } p := new(Project) resp, err := s.client.Do(req, p) if err != nil { return nil, resp, err } return p, resp, nil } // CreateProjectOptions represents the available CreateProject() options. // // GitLab API docs: https://docs.gitlab.com/ee/api/projects.html#create-project type CreateProjectOptions struct { AllowMergeOnSkippedPipeline *bool `url:"allow_merge_on_skipped_pipeline,omitempty" json:"allow_merge_on_skipped_pipeline,omitempty"` OnlyAllowMergeIfAllStatusChecksPassed *bool `url:"only_allow_merge_if_all_status_checks_passed,omitempty" json:"only_allow_merge_if_all_status_checks_passed,omitempty"` AnalyticsAccessLevel *AccessControlValue `url:"analytics_access_level,omitempty" json:"analytics_access_level,omitempty"` ApprovalsBeforeMerge *int `url:"approvals_before_merge,omitempty" json:"approvals_before_merge,omitempty"` AutoCancelPendingPipelines *string `url:"auto_cancel_pending_pipelines,omitempty" json:"auto_cancel_pending_pipelines,omitempty"` AutoDevopsDeployStrategy *string `url:"auto_devops_deploy_strategy,omitempty" json:"auto_devops_deploy_strategy,omitempty"` AutoDevopsEnabled *bool `url:"auto_devops_enabled,omitempty" json:"auto_devops_enabled,omitempty"` AutocloseReferencedIssues *bool `url:"autoclose_referenced_issues,omitempty" json:"autoclose_referenced_issues,omitempty"` Avatar *ProjectAvatar `url:"-" json:"-"` BuildCoverageRegex *string `url:"build_coverage_regex,omitempty" json:"build_coverage_regex,omitempty"` BuildGitStrategy *string `url:"build_git_strategy,omitempty" json:"build_git_strategy,omitempty"` BuildTimeout *int `url:"build_timeout,omitempty" json:"build_timeout,omitempty"` BuildsAccessLevel *AccessControlValue `url:"builds_access_level,omitempty" json:"builds_access_level,omitempty"` CIConfigPath *string `url:"ci_config_path,omitempty" json:"ci_config_path,omitempty"` ContainerExpirationPolicyAttributes *ContainerExpirationPolicyAttributes `url:"container_expiration_policy_attributes,omitempty" json:"container_expiration_policy_attributes,omitempty"` ContainerRegistryAccessLevel *AccessControlValue `url:"container_registry_access_level,omitempty" json:"container_registry_access_level,omitempty"` DefaultBranch *string `url:"default_branch,omitempty" json:"default_branch,omitempty"` Description *string `url:"description,omitempty" json:"description,omitempty"` EmailsEnabled *bool `url:"emails_enabled,omitempty" json:"emails_enabled,omitempty"` EnforceAuthChecksOnUploads *bool `url:"enforce_auth_checks_on_uploads,omitempty" json:"enforce_auth_checks_on_uploads,omitempty"` ExternalAuthorizationClassificationLabel *string `url:"external_authorization_classification_label,omitempty" json:"external_authorization_classification_label,omitempty"` ForkingAccessLevel *AccessControlValue `url:"forking_access_level,omitempty" json:"forking_access_level,omitempty"` GroupWithProjectTemplatesID *int `url:"group_with_project_templates_id,omitempty" json:"group_with_project_templates_id,omitempty"` ImportURL *string `url:"import_url,omitempty" json:"import_url,omitempty"` InitializeWithReadme *bool `url:"initialize_with_readme,omitempty" json:"initialize_with_readme,omitempty"` IssuesAccessLevel *AccessControlValue `url:"issues_access_level,omitempty" json:"issues_access_level,omitempty"` IssueBranchTemplate *string `url:"issue_branch_template,omitempty" json:"issue_branch_template,omitempty"` LFSEnabled *bool `url:"lfs_enabled,omitempty" json:"lfs_enabled,omitempty"` MergeCommitTemplate *string `url:"merge_commit_template,omitempty" json:"merge_commit_template,omitempty"` MergeMethod *MergeMethodValue `url:"merge_method,omitempty" json:"merge_method,omitempty"` MergePipelinesEnabled *bool `url:"merge_pipelines_enabled,omitempty" json:"merge_pipelines_enabled,omitempty"` MergeRequestsAccessLevel *AccessControlValue `url:"merge_requests_access_level,omitempty" json:"merge_requests_access_level,omitempty"` MergeTrainsEnabled *bool `url:"merge_trains_enabled,omitempty" json:"merge_trains_enabled,omitempty"` Mirror *bool `url:"mirror,omitempty" json:"mirror,omitempty"` MirrorTriggerBuilds *bool `url:"mirror_trigger_builds,omitempty" json:"mirror_trigger_builds,omitempty"` ModelExperimentsAccessLevel *AccessControlValue `url:"model_experiments_access_level,omitempty" json:"model_experiments_access_level,omitempty"` ModelRegistryAccessLevel *AccessControlValue `url:"model_registry_access_level,omitempty" json:"model_registry_access_level,omitempty"` Name *string `url:"name,omitempty" json:"name,omitempty"` NamespaceID *int `url:"namespace_id,omitempty" json:"namespace_id,omitempty"` OnlyAllowMergeIfAllDiscussionsAreResolved *bool `url:"only_allow_merge_if_all_discussions_are_resolved,omitempty" json:"only_allow_merge_if_all_discussions_are_resolved,omitempty"` OnlyAllowMergeIfPipelineSucceeds *bool `url:"only_allow_merge_if_pipeline_succeeds,omitempty" json:"only_allow_merge_if_pipeline_succeeds,omitempty"` OperationsAccessLevel *AccessControlValue `url:"operations_access_level,omitempty" json:"operations_access_level,omitempty"` PackagesEnabled *bool `url:"packages_enabled,omitempty" json:"packages_enabled,omitempty"` PagesAccessLevel *AccessControlValue `url:"pages_access_level,omitempty" json:"pages_access_level,omitempty"` Path *string `url:"path,omitempty" json:"path,omitempty"` PublicBuilds *bool `url:"public_builds,omitempty" json:"public_builds,omitempty"` ReleasesAccessLevel *AccessControlValue `url:"releases_access_level,omitempty" json:"releases_access_level,omitempty"` EnvironmentsAccessLevel *AccessControlValue `url:"environments_access_level,omitempty" json:"environments_access_level,omitempty"` FeatureFlagsAccessLevel *AccessControlValue `url:"feature_flags_access_level,omitempty" json:"feature_flags_access_level,omitempty"` InfrastructureAccessLevel *AccessControlValue `url:"infrastructure_access_level,omitempty" json:"infrastructure_access_level,omitempty"` MonitorAccessLevel *AccessControlValue `url:"monitor_access_level,omitempty" json:"monitor_access_level,omitempty"` RemoveSourceBranchAfterMerge *bool `url:"remove_source_branch_after_merge,omitempty" json:"remove_source_branch_after_merge,omitempty"` PrintingMergeRequestLinkEnabled *bool `url:"printing_merge_request_link_enabled,omitempty" json:"printing_merge_request_link_enabled,omitempty"` RepositoryAccessLevel *AccessControlValue `url:"repository_access_level,omitempty" json:"repository_access_level,omitempty"` RepositoryStorage *string `url:"repository_storage,omitempty" json:"repository_storage,omitempty"` RequestAccessEnabled *bool `url:"request_access_enabled,omitempty" json:"request_access_enabled,omitempty"` RequirementsAccessLevel *AccessControlValue `url:"requirements_access_level,omitempty" json:"requirements_access_level,omitempty"` ResolveOutdatedDiffDiscussions *bool `url:"resolve_outdated_diff_discussions,omitempty" json:"resolve_outdated_diff_discussions,omitempty"` SecurityAndComplianceAccessLevel *AccessControlValue `url:"security_and_compliance_access_level,omitempty" json:"security_and_compliance_access_level,omitempty"` SharedRunnersEnabled *bool `url:"shared_runners_enabled,omitempty" json:"shared_runners_enabled,omitempty"` GroupRunnersEnabled *bool `url:"group_runners_enabled,omitempty" json:"group_runners_enabled,omitempty"` ShowDefaultAwardEmojis *bool `url:"show_default_award_emojis,omitempty" json:"show_default_award_emojis,omitempty"` SnippetsAccessLevel *AccessControlValue `url:"snippets_access_level,omitempty" json:"snippets_access_level,omitempty"` SquashCommitTemplate *string `url:"squash_commit_template,omitempty" json:"squash_commit_template,omitempty"` SquashOption *SquashOptionValue `url:"squash_option,omitempty" json:"squash_option,omitempty"` SuggestionCommitMessage *string `url:"suggestion_commit_message,omitempty" json:"suggestion_commit_message,omitempty"` TemplateName *string `url:"template_name,omitempty" json:"template_name,omitempty"` TemplateProjectID *int `url:"template_project_id,omitempty" json:"template_project_id,omitempty"` Topics *[]string `url:"topics,omitempty" json:"topics,omitempty"` UseCustomTemplate *bool `url:"use_custom_template,omitempty" json:"use_custom_template,omitempty"` Visibility *VisibilityValue `url:"visibility,omitempty" json:"visibility,omitempty"` WikiAccessLevel *AccessControlValue `url:"wiki_access_level,omitempty" json:"wiki_access_level,omitempty"` // Deprecated: No longer supported in recent versions. CIForwardDeploymentEnabled *bool `url:"ci_forward_deployment_enabled,omitempty" json:"ci_forward_deployment_enabled,omitempty"` // Deprecated: Use ContainerRegistryAccessLevel instead. ContainerRegistryEnabled *bool `url:"container_registry_enabled,omitempty" json:"container_registry_enabled,omitempty"` // Deprecated: Use EmailsEnabled instead EmailsDisabled *bool `url:"emails_disabled,omitempty" json:"emails_disabled,omitempty"` // Deprecated: Use IssuesAccessLevel instead. IssuesEnabled *bool `url:"issues_enabled,omitempty" json:"issues_enabled,omitempty"` // Deprecated: No longer supported in recent versions. IssuesTemplate *string `url:"issues_template,omitempty" json:"issues_template,omitempty"` // Deprecated: Use BuildsAccessLevel instead. JobsEnabled *bool `url:"jobs_enabled,omitempty" json:"jobs_enabled,omitempty"` // Deprecated: Use MergeRequestsAccessLevel instead. MergeRequestsEnabled *bool `url:"merge_requests_enabled,omitempty" json:"merge_requests_enabled,omitempty"` // Deprecated: No longer supported in recent versions. MergeRequestsTemplate *string `url:"merge_requests_template,omitempty" json:"merge_requests_template,omitempty"` // Deprecated: No longer supported in recent versions. ServiceDeskEnabled *bool `url:"service_desk_enabled,omitempty" json:"service_desk_enabled,omitempty"` // Deprecated: Use SnippetsAccessLevel instead. SnippetsEnabled *bool `url:"snippets_enabled,omitempty" json:"snippets_enabled,omitempty"` // Deprecated: Use Topics instead. (Deprecated in GitLab 14.0) TagList *[]string `url:"tag_list,omitempty" json:"tag_list,omitempty"` // Deprecated: Use WikiAccessLevel instead. WikiEnabled *bool `url:"wiki_enabled,omitempty" json:"wiki_enabled,omitempty"` } // ContainerExpirationPolicyAttributes represents the available container // expiration policy attributes. // // GitLab API docs: https://docs.gitlab.com/ee/api/projects.html#create-project type ContainerExpirationPolicyAttributes struct { Cadence *string `url:"cadence,omitempty" json:"cadence,omitempty"` KeepN *int `url:"keep_n,omitempty" json:"keep_n,omitempty"` OlderThan *string `url:"older_than,omitempty" json:"older_than,omitempty"` NameRegexDelete *string `url:"name_regex_delete,omitempty" json:"name_regex_delete,omitempty"` NameRegexKeep *string `url:"name_regex_keep,omitempty" json:"name_regex_keep,omitempty"` Enabled *bool `url:"enabled,omitempty" json:"enabled,omitempty"` // Deprecated: Is replaced by NameRegexDelete and is internally hardwired to its value. NameRegex *string `url:"name_regex,omitempty" json:"name_regex,omitempty"` } // ProjectAvatar represents a GitLab project avatar. // // GitLab API docs: https://docs.gitlab.com/ee/api/projects.html#create-project type ProjectAvatar struct { Filename string Image io.Reader } // MarshalJSON implements the json.Marshaler interface. func (a *ProjectAvatar) MarshalJSON() ([]byte, error) { if a.Filename == "" && a.Image == nil { return []byte(`""`), nil } type alias ProjectAvatar return json.Marshal((*alias)(a)) } // CreateProject creates a new project owned by the authenticated user. // // GitLab API docs: https://docs.gitlab.com/ee/api/projects.html#create-project func (s *ProjectsService) CreateProject(opt *CreateProjectOptions, options ...RequestOptionFunc) (*Project, *Response, error) { if opt.ContainerExpirationPolicyAttributes != nil { // This is needed to satisfy the API. Should be deleted // when NameRegex is removed (it's now deprecated). opt.ContainerExpirationPolicyAttributes.NameRegex = opt.ContainerExpirationPolicyAttributes.NameRegexDelete } var err error var req *retryablehttp.Request if opt.Avatar == nil { req, err = s.client.NewRequest(http.MethodPost, "projects", opt, options) } else { req, err = s.client.UploadRequest( http.MethodPost, "projects", opt.Avatar.Image, opt.Avatar.Filename, UploadAvatar, opt, options, ) } if err != nil { return nil, nil, err } p := new(Project) resp, err := s.client.Do(req, p) if err != nil { return nil, resp, err } return p, resp, nil } // CreateProjectForUserOptions represents the available CreateProjectForUser() // options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/projects.html#create-project-for-user type CreateProjectForUserOptions CreateProjectOptions // CreateProjectForUser creates a new project owned by the specified user. // Available only for admins. // // GitLab API docs: // https://docs.gitlab.com/ee/api/projects.html#create-project-for-user func (s *ProjectsService) CreateProjectForUser(user int, opt *CreateProjectForUserOptions, options ...RequestOptionFunc) (*Project, *Response, error) { if opt.ContainerExpirationPolicyAttributes != nil { // This is needed to satisfy the API. Should be deleted // when NameRegex is removed (it's now deprecated). opt.ContainerExpirationPolicyAttributes.NameRegex = opt.ContainerExpirationPolicyAttributes.NameRegexDelete } var err error var req *retryablehttp.Request u := fmt.Sprintf("projects/user/%d", user) if opt.Avatar == nil { req, err = s.client.NewRequest(http.MethodPost, u, opt, options) } else { req, err = s.client.UploadRequest( http.MethodPost, u, opt.Avatar.Image, opt.Avatar.Filename, UploadAvatar, opt, options, ) } if err != nil { return nil, nil, err } p := new(Project) resp, err := s.client.Do(req, p) if err != nil { return nil, resp, err } return p, resp, nil } // EditProjectOptions represents the available EditProject() options. // // GitLab API docs: https://docs.gitlab.com/ee/api/projects.html#edit-project type EditProjectOptions struct { AllowMergeOnSkippedPipeline *bool `url:"allow_merge_on_skipped_pipeline,omitempty" json:"allow_merge_on_skipped_pipeline,omitempty"` AllowPipelineTriggerApproveDeployment *bool `url:"allow_pipeline_trigger_approve_deployment,omitempty" json:"allow_pipeline_trigger_approve_deployment,omitempty"` OnlyAllowMergeIfAllStatusChecksPassed *bool `url:"only_allow_merge_if_all_status_checks_passed,omitempty" json:"only_allow_merge_if_all_status_checks_passed,omitempty"` AnalyticsAccessLevel *AccessControlValue `url:"analytics_access_level,omitempty" json:"analytics_access_level,omitempty"` ApprovalsBeforeMerge *int `url:"approvals_before_merge,omitempty" json:"approvals_before_merge,omitempty"` AutoCancelPendingPipelines *string `url:"auto_cancel_pending_pipelines,omitempty" json:"auto_cancel_pending_pipelines,omitempty"` AutoDevopsDeployStrategy *string `url:"auto_devops_deploy_strategy,omitempty" json:"auto_devops_deploy_strategy,omitempty"` AutoDevopsEnabled *bool `url:"auto_devops_enabled,omitempty" json:"auto_devops_enabled,omitempty"` AutocloseReferencedIssues *bool `url:"autoclose_referenced_issues,omitempty" json:"autoclose_referenced_issues,omitempty"` Avatar *ProjectAvatar `url:"-" json:"avatar,omitempty"` BuildCoverageRegex *string `url:"build_coverage_regex,omitempty" json:"build_coverage_regex,omitempty"` BuildGitStrategy *string `url:"build_git_strategy,omitempty" json:"build_git_strategy,omitempty"` BuildTimeout *int `url:"build_timeout,omitempty" json:"build_timeout,omitempty"` BuildsAccessLevel *AccessControlValue `url:"builds_access_level,omitempty" json:"builds_access_level,omitempty"` CIConfigPath *string `url:"ci_config_path,omitempty" json:"ci_config_path,omitempty"` CIDefaultGitDepth *int `url:"ci_default_git_depth,omitempty" json:"ci_default_git_depth,omitempty"` CIForwardDeploymentEnabled *bool `url:"ci_forward_deployment_enabled,omitempty" json:"ci_forward_deployment_enabled,omitempty"` CIForwardDeploymentRollbackAllowed *bool `url:"ci_forward_deployment_rollback_allowed,omitempty" json:"ci_forward_deployment_rollback_allowed,omitempty"` CISeperateCache *bool `url:"ci_separated_caches,omitempty" json:"ci_separated_caches,omitempty"` CIRestrictPipelineCancellationRole *AccessControlValue `url:"ci_restrict_pipeline_cancellation_role,omitempty" json:"ci_restrict_pipeline_cancellation_role,omitempty"` CIPipelineVariablesMinimumOverrideRole *CIPipelineVariablesMinimumOverrideRoleValue `url:"ci_pipeline_variables_minimum_override_role,omitempty" json:"ci_pipeline_variables_minimum_override_role,omitempty"` ContainerExpirationPolicyAttributes *ContainerExpirationPolicyAttributes `url:"container_expiration_policy_attributes,omitempty" json:"container_expiration_policy_attributes,omitempty"` ContainerRegistryAccessLevel *AccessControlValue `url:"container_registry_access_level,omitempty" json:"container_registry_access_level,omitempty"` DefaultBranch *string `url:"default_branch,omitempty" json:"default_branch,omitempty"` Description *string `url:"description,omitempty" json:"description,omitempty"` EmailsEnabled *bool `url:"emails_enabled,omitempty" json:"emails_enabled,omitempty"` EnforceAuthChecksOnUploads *bool `url:"enforce_auth_checks_on_uploads,omitempty" json:"enforce_auth_checks_on_uploads,omitempty"` ExternalAuthorizationClassificationLabel *string `url:"external_authorization_classification_label,omitempty" json:"external_authorization_classification_label,omitempty"` ForkingAccessLevel *AccessControlValue `url:"forking_access_level,omitempty" json:"forking_access_level,omitempty"` ImportURL *string `url:"import_url,omitempty" json:"import_url,omitempty"` IssuesAccessLevel *AccessControlValue `url:"issues_access_level,omitempty" json:"issues_access_level,omitempty"` IssueBranchTemplate *string `url:"issue_branch_template,omitempty" json:"issue_branch_template,omitempty"` IssuesTemplate *string `url:"issues_template,omitempty" json:"issues_template,omitempty"` KeepLatestArtifact *bool `url:"keep_latest_artifact,omitempty" json:"keep_latest_artifact,omitempty"` LFSEnabled *bool `url:"lfs_enabled,omitempty" json:"lfs_enabled,omitempty"` MergeCommitTemplate *string `url:"merge_commit_template,omitempty" json:"merge_commit_template,omitempty"` MergeRequestDefaultTargetSelf *bool `url:"mr_default_target_self,omitempty" json:"mr_default_target_self,omitempty"` MergeMethod *MergeMethodValue `url:"merge_method,omitempty" json:"merge_method,omitempty"` MergePipelinesEnabled *bool `url:"merge_pipelines_enabled,omitempty" json:"merge_pipelines_enabled,omitempty"` MergeRequestsAccessLevel *AccessControlValue `url:"merge_requests_access_level,omitempty" json:"merge_requests_access_level,omitempty"` MergeRequestsTemplate *string `url:"merge_requests_template,omitempty" json:"merge_requests_template,omitempty"` MergeTrainsEnabled *bool `url:"merge_trains_enabled,omitempty" json:"merge_trains_enabled,omitempty"` Mirror *bool `url:"mirror,omitempty" json:"mirror,omitempty"` MirrorBranchRegex *string `url:"mirror_branch_regex,omitempty" json:"mirror_branch_regex,omitempty"` MirrorOverwritesDivergedBranches *bool `url:"mirror_overwrites_diverged_branches,omitempty" json:"mirror_overwrites_diverged_branches,omitempty"` MirrorTriggerBuilds *bool `url:"mirror_trigger_builds,omitempty" json:"mirror_trigger_builds,omitempty"` MirrorUserID *int `url:"mirror_user_id,omitempty" json:"mirror_user_id,omitempty"` ModelExperimentsAccessLevel *AccessControlValue `url:"model_experiments_access_level,omitempty" json:"model_experiments_access_level,omitempty"` ModelRegistryAccessLevel *AccessControlValue `url:"model_registry_access_level,omitempty" json:"model_registry_access_level,omitempty"` Name *string `url:"name,omitempty" json:"name,omitempty"` OnlyAllowMergeIfAllDiscussionsAreResolved *bool `url:"only_allow_merge_if_all_discussions_are_resolved,omitempty" json:"only_allow_merge_if_all_discussions_are_resolved,omitempty"` OnlyAllowMergeIfPipelineSucceeds *bool `url:"only_allow_merge_if_pipeline_succeeds,omitempty" json:"only_allow_merge_if_pipeline_succeeds,omitempty"` OnlyMirrorProtectedBranches *bool `url:"only_mirror_protected_branches,omitempty" json:"only_mirror_protected_branches,omitempty"` OperationsAccessLevel *AccessControlValue `url:"operations_access_level,omitempty" json:"operations_access_level,omitempty"` PackagesEnabled *bool `url:"packages_enabled,omitempty" json:"packages_enabled,omitempty"` PagesAccessLevel *AccessControlValue `url:"pages_access_level,omitempty" json:"pages_access_level,omitempty"` Path *string `url:"path,omitempty" json:"path,omitempty"` PublicBuilds *bool `url:"public_builds,omitempty" json:"public_builds,omitempty"` ReleasesAccessLevel *AccessControlValue `url:"releases_access_level,omitempty" json:"releases_access_level,omitempty"` EnvironmentsAccessLevel *AccessControlValue `url:"environments_access_level,omitempty" json:"environments_access_level,omitempty"` FeatureFlagsAccessLevel *AccessControlValue `url:"feature_flags_access_level,omitempty" json:"feature_flags_access_level,omitempty"` InfrastructureAccessLevel *AccessControlValue `url:"infrastructure_access_level,omitempty" json:"infrastructure_access_level,omitempty"` MonitorAccessLevel *AccessControlValue `url:"monitor_access_level,omitempty" json:"monitor_access_level,omitempty"` RemoveSourceBranchAfterMerge *bool `url:"remove_source_branch_after_merge,omitempty" json:"remove_source_branch_after_merge,omitempty"` PreventMergeWithoutJiraIssue *bool `url:"prevent_merge_without_jira_issue,omitempty" json:"prevent_merge_without_jira_issue,omitempty"` PrintingMergeRequestLinkEnabled *bool `url:"printing_merge_request_link_enabled,omitempty" json:"printing_merge_request_link_enabled,omitempty"` RepositoryAccessLevel *AccessControlValue `url:"repository_access_level,omitempty" json:"repository_access_level,omitempty"` RepositoryStorage *string `url:"repository_storage,omitempty" json:"repository_storage,omitempty"` RequestAccessEnabled *bool `url:"request_access_enabled,omitempty" json:"request_access_enabled,omitempty"` RequirementsAccessLevel *AccessControlValue `url:"requirements_access_level,omitempty" json:"requirements_access_level,omitempty"` ResolveOutdatedDiffDiscussions *bool `url:"resolve_outdated_diff_discussions,omitempty" json:"resolve_outdated_diff_discussions,omitempty"` RestrictUserDefinedVariables *bool `url:"restrict_user_defined_variables,omitempty" json:"restrict_user_defined_variables,omitempty"` SecurityAndComplianceAccessLevel *AccessControlValue `url:"security_and_compliance_access_level,omitempty" json:"security_and_compliance_access_level,omitempty"` ServiceDeskEnabled *bool `url:"service_desk_enabled,omitempty" json:"service_desk_enabled,omitempty"` SharedRunnersEnabled *bool `url:"shared_runners_enabled,omitempty" json:"shared_runners_enabled,omitempty"` GroupRunnersEnabled *bool `url:"group_runners_enabled,omitempty" json:"group_runners_enabled,omitempty"` ShowDefaultAwardEmojis *bool `url:"show_default_award_emojis,omitempty" json:"show_default_award_emojis,omitempty"` SnippetsAccessLevel *AccessControlValue `url:"snippets_access_level,omitempty" json:"snippets_access_level,omitempty"` SquashCommitTemplate *string `url:"squash_commit_template,omitempty" json:"squash_commit_template,omitempty"` SquashOption *SquashOptionValue `url:"squash_option,omitempty" json:"squash_option,omitempty"` SuggestionCommitMessage *string `url:"suggestion_commit_message,omitempty" json:"suggestion_commit_message,omitempty"` Topics *[]string `url:"topics,omitempty" json:"topics,omitempty"` Visibility *VisibilityValue `url:"visibility,omitempty" json:"visibility,omitempty"` WikiAccessLevel *AccessControlValue `url:"wiki_access_level,omitempty" json:"wiki_access_level,omitempty"` // Deprecated: Use ContainerRegistryAccessLevel instead. ContainerRegistryEnabled *bool `url:"container_registry_enabled,omitempty" json:"container_registry_enabled,omitempty"` // Deprecated: Use EmailsEnabled instead EmailsDisabled *bool `url:"emails_disabled,omitempty" json:"emails_disabled,omitempty"` // Deprecated: Use IssuesAccessLevel instead. IssuesEnabled *bool `url:"issues_enabled,omitempty" json:"issues_enabled,omitempty"` // Deprecated: Use BuildsAccessLevel instead. JobsEnabled *bool `url:"jobs_enabled,omitempty" json:"jobs_enabled,omitempty"` // Deprecated: Use MergeRequestsAccessLevel instead. MergeRequestsEnabled *bool `url:"merge_requests_enabled,omitempty" json:"merge_requests_enabled,omitempty"` // Deprecated: Use SnippetsAccessLevel instead. SnippetsEnabled *bool `url:"snippets_enabled,omitempty" json:"snippets_enabled,omitempty"` // Deprecated: Use Topics instead. (Deprecated in GitLab 14.0) TagList *[]string `url:"tag_list,omitempty" json:"tag_list,omitempty"` // Deprecated: Use WikiAccessLevel instead. WikiEnabled *bool `url:"wiki_enabled,omitempty" json:"wiki_enabled,omitempty"` } // EditProject updates an existing project. // // GitLab API docs: https://docs.gitlab.com/ee/api/projects.html#edit-project func (s *ProjectsService) EditProject(pid interface{}, opt *EditProjectOptions, options ...RequestOptionFunc) (*Project, *Response, error) { if opt.ContainerExpirationPolicyAttributes != nil { // This is needed to satisfy the API. Should be deleted // when NameRegex is removed (it's now deprecated). opt.ContainerExpirationPolicyAttributes.NameRegex = opt.ContainerExpirationPolicyAttributes.NameRegexDelete } project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s", PathEscape(project)) var req *retryablehttp.Request if opt.Avatar == nil || (opt.Avatar.Filename == "" && opt.Avatar.Image == nil) { req, err = s.client.NewRequest(http.MethodPut, u, opt, options) } else { req, err = s.client.UploadRequest( http.MethodPut, u, opt.Avatar.Image, opt.Avatar.Filename, UploadAvatar, opt, options, ) } if err != nil { return nil, nil, err } p := new(Project) resp, err := s.client.Do(req, p) if err != nil { return nil, resp, err } return p, resp, nil } // ForkProjectOptions represents the available ForkProject() options. // // GitLab API docs: https://docs.gitlab.com/ee/api/projects.html#fork-project type ForkProjectOptions struct { Description *string `url:"description,omitempty" json:"description,omitempty"` MergeRequestDefaultTargetSelf *bool `url:"mr_default_target_self,omitempty" json:"mr_default_target_self,omitempty"` Name *string `url:"name,omitempty" json:"name,omitempty"` NamespaceID *int `url:"namespace_id,omitempty" json:"namespace_id,omitempty"` NamespacePath *string `url:"namespace_path,omitempty" json:"namespace_path,omitempty"` Path *string `url:"path,omitempty" json:"path,omitempty"` Visibility *VisibilityValue `url:"visibility,omitempty" json:"visibility,omitempty"` // Deprecated: This parameter has been split into NamespaceID and NamespacePath. Namespace *string `url:"namespace,omitempty" json:"namespace,omitempty"` } // ForkProject forks a project into the user namespace of the authenticated // user. // // GitLab API docs: https://docs.gitlab.com/ee/api/projects.html#fork-project func (s *ProjectsService) ForkProject(pid interface{}, opt *ForkProjectOptions, options ...RequestOptionFunc) (*Project, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/fork", PathEscape(project)) req, err := s.client.NewRequest(http.MethodPost, u, opt, options) if err != nil { return nil, nil, err } p := new(Project) resp, err := s.client.Do(req, p) if err != nil { return nil, resp, err } return p, resp, nil } // StarProject stars a given the project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/projects.html#star-a-project func (s *ProjectsService) StarProject(pid interface{}, options ...RequestOptionFunc) (*Project, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/star", PathEscape(project)) req, err := s.client.NewRequest(http.MethodPost, u, nil, options) if err != nil { return nil, nil, err } p := new(Project) resp, err := s.client.Do(req, p) if err != nil { return nil, resp, err } return p, resp, nil } // ListProjectInvidedGroupOptions represents the available // ListProjectsInvitedGroups() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/projects.html#list-a-projects-invited-groups type ListProjectInvidedGroupOptions struct { ListOptions Search *string `url:"search,omitempty" json:"search,omitempty"` MinAccessLevel *AccessLevelValue `url:"min_access_level,omitempty" json:"min_access_level,omitempty"` Relation *[]string `url:"relation,omitempty" json:"relation,omitempty"` WithCustomAttributes *bool `url:"with_custom_attributes,omitempty" json:"with_custom_attributes,omitempty"` } // ListProjectsInvitedGroups lists invited groups of a project // // GitLab API docs: // https://docs.gitlab.com/ee/api/projects.html#list-a-projects-invited-groups func (s *ProjectsService) ListProjectsInvitedGroups(pid interface{}, opt *ListProjectInvidedGroupOptions, options ...RequestOptionFunc) ([]*ProjectGroup, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/invited_groups", PathEscape(project)) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } var pg []*ProjectGroup resp, err := s.client.Do(req, &pg) if err != nil { return nil, resp, err } return pg, resp, nil } // UnstarProject unstars a given project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/projects.html#unstar-a-project func (s *ProjectsService) UnstarProject(pid interface{}, options ...RequestOptionFunc) (*Project, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/unstar", PathEscape(project)) req, err := s.client.NewRequest(http.MethodPost, u, nil, options) if err != nil { return nil, nil, err } p := new(Project) resp, err := s.client.Do(req, p) if err != nil { return nil, resp, err } return p, resp, nil } // ArchiveProject archives the project if the user is either admin or the // project owner of this project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/projects.html#archive-a-project func (s *ProjectsService) ArchiveProject(pid interface{}, options ...RequestOptionFunc) (*Project, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/archive", PathEscape(project)) req, err := s.client.NewRequest(http.MethodPost, u, nil, options) if err != nil { return nil, nil, err } p := new(Project) resp, err := s.client.Do(req, p) if err != nil { return nil, resp, err } return p, resp, nil } // UnarchiveProject unarchives the project if the user is either admin or // the project owner of this project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/projects.html#unarchive-a-project func (s *ProjectsService) UnarchiveProject(pid interface{}, options ...RequestOptionFunc) (*Project, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/unarchive", PathEscape(project)) req, err := s.client.NewRequest(http.MethodPost, u, nil, options) if err != nil { return nil, nil, err } p := new(Project) resp, err := s.client.Do(req, p) if err != nil { return nil, resp, err } return p, resp, nil } // DeleteProjectOptions represents the available DeleteProject() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/projects.html#delete-project type DeleteProjectOptions struct { FullPath *string `url:"full_path" json:"full_path"` PermanentlyRemove *bool `url:"permanently_remove" json:"permanently_remove"` } // DeleteProject removes a project including all associated resources // (issues, merge requests etc.) // // GitLab API docs: // https://docs.gitlab.com/ee/api/projects.html#delete-project func (s *ProjectsService) DeleteProject(pid interface{}, opt *DeleteProjectOptions, options ...RequestOptionFunc) (*Response, error) { project, err := parseID(pid) if err != nil { return nil, err } u := fmt.Sprintf("projects/%s", PathEscape(project)) req, err := s.client.NewRequest(http.MethodDelete, u, opt, options) if err != nil { return nil, err } return s.client.Do(req, nil) } // ShareWithGroupOptions represents the available SharedWithGroup() options. // // GitLab API docs: https://docs.gitlab.com/ee/api/projects.html#share-project-with-group type ShareWithGroupOptions struct { ExpiresAt *string `url:"expires_at" json:"expires_at"` GroupAccess *AccessLevelValue `url:"group_access" json:"group_access"` GroupID *int `url:"group_id" json:"group_id"` } // ShareProjectWithGroup allows to share a project with a group. // // GitLab API docs: https://docs.gitlab.com/ee/api/projects.html#share-project-with-group func (s *ProjectsService) ShareProjectWithGroup(pid interface{}, opt *ShareWithGroupOptions, options ...RequestOptionFunc) (*Response, error) { project, err := parseID(pid) if err != nil { return nil, err } u := fmt.Sprintf("projects/%s/share", PathEscape(project)) req, err := s.client.NewRequest(http.MethodPost, u, opt, options) if err != nil { return nil, err } return s.client.Do(req, nil) } // DeleteSharedProjectFromGroup allows to unshare a project from a group. // // GitLab API docs: https://docs.gitlab.com/ee/api/projects.html#delete-a-shared-project-link-within-a-group func (s *ProjectsService) DeleteSharedProjectFromGroup(pid interface{}, groupID int, options ...RequestOptionFunc) (*Response, error) { project, err := parseID(pid) if err != nil { return nil, err } u := fmt.Sprintf("projects/%s/share/%d", PathEscape(project), groupID) req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } // HookCustomHeader represents a project or group hook custom header // Note: "Key" is returned from the Get operation, but "Value" is not // The List operation doesn't return any headers at all for Projects, // but does return headers for Groups type HookCustomHeader struct { Key string `json:"key"` Value string `json:"value"` } // ProjectHook represents a project hook. // // GitLab API docs: // https://docs.gitlab.com/ee/api/projects.html#list-project-hooks type ProjectHook struct { ID int `json:"id"` URL string `json:"url"` Name string `json:"name"` Description string `json:"description"` ConfidentialNoteEvents bool `json:"confidential_note_events"` ProjectID int `json:"project_id"` PushEvents bool `json:"push_events"` PushEventsBranchFilter string `json:"push_events_branch_filter"` IssuesEvents bool `json:"issues_events"` ConfidentialIssuesEvents bool `json:"confidential_issues_events"` MergeRequestsEvents bool `json:"merge_requests_events"` TagPushEvents bool `json:"tag_push_events"` NoteEvents bool `json:"note_events"` JobEvents bool `json:"job_events"` PipelineEvents bool `json:"pipeline_events"` WikiPageEvents bool `json:"wiki_page_events"` DeploymentEvents bool `json:"deployment_events"` ReleasesEvents bool `json:"releases_events"` EnableSSLVerification bool `json:"enable_ssl_verification"` AlertStatus string `json:"alert_status"` CreatedAt *time.Time `json:"created_at"` ResourceAccessTokenEvents bool `json:"resource_access_token_events"` CustomWebhookTemplate string `json:"custom_webhook_template"` CustomHeaders []*HookCustomHeader `json:"custom_headers"` } // ListProjectHooksOptions represents the available ListProjectHooks() options. // // GitLab API docs: https://docs.gitlab.com/ee/api/projects.html#list-project-hooks type ListProjectHooksOptions ListOptions // ListProjectHooks gets a list of project hooks. // // GitLab API docs: // https://docs.gitlab.com/ee/api/projects.html#list-project-hooks func (s *ProjectsService) ListProjectHooks(pid interface{}, opt *ListProjectHooksOptions, options ...RequestOptionFunc) ([]*ProjectHook, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/hooks", PathEscape(project)) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } var ph []*ProjectHook resp, err := s.client.Do(req, &ph) if err != nil { return nil, resp, err } return ph, resp, nil } // GetProjectHook gets a specific hook for a project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/projects.html#get-project-hook func (s *ProjectsService) GetProjectHook(pid interface{}, hook int, options ...RequestOptionFunc) (*ProjectHook, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/hooks/%d", PathEscape(project), hook) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } ph := new(ProjectHook) resp, err := s.client.Do(req, ph) if err != nil { return nil, resp, err } return ph, resp, nil } // AddProjectHookOptions represents the available AddProjectHook() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/projects.html#add-project-hook type AddProjectHookOptions struct { Name *string `url:"name,omitempty" json:"name,omitempty"` Description *string `url:"description,omitempty" json:"description,omitempty"` ConfidentialIssuesEvents *bool `url:"confidential_issues_events,omitempty" json:"confidential_issues_events,omitempty"` ConfidentialNoteEvents *bool `url:"confidential_note_events,omitempty" json:"confidential_note_events,omitempty"` DeploymentEvents *bool `url:"deployment_events,omitempty" json:"deployment_events,omitempty"` EnableSSLVerification *bool `url:"enable_ssl_verification,omitempty" json:"enable_ssl_verification,omitempty"` IssuesEvents *bool `url:"issues_events,omitempty" json:"issues_events,omitempty"` JobEvents *bool `url:"job_events,omitempty" json:"job_events,omitempty"` MergeRequestsEvents *bool `url:"merge_requests_events,omitempty" json:"merge_requests_events,omitempty"` NoteEvents *bool `url:"note_events,omitempty" json:"note_events,omitempty"` PipelineEvents *bool `url:"pipeline_events,omitempty" json:"pipeline_events,omitempty"` PushEvents *bool `url:"push_events,omitempty" json:"push_events,omitempty"` PushEventsBranchFilter *string `url:"push_events_branch_filter,omitempty" json:"push_events_branch_filter,omitempty"` ReleasesEvents *bool `url:"releases_events,omitempty" json:"releases_events,omitempty"` TagPushEvents *bool `url:"tag_push_events,omitempty" json:"tag_push_events,omitempty"` Token *string `url:"token,omitempty" json:"token,omitempty"` URL *string `url:"url,omitempty" json:"url,omitempty"` WikiPageEvents *bool `url:"wiki_page_events,omitempty" json:"wiki_page_events,omitempty"` ResourceAccessTokenEvents *bool `url:"resource_access_token_events,omitempty" json:"resource_access_token_events,omitempty"` CustomWebhookTemplate *string `url:"custom_webhook_template,omitempty" json:"custom_webhook_template,omitempty"` CustomHeaders *[]*HookCustomHeader `url:"custom_headers,omitempty" json:"custom_headers,omitempty"` } // AddProjectHook adds a hook to a specified project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/projects.html#add-project-hook func (s *ProjectsService) AddProjectHook(pid interface{}, opt *AddProjectHookOptions, options ...RequestOptionFunc) (*ProjectHook, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/hooks", PathEscape(project)) req, err := s.client.NewRequest(http.MethodPost, u, opt, options) if err != nil { return nil, nil, err } ph := new(ProjectHook) resp, err := s.client.Do(req, ph) if err != nil { return nil, resp, err } return ph, resp, nil } // EditProjectHookOptions represents the available EditProjectHook() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/projects.html#edit-project-hook type EditProjectHookOptions struct { Name *string `url:"name,omitempty" json:"name,omitempty"` Description *string `url:"description,omitempty" json:"description,omitempty"` ConfidentialIssuesEvents *bool `url:"confidential_issues_events,omitempty" json:"confidential_issues_events,omitempty"` ConfidentialNoteEvents *bool `url:"confidential_note_events,omitempty" json:"confidential_note_events,omitempty"` DeploymentEvents *bool `url:"deployment_events,omitempty" json:"deployment_events,omitempty"` EnableSSLVerification *bool `url:"enable_ssl_verification,omitempty" json:"enable_ssl_verification,omitempty"` IssuesEvents *bool `url:"issues_events,omitempty" json:"issues_events,omitempty"` JobEvents *bool `url:"job_events,omitempty" json:"job_events,omitempty"` MergeRequestsEvents *bool `url:"merge_requests_events,omitempty" json:"merge_requests_events,omitempty"` NoteEvents *bool `url:"note_events,omitempty" json:"note_events,omitempty"` PipelineEvents *bool `url:"pipeline_events,omitempty" json:"pipeline_events,omitempty"` PushEvents *bool `url:"push_events,omitempty" json:"push_events,omitempty"` PushEventsBranchFilter *string `url:"push_events_branch_filter,omitempty" json:"push_events_branch_filter,omitempty"` ReleasesEvents *bool `url:"releases_events,omitempty" json:"releases_events,omitempty"` TagPushEvents *bool `url:"tag_push_events,omitempty" json:"tag_push_events,omitempty"` Token *string `url:"token,omitempty" json:"token,omitempty"` URL *string `url:"url,omitempty" json:"url,omitempty"` WikiPageEvents *bool `url:"wiki_page_events,omitempty" json:"wiki_page_events,omitempty"` ResourceAccessTokenEvents *bool `url:"resource_access_token_events,omitempty" json:"resource_access_token_events,omitempty"` CustomWebhookTemplate *string `url:"custom_webhook_template,omitempty" json:"custom_webhook_template,omitempty"` CustomHeaders *[]*HookCustomHeader `url:"custom_headers,omitempty" json:"custom_headers,omitempty"` } // EditProjectHook edits a hook for a specified project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/projects.html#edit-project-hook func (s *ProjectsService) EditProjectHook(pid interface{}, hook int, opt *EditProjectHookOptions, options ...RequestOptionFunc) (*ProjectHook, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/hooks/%d", PathEscape(project), hook) req, err := s.client.NewRequest(http.MethodPut, u, opt, options) if err != nil { return nil, nil, err } ph := new(ProjectHook) resp, err := s.client.Do(req, ph) if err != nil { return nil, resp, err } return ph, resp, nil } // DeleteProjectHook removes a hook from a project. This is an idempotent // method and can be called multiple times. Either the hook is available or not. // // GitLab API docs: // https://docs.gitlab.com/ee/api/projects.html#delete-project-hook func (s *ProjectsService) DeleteProjectHook(pid interface{}, hook int, options ...RequestOptionFunc) (*Response, error) { project, err := parseID(pid) if err != nil { return nil, err } u := fmt.Sprintf("projects/%s/hooks/%d", PathEscape(project), hook) req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } // TriggerTestProjectHook Trigger a test hook for a specified project. // // In GitLab 17.0 and later, this endpoint has a special rate limit. // In GitLab 17.0 the rate was three requests per minute for each project hook. // In GitLab 17.1 this was changed to five requests per minute for each project // and authenticated user. // // To disable this limit on self-managed GitLab and GitLab Dedicated, // an administrator can disable the feature flag named web_hook_test_api_endpoint_rate_limit. // // GitLab API docs: // https://docs.gitlab.com/ee/api/projects.html#trigger-a-test-project-hook func (s *ProjectsService) TriggerTestProjectHook(pid interface{}, hook int, event ProjectHookEvent, options ...RequestOptionFunc) (*Response, error) { project, err := parseID(pid) if err != nil { return nil, err } u := fmt.Sprintf("projects/%s/hooks/%d/test/%s", PathEscape(project), hook, string(event)) req, err := s.client.NewRequest(http.MethodPost, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } // SetHookCustomHeaderOptions represents the available SetProjectCustomHeader() // options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/projects.html#set-a-custom-header type SetHookCustomHeaderOptions struct { Value *string `json:"value,omitempty"` } // SetProjectCustomHeader creates or updates a project custom webhook header. // // GitLab API docs: // https://docs.gitlab.com/ee/api/projects.html#set-a-custom-header func (s *ProjectsService) SetProjectCustomHeader(pid interface{}, hook int, key string, opt *SetHookCustomHeaderOptions, options ...RequestOptionFunc) (*Response, error) { project, err := parseID(pid) if err != nil { return nil, err } u := fmt.Sprintf("projects/%s/hooks/%d/custom_headers/%s", PathEscape(project), hook, key) req, err := s.client.NewRequest(http.MethodPut, u, opt, options) if err != nil { return nil, err } return s.client.Do(req, nil) } // DeleteProjectCustomHeader deletes a project custom webhook header. // // GitLab API docs: // https://docs.gitlab.com/ee/api/projects.html#delete-a-custom-header func (s *ProjectsService) DeleteProjectCustomHeader(pid interface{}, hook int, key string, options ...RequestOptionFunc) (*Response, error) { project, err := parseID(pid) if err != nil { return nil, err } u := fmt.Sprintf("projects/%s/hooks/%d/custom_headers/%s", PathEscape(project), hook, key) req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } // ProjectForkRelation represents a project fork relationship. // // GitLab API docs: // https://docs.gitlab.com/ee/api/projects.html#admin-fork-relation type ProjectForkRelation struct { ID int `json:"id"` ForkedToProjectID int `json:"forked_to_project_id"` ForkedFromProjectID int `json:"forked_from_project_id"` CreatedAt *time.Time `json:"created_at"` UpdatedAt *time.Time `json:"updated_at"` } // CreateProjectForkRelation creates a forked from/to relation between // existing projects. // // GitLab API docs: // https://docs.gitlab.com/ee/api/projects.html#create-a-forked-fromto-relation-between-existing-projects. func (s *ProjectsService) CreateProjectForkRelation(pid interface{}, fork int, options ...RequestOptionFunc) (*ProjectForkRelation, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/fork/%d", PathEscape(project), fork) req, err := s.client.NewRequest(http.MethodPost, u, nil, options) if err != nil { return nil, nil, err } pfr := new(ProjectForkRelation) resp, err := s.client.Do(req, pfr) if err != nil { return nil, resp, err } return pfr, resp, nil } // DeleteProjectForkRelation deletes an existing forked from relationship. // // GitLab API docs: // https://docs.gitlab.com/ee/api/projects.html#delete-an-existing-forked-from-relationship func (s *ProjectsService) DeleteProjectForkRelation(pid interface{}, options ...RequestOptionFunc) (*Response, error) { project, err := parseID(pid) if err != nil { return nil, err } u := fmt.Sprintf("projects/%s/fork", PathEscape(project)) req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } // ProjectFile represents an uploaded project file. // // GitLab API docs: https://docs.gitlab.com/ee/api/projects.html#upload-a-file type ProjectFile struct { Alt string `json:"alt"` URL string `json:"url"` FullPath string `json:"full_path"` Markdown string `json:"markdown"` } // UploadFile uploads a file. // // Deprecated: UploadFile is deprecated and will be removed in a future release. // Use [ProjectMarkdownUploadsService.UploadProjectMarkdown] instead for uploading // markdown files to a project. // // GitLab API docs: https://docs.gitlab.com/ee/api/project_markdown_uploads.html#upload-a-file func (s *ProjectsService) UploadFile(pid interface{}, content io.Reader, filename string, options ...RequestOptionFunc) (*ProjectFile, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/uploads", PathEscape(project)) req, err := s.client.UploadRequest( http.MethodPost, u, content, filename, UploadFile, nil, options, ) if err != nil { return nil, nil, err } pf := new(ProjectFile) resp, err := s.client.Do(req, pf) if err != nil { return nil, resp, err } return pf, resp, nil } // UploadAvatar uploads an avatar. // // GitLab API docs: // https://docs.gitlab.com/ee/api/projects.html#upload-a-project-avatar func (s *ProjectsService) UploadAvatar(pid interface{}, avatar io.Reader, filename string, options ...RequestOptionFunc) (*Project, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s", PathEscape(project)) req, err := s.client.UploadRequest( http.MethodPut, u, avatar, filename, UploadAvatar, nil, options, ) if err != nil { return nil, nil, err } p := new(Project) resp, err := s.client.Do(req, p) if err != nil { return nil, resp, err } return p, resp, nil } // DownloadAvatar downloads an avatar. // // GitLab API docs: // https://docs.gitlab.com/ee/api/projects.html#download-a-project-avatar func (s *ProjectsService) DownloadAvatar(pid interface{}, options ...RequestOptionFunc) (*bytes.Reader, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/avatar", PathEscape(project)) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } avatar := new(bytes.Buffer) resp, err := s.client.Do(req, avatar) if err != nil { return nil, resp, err } return bytes.NewReader(avatar.Bytes()), resp, err } // ListProjectForks gets a list of project forks. // // GitLab API docs: // https://docs.gitlab.com/ee/api/projects.html#list-forks-of-a-project func (s *ProjectsService) ListProjectForks(pid interface{}, opt *ListProjectsOptions, options ...RequestOptionFunc) ([]*Project, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/forks", PathEscape(project)) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } var forks []*Project resp, err := s.client.Do(req, &forks) if err != nil { return nil, resp, err } return forks, resp, nil } // ProjectPushRules represents a project push rule. // // GitLab API docs: // https://docs.gitlab.com/ee/api/projects.html#push-rules type ProjectPushRules struct { ID int `json:"id"` ProjectID int `json:"project_id"` CommitMessageRegex string `json:"commit_message_regex"` CommitMessageNegativeRegex string `json:"commit_message_negative_regex"` BranchNameRegex string `json:"branch_name_regex"` DenyDeleteTag bool `json:"deny_delete_tag"` CreatedAt *time.Time `json:"created_at"` MemberCheck bool `json:"member_check"` PreventSecrets bool `json:"prevent_secrets"` AuthorEmailRegex string `json:"author_email_regex"` FileNameRegex string `json:"file_name_regex"` MaxFileSize int `json:"max_file_size"` CommitCommitterCheck bool `json:"commit_committer_check"` CommitCommitterNameCheck bool `json:"commit_committer_name_check"` RejectUnsignedCommits bool `json:"reject_unsigned_commits"` RejectNonDCOCommits bool `json:"reject_non_dco_commits"` } // GetProjectPushRules gets the push rules of a project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/projects.html#get-project-push-rules func (s *ProjectsService) GetProjectPushRules(pid interface{}, options ...RequestOptionFunc) (*ProjectPushRules, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/push_rule", PathEscape(project)) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } ppr := new(ProjectPushRules) resp, err := s.client.Do(req, ppr) if err != nil { return nil, resp, err } return ppr, resp, nil } // AddProjectPushRuleOptions represents the available AddProjectPushRule() // options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/projects.html#add-project-push-rule type AddProjectPushRuleOptions struct { AuthorEmailRegex *string `url:"author_email_regex,omitempty" json:"author_email_regex,omitempty"` BranchNameRegex *string `url:"branch_name_regex,omitempty" json:"branch_name_regex,omitempty"` CommitCommitterCheck *bool `url:"commit_committer_check,omitempty" json:"commit_committer_check,omitempty"` CommitCommitterNameCheck *bool `url:"commit_committer_name_check,omitempty" json:"commit_committer_name_check,omitempty"` CommitMessageNegativeRegex *string `url:"commit_message_negative_regex,omitempty" json:"commit_message_negative_regex,omitempty"` CommitMessageRegex *string `url:"commit_message_regex,omitempty" json:"commit_message_regex,omitempty"` DenyDeleteTag *bool `url:"deny_delete_tag,omitempty" json:"deny_delete_tag,omitempty"` FileNameRegex *string `url:"file_name_regex,omitempty" json:"file_name_regex,omitempty"` MaxFileSize *int `url:"max_file_size,omitempty" json:"max_file_size,omitempty"` MemberCheck *bool `url:"member_check,omitempty" json:"member_check,omitempty"` PreventSecrets *bool `url:"prevent_secrets,omitempty" json:"prevent_secrets,omitempty"` RejectUnsignedCommits *bool `url:"reject_unsigned_commits,omitempty" json:"reject_unsigned_commits,omitempty"` RejectNonDCOCommits *bool `url:"reject_non_dco_commits,omitempty" json:"reject_non_dco_commits,omitempty"` } // AddProjectPushRule adds a push rule to a specified project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/projects.html#add-project-push-rule func (s *ProjectsService) AddProjectPushRule(pid interface{}, opt *AddProjectPushRuleOptions, options ...RequestOptionFunc) (*ProjectPushRules, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/push_rule", PathEscape(project)) req, err := s.client.NewRequest(http.MethodPost, u, opt, options) if err != nil { return nil, nil, err } ppr := new(ProjectPushRules) resp, err := s.client.Do(req, ppr) if err != nil { return nil, resp, err } return ppr, resp, nil } // EditProjectPushRuleOptions represents the available EditProjectPushRule() // options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/projects.html#edit-project-push-rule type EditProjectPushRuleOptions struct { AuthorEmailRegex *string `url:"author_email_regex,omitempty" json:"author_email_regex,omitempty"` BranchNameRegex *string `url:"branch_name_regex,omitempty" json:"branch_name_regex,omitempty"` CommitCommitterCheck *bool `url:"commit_committer_check,omitempty" json:"commit_committer_check,omitempty"` CommitCommitterNameCheck *bool `url:"commit_committer_name_check,omitempty" json:"commit_committer_name_check,omitempty"` CommitMessageNegativeRegex *string `url:"commit_message_negative_regex,omitempty" json:"commit_message_negative_regex,omitempty"` CommitMessageRegex *string `url:"commit_message_regex,omitempty" json:"commit_message_regex,omitempty"` DenyDeleteTag *bool `url:"deny_delete_tag,omitempty" json:"deny_delete_tag,omitempty"` FileNameRegex *string `url:"file_name_regex,omitempty" json:"file_name_regex,omitempty"` MaxFileSize *int `url:"max_file_size,omitempty" json:"max_file_size,omitempty"` MemberCheck *bool `url:"member_check,omitempty" json:"member_check,omitempty"` PreventSecrets *bool `url:"prevent_secrets,omitempty" json:"prevent_secrets,omitempty"` RejectUnsignedCommits *bool `url:"reject_unsigned_commits,omitempty" json:"reject_unsigned_commits,omitempty"` RejectNonDCOCommits *bool `url:"reject_non_dco_commits,omitempty" json:"reject_non_dco_commits,omitempty"` } // EditProjectPushRule edits a push rule for a specified project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/projects.html#edit-project-push-rule func (s *ProjectsService) EditProjectPushRule(pid interface{}, opt *EditProjectPushRuleOptions, options ...RequestOptionFunc) (*ProjectPushRules, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/push_rule", PathEscape(project)) req, err := s.client.NewRequest(http.MethodPut, u, opt, options) if err != nil { return nil, nil, err } ppr := new(ProjectPushRules) resp, err := s.client.Do(req, ppr) if err != nil { return nil, resp, err } return ppr, resp, nil } // DeleteProjectPushRule removes a push rule from a project. This is an // idempotent method and can be called multiple times. Either the push rule is // available or not. // // GitLab API docs: // https://docs.gitlab.com/ee/api/projects.html#delete-project-push-rule func (s *ProjectsService) DeleteProjectPushRule(pid interface{}, options ...RequestOptionFunc) (*Response, error) { project, err := parseID(pid) if err != nil { return nil, err } u := fmt.Sprintf("projects/%s/push_rule", PathEscape(project)) req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } // ProjectApprovals represents GitLab project level merge request approvals. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_request_approvals.html#project-level-mr-approvals type ProjectApprovals struct { Approvers []*MergeRequestApproverUser `json:"approvers"` ApproverGroups []*MergeRequestApproverGroup `json:"approver_groups"` ApprovalsBeforeMerge int `json:"approvals_before_merge"` ResetApprovalsOnPush bool `json:"reset_approvals_on_push"` DisableOverridingApproversPerMergeRequest bool `json:"disable_overriding_approvers_per_merge_request"` MergeRequestsAuthorApproval bool `json:"merge_requests_author_approval"` MergeRequestsDisableCommittersApproval bool `json:"merge_requests_disable_committers_approval"` RequirePasswordToApprove bool `json:"require_password_to_approve"` SelectiveCodeOwnerRemovals bool `json:"selective_code_owner_removals,omitempty"` } // GetApprovalConfiguration get the approval configuration for a project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_request_approvals.html#get-configuration func (s *ProjectsService) GetApprovalConfiguration(pid interface{}, options ...RequestOptionFunc) (*ProjectApprovals, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/approvals", PathEscape(project)) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } pa := new(ProjectApprovals) resp, err := s.client.Do(req, pa) if err != nil { return nil, resp, err } return pa, resp, nil } // ChangeApprovalConfigurationOptions represents the available // ApprovalConfiguration() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_request_approvals.html#change-configuration type ChangeApprovalConfigurationOptions struct { ApprovalsBeforeMerge *int `url:"approvals_before_merge,omitempty" json:"approvals_before_merge,omitempty"` DisableOverridingApproversPerMergeRequest *bool `url:"disable_overriding_approvers_per_merge_request,omitempty" json:"disable_overriding_approvers_per_merge_request,omitempty"` MergeRequestsAuthorApproval *bool `url:"merge_requests_author_approval,omitempty" json:"merge_requests_author_approval,omitempty"` MergeRequestsDisableCommittersApproval *bool `url:"merge_requests_disable_committers_approval,omitempty" json:"merge_requests_disable_committers_approval,omitempty"` RequirePasswordToApprove *bool `url:"require_password_to_approve,omitempty" json:"require_password_to_approve,omitempty"` ResetApprovalsOnPush *bool `url:"reset_approvals_on_push,omitempty" json:"reset_approvals_on_push,omitempty"` SelectiveCodeOwnerRemovals *bool `url:"selective_code_owner_removals,omitempty" json:"selective_code_owner_removals,omitempty"` } // ChangeApprovalConfiguration updates the approval configuration for a project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_request_approvals.html#change-configuration func (s *ProjectsService) ChangeApprovalConfiguration(pid interface{}, opt *ChangeApprovalConfigurationOptions, options ...RequestOptionFunc) (*ProjectApprovals, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/approvals", PathEscape(project)) req, err := s.client.NewRequest(http.MethodPost, u, opt, options) if err != nil { return nil, nil, err } pa := new(ProjectApprovals) resp, err := s.client.Do(req, pa) if err != nil { return nil, resp, err } return pa, resp, nil } // GetProjectApprovalRulesListsOptions represents the available // GetProjectApprovalRules() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_request_approvals.html#get-project-level-rules type GetProjectApprovalRulesListsOptions ListOptions // GetProjectApprovalRules looks up the list of project level approver rules. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_request_approvals.html#get-project-level-rules func (s *ProjectsService) GetProjectApprovalRules(pid interface{}, opt *GetProjectApprovalRulesListsOptions, options ...RequestOptionFunc) ([]*ProjectApprovalRule, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/approval_rules", PathEscape(project)) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } var par []*ProjectApprovalRule resp, err := s.client.Do(req, &par) if err != nil { return nil, resp, err } return par, resp, nil } // GetProjectApprovalRule gets the project level approvers. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_request_approvals.html#get-a-single-project-level-rule func (s *ProjectsService) GetProjectApprovalRule(pid interface{}, ruleID int, options ...RequestOptionFunc) (*ProjectApprovalRule, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/approval_rules/%d", PathEscape(project), ruleID) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } par := new(ProjectApprovalRule) resp, err := s.client.Do(req, &par) if err != nil { return nil, resp, err } return par, resp, nil } // CreateProjectLevelRuleOptions represents the available CreateProjectApprovalRule() // options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_request_approvals.html#create-project-level-rule type CreateProjectLevelRuleOptions struct { Name *string `url:"name,omitempty" json:"name,omitempty"` ApprovalsRequired *int `url:"approvals_required,omitempty" json:"approvals_required,omitempty"` ReportType *string `url:"report_type,omitempty" json:"report_type,omitempty"` RuleType *string `url:"rule_type,omitempty" json:"rule_type,omitempty"` UserIDs *[]int `url:"user_ids,omitempty" json:"user_ids,omitempty"` GroupIDs *[]int `url:"group_ids,omitempty" json:"group_ids,omitempty"` ProtectedBranchIDs *[]int `url:"protected_branch_ids,omitempty" json:"protected_branch_ids,omitempty"` AppliesToAllProtectedBranches *bool `url:"applies_to_all_protected_branches,omitempty" json:"applies_to_all_protected_branches,omitempty"` } // CreateProjectApprovalRule creates a new project-level approval rule. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_request_approvals.html#create-project-level-rule func (s *ProjectsService) CreateProjectApprovalRule(pid interface{}, opt *CreateProjectLevelRuleOptions, options ...RequestOptionFunc) (*ProjectApprovalRule, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/approval_rules", PathEscape(project)) req, err := s.client.NewRequest(http.MethodPost, u, opt, options) if err != nil { return nil, nil, err } par := new(ProjectApprovalRule) resp, err := s.client.Do(req, &par) if err != nil { return nil, resp, err } return par, resp, nil } // UpdateProjectLevelRuleOptions represents the available UpdateProjectApprovalRule() // options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_request_approvals.html#update-project-level-rule type UpdateProjectLevelRuleOptions struct { Name *string `url:"name,omitempty" json:"name,omitempty"` ApprovalsRequired *int `url:"approvals_required,omitempty" json:"approvals_required,omitempty"` UserIDs *[]int `url:"user_ids,omitempty" json:"user_ids,omitempty"` GroupIDs *[]int `url:"group_ids,omitempty" json:"group_ids,omitempty"` ProtectedBranchIDs *[]int `url:"protected_branch_ids,omitempty" json:"protected_branch_ids,omitempty"` AppliesToAllProtectedBranches *bool `url:"applies_to_all_protected_branches,omitempty" json:"applies_to_all_protected_branches,omitempty"` } // UpdateProjectApprovalRule updates an existing approval rule with new options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_request_approvals.html#update-project-level-rule func (s *ProjectsService) UpdateProjectApprovalRule(pid interface{}, approvalRule int, opt *UpdateProjectLevelRuleOptions, options ...RequestOptionFunc) (*ProjectApprovalRule, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/approval_rules/%d", PathEscape(project), approvalRule) req, err := s.client.NewRequest(http.MethodPut, u, opt, options) if err != nil { return nil, nil, err } par := new(ProjectApprovalRule) resp, err := s.client.Do(req, &par) if err != nil { return nil, resp, err } return par, resp, nil } // DeleteProjectApprovalRule deletes a project-level approval rule. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_request_approvals.html#delete-project-level-rule func (s *ProjectsService) DeleteProjectApprovalRule(pid interface{}, approvalRule int, options ...RequestOptionFunc) (*Response, error) { project, err := parseID(pid) if err != nil { return nil, err } u := fmt.Sprintf("projects/%s/approval_rules/%d", PathEscape(project), approvalRule) req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } // ChangeAllowedApproversOptions represents the available ChangeAllowedApprovers() // options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_request_approvals.html#change-allowed-approvers type ChangeAllowedApproversOptions struct { ApproverGroupIDs *[]int `url:"approver_group_ids,omitempty" json:"approver_group_ids,omitempty"` ApproverIDs *[]int `url:"approver_ids,omitempty" json:"approver_ids,omitempty"` } // ChangeAllowedApprovers updates the list of approvers and approver groups. // // GitLab API docs: // https://docs.gitlab.com/ee/api/merge_request_approvals.html#change-allowed-approvers func (s *ProjectsService) ChangeAllowedApprovers(pid interface{}, opt *ChangeAllowedApproversOptions, options ...RequestOptionFunc) (*ProjectApprovals, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/approvers", PathEscape(project)) req, err := s.client.NewRequest(http.MethodPut, u, opt, options) if err != nil { return nil, nil, err } pa := new(ProjectApprovals) resp, err := s.client.Do(req, pa) if err != nil { return nil, resp, err } return pa, resp, nil } // ProjectPullMirrorDetails represent the details of the configuration pull // mirror and its update status. // // GitLab API docs: // https://docs.gitlab.com/ee/api/projects.html#get-a-projects-pull-mirror-details type ProjectPullMirrorDetails struct { ID int `json:"id"` LastError string `json:"last_error"` LastSuccessfulUpdateAt *time.Time `json:"last_successful_update_at"` LastUpdateAt *time.Time `json:"last_update_at"` LastUpdateStartedAt *time.Time `json:"last_update_started_at"` UpdateStatus string `json:"update_status"` URL string `json:"url"` } // GetProjectPullMirrorDetails returns the pull mirror details. // // GitLab API docs: // https://docs.gitlab.com/ee/api/projects.html#get-a-projects-pull-mirror-details func (s *ProjectsService) GetProjectPullMirrorDetails(pid interface{}, options ...RequestOptionFunc) (*ProjectPullMirrorDetails, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/mirror/pull", PathEscape(project)) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } pmd := new(ProjectPullMirrorDetails) resp, err := s.client.Do(req, pmd) if err != nil { return nil, resp, err } return pmd, resp, nil } // StartMirroringProject start the pull mirroring process for a project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/projects.html#start-the-pull-mirroring-process-for-a-project func (s *ProjectsService) StartMirroringProject(pid interface{}, options ...RequestOptionFunc) (*Response, error) { project, err := parseID(pid) if err != nil { return nil, err } u := fmt.Sprintf("projects/%s/mirror/pull", PathEscape(project)) req, err := s.client.NewRequest(http.MethodPost, u, nil, options) if err != nil { return nil, err } resp, err := s.client.Do(req, nil) if err != nil { return resp, err } return resp, nil } // TransferProjectOptions represents the available TransferProject() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/projects.html#transfer-a-project-to-a-new-namespace type TransferProjectOptions struct { Namespace interface{} `url:"namespace,omitempty" json:"namespace,omitempty"` } // TransferProject transfer a project into the specified namespace // // GitLab API docs: https://docs.gitlab.com/ee/api/projects.html#transfer-a-project-to-a-new-namespace func (s *ProjectsService) TransferProject(pid interface{}, opt *TransferProjectOptions, options ...RequestOptionFunc) (*Project, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/transfer", PathEscape(project)) req, err := s.client.NewRequest(http.MethodPut, u, opt, options) if err != nil { return nil, nil, err } p := new(Project) resp, err := s.client.Do(req, p) if err != nil { return nil, resp, err } return p, resp, nil } // StartHousekeepingProject start the Housekeeping task for a project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/projects.html#start-the-housekeeping-task-for-a-project func (s *ProjectsService) StartHousekeepingProject(pid interface{}, options ...RequestOptionFunc) (*Response, error) { project, err := parseID(pid) if err != nil { return nil, err } u := fmt.Sprintf("projects/%s/housekeeping", PathEscape(project)) req, err := s.client.NewRequest(http.MethodPost, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } // GetRepositoryStorage Get the path to repository storage. // // GitLab API docs: // https://docs.gitlab.com/ee/api/projects.html#get-the-path-to-repository-storage type ProjectReposityStorage struct { ProjectID int `json:"project_id"` DiskPath string `json:"disk_path"` CreatedAt *time.Time `json:"created_at"` RepositoryStorage string `json:"repository_storage"` } func (s *ProjectsService) GetRepositoryStorage(pid interface{}, options ...RequestOptionFunc) (*ProjectReposityStorage, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/storage", PathEscape(project)) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } prs := new(ProjectReposityStorage) resp, err := s.client.Do(req, prs) if err != nil { return nil, resp, err } return prs, resp, nil } golang-gitlab-gitlab-org-api-client-go-0.123.0/projects_test.go000066400000000000000000001632161475761473200243070ustar00rootroot00000000000000// // Copyright 2021, Sander van Harmelen // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "bytes" "encoding/base64" "encoding/json" "fmt" "io" "net/http" "os" "reflect" "strconv" "strings" "testing" "time" "github.com/stretchr/testify/assert" ) func TestListProjects(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, `[{"id":1},{"id":2}]`) }) opt := &ListProjectsOptions{ ListOptions: ListOptions{Page: 2, PerPage: 3}, Archived: Ptr(true), OrderBy: Ptr("name"), Sort: Ptr("asc"), Search: Ptr("query"), Simple: Ptr(true), Visibility: Ptr(PublicVisibility), } projects, _, err := client.Projects.ListProjects(opt) if err != nil { t.Errorf("Projects.ListProjects returned error: %v", err) } want := []*Project{{ID: 1}, {ID: 2}} if !reflect.DeepEqual(want, projects) { t.Errorf("Projects.ListProjects returned %+v, want %+v", projects, want) } } func TestListUserProjects(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/users/1/projects", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, `[{"id":1},{"id":2}]`) }) opt := &ListProjectsOptions{ ListOptions: ListOptions{Page: 2, PerPage: 3}, Archived: Ptr(true), OrderBy: Ptr("name"), Sort: Ptr("asc"), Search: Ptr("query"), Simple: Ptr(true), Visibility: Ptr(PublicVisibility), } projects, _, err := client.Projects.ListUserProjects(1, opt) if err != nil { t.Errorf("Projects.ListUserProjects returned error: %v", err) } want := []*Project{{ID: 1}, {ID: 2}} if !reflect.DeepEqual(want, projects) { t.Errorf("Projects.ListUserProjects returned %+v, want %+v", projects, want) } } func TestListUserContributedProjects(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/users/1/contributed_projects", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, `[{"id":1},{"id":2}]`) }) opt := &ListProjectsOptions{ ListOptions: ListOptions{Page: 2, PerPage: 3}, Archived: Bool(true), OrderBy: String("name"), Sort: String("asc"), Search: String("query"), Simple: Bool(true), Visibility: Visibility(PublicVisibility), } projects, _, err := client.Projects.ListUserContributedProjects(1, opt) if err != nil { t.Errorf("Projects.ListUserContributedProjects returned error: %v", err) } want := []*Project{{ID: 1}, {ID: 2}} if !reflect.DeepEqual(want, projects) { t.Errorf("Projects.ListUserContributedProjects returned %+v, want %+v", projects, want) } } func TestListUserStarredProjects(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/users/1/starred_projects", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, `[{"id":1},{"id":2}]`) }) opt := &ListProjectsOptions{ ListOptions: ListOptions{Page: 2, PerPage: 3}, Archived: Ptr(true), OrderBy: Ptr("name"), Sort: Ptr("asc"), Search: Ptr("query"), Simple: Ptr(true), Visibility: Ptr(PublicVisibility), } projects, _, err := client.Projects.ListUserStarredProjects(1, opt) if err != nil { t.Errorf("Projects.ListUserStarredProjects returned error: %v", err) } want := []*Project{{ID: 1}, {ID: 2}} if !reflect.DeepEqual(want, projects) { t.Errorf("Projects.ListUserStarredProjects returned %+v, want %+v", projects, want) } } func TestListProjectsUsersByID(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/", func(w http.ResponseWriter, r *http.Request) { testURL(t, r, "/api/v4/projects/1/users?page=2&per_page=3&search=query") testMethod(t, r, http.MethodGet) fmt.Fprint(w, `[{"id":1},{"id":2}]`) }) opt := &ListProjectUserOptions{ ListOptions: ListOptions{Page: 2, PerPage: 3}, Search: Ptr("query"), } projects, _, err := client.Projects.ListProjectsUsers(1, opt) if err != nil { t.Errorf("Projects.ListProjectsUsers returned error: %v", err) } want := []*ProjectUser{{ID: 1}, {ID: 2}} if !reflect.DeepEqual(want, projects) { t.Errorf("Projects.ListProjectsUsers returned %+v, want %+v", projects, want) } } func TestListProjectsUsersByName(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/", func(w http.ResponseWriter, r *http.Request) { testURL(t, r, "/api/v4/projects/namespace%2Fname/users?page=2&per_page=3&search=query") testMethod(t, r, http.MethodGet) fmt.Fprint(w, `[{"id":1},{"id":2}]`) }) opt := &ListProjectUserOptions{ ListOptions: ListOptions{Page: 2, PerPage: 3}, Search: Ptr("query"), } projects, _, err := client.Projects.ListProjectsUsers("namespace/name", opt) if err != nil { t.Errorf("Projects.ListProjectsUsers returned error: %v", err) } want := []*ProjectUser{{ID: 1}, {ID: 2}} if !reflect.DeepEqual(want, projects) { t.Errorf("Projects.ListProjectsUsers returned %+v, want %+v", projects, want) } } func TestListProjectsGroupsByID(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/", func(w http.ResponseWriter, r *http.Request) { testURL(t, r, "/api/v4/projects/1/groups?page=2&per_page=3&search=query") testMethod(t, r, http.MethodGet) fmt.Fprint(w, `[{"id":1},{"id":2}]`) }) opt := &ListProjectGroupOptions{ ListOptions: ListOptions{Page: 2, PerPage: 3}, Search: Ptr("query"), } groups, _, err := client.Projects.ListProjectsGroups(1, opt) if err != nil { t.Errorf("Projects.ListProjectsGroups returned error: %v", err) } want := []*ProjectGroup{{ID: 1}, {ID: 2}} if !reflect.DeepEqual(want, groups) { t.Errorf("Projects.ListProjectsGroups returned %+v, want %+v", groups, want) } } func TestListProjectsGroupsByName(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/", func(w http.ResponseWriter, r *http.Request) { testURL(t, r, "/api/v4/projects/namespace%2Fname/groups?page=2&per_page=3&search=query") testMethod(t, r, http.MethodGet) fmt.Fprint(w, `[{"id":1},{"id":2}]`) }) opt := &ListProjectGroupOptions{ ListOptions: ListOptions{Page: 2, PerPage: 3}, Search: Ptr("query"), } groups, _, err := client.Projects.ListProjectsGroups("namespace/name", opt) if err != nil { t.Errorf("Projects.ListProjectsGroups returned error: %v", err) } want := []*ProjectGroup{{ID: 1}, {ID: 2}} if !reflect.DeepEqual(want, groups) { t.Errorf("Projects.ListProjectsGroups returned %+v, want %+v", groups, want) } } func TestListOwnedProjects(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, `[{"id":1},{"id":2}]`) }) opt := &ListProjectsOptions{ ListOptions: ListOptions{Page: 2, PerPage: 3}, Archived: Ptr(true), OrderBy: Ptr("name"), Sort: Ptr("asc"), Search: Ptr("query"), Simple: Ptr(true), Owned: Ptr(true), Visibility: Ptr(PublicVisibility), } projects, _, err := client.Projects.ListProjects(opt) if err != nil { t.Errorf("Projects.ListOwnedProjects returned error: %v", err) } want := []*Project{{ID: 1}, {ID: 2}} if !reflect.DeepEqual(want, projects) { t.Errorf("Projects.ListOwnedProjects returned %+v, want %+v", projects, want) } } func TestEditProject(t *testing.T) { mux, client := setup(t) var developerRole AccessControlValue = "developer" developerPipelineVariablesRole := CIPipelineVariablesDeveloperRole opt := &EditProjectOptions{ CIRestrictPipelineCancellationRole: Ptr(developerRole), CIPipelineVariablesMinimumOverrideRole: Ptr(developerPipelineVariablesRole), } // Store whether we've seen all the attributes we set attributesFound := false mux.HandleFunc("/api/v4/projects/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPut) // Check that our request properly included ci_restrict_pipeline_cancellation_role body, err := io.ReadAll(r.Body) if err != nil { t.Fatalf("Unable to read body properly. Error: %v", err) } // Set the value to check if our value is included attributesFound = strings.Contains(string(body), "ci_restrict_pipeline_cancellation_role") && strings.Contains(string(body), "ci_pipeline_variables_minimum_override_role") // Print the start of the mock example from https://docs.gitlab.com/ee/api/projects.html#edit-project // including the attribute we edited fmt.Fprint(w, ` { "id": 1, "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", "description_html": "

Lorem ipsum dolor sit amet, consectetur adipiscing elit.

", "default_branch": "main", "visibility": "private", "ssh_url_to_repo": "git@example.com:diaspora/diaspora-project-site.git", "http_url_to_repo": "http://example.com/diaspora/diaspora-project-site.git", "web_url": "http://example.com/diaspora/diaspora-project-site", "readme_url": "http://example.com/diaspora/diaspora-project-site/blob/main/README.md", "ci_restrict_pipeline_cancellation_role": "developer", "ci_pipeline_variables_minimum_override_role": "developer" }`) }) project, resp, err := client.Projects.EditProject(1, opt) assert.NoError(t, err) assert.Equal(t, http.StatusOK, resp.StatusCode) assert.Equal(t, true, attributesFound) assert.Equal(t, developerRole, project.CIRestrictPipelineCancellationRole) assert.Equal(t, developerPipelineVariablesRole, project.CIPipelineVariablesMinimumOverrideRole) } func TestListStarredProjects(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, `[{"id":1},{"id":2}]`) }) opt := &ListProjectsOptions{ ListOptions: ListOptions{Page: 2, PerPage: 3}, Archived: Ptr(true), OrderBy: Ptr("name"), Sort: Ptr("asc"), Search: Ptr("query"), Simple: Ptr(true), Starred: Ptr(true), Visibility: Ptr(PublicVisibility), } projects, _, err := client.Projects.ListProjects(opt) if err != nil { t.Errorf("Projects.ListStarredProjects returned error: %v", err) } want := []*Project{{ID: 1}, {ID: 2}} if !reflect.DeepEqual(want, projects) { t.Errorf("Projects.ListStarredProjects returned %+v, want %+v", projects, want) } } func TestGetProjectByID(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, `{ "id": 1, "container_registry_enabled": true, "container_expiration_policy": { "cadence": "7d", "enabled": false, "keep_n": null, "older_than": null, "name_regex_delete": null, "name_regex_keep": null, "next_run_at": "2020-01-07T21:42:58.658Z" }, "ci_forward_deployment_enabled": true, "ci_forward_deployment_rollback_allowed": true, "ci_restrict_pipeline_cancellation_role": "developer", "ci_pipeline_variables_minimum_override_role": "no_one_allowed", "packages_enabled": false, "build_coverage_regex": "Total.*([0-9]{1,3})%" }`) }) wantTimestamp := time.Date(2020, 0o1, 0o7, 21, 42, 58, 658000000, time.UTC) want := &Project{ ID: 1, ContainerRegistryEnabled: true, ContainerExpirationPolicy: &ContainerExpirationPolicy{ Cadence: "7d", NextRunAt: &wantTimestamp, }, PackagesEnabled: false, BuildCoverageRegex: `Total.*([0-9]{1,3})%`, CIForwardDeploymentEnabled: true, CIForwardDeploymentRollbackAllowed: true, CIRestrictPipelineCancellationRole: "developer", CIPipelineVariablesMinimumOverrideRole: "no_one_allowed", } project, _, err := client.Projects.GetProject(1, nil) if err != nil { t.Fatalf("Projects.GetProject returns an error: %v", err) } if !reflect.DeepEqual(want, project) { t.Errorf("Projects.GetProject returned %+v, want %+v", project, want) } } func TestGetProjectByName(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/", func(w http.ResponseWriter, r *http.Request) { testURL(t, r, "/api/v4/projects/namespace%2Fname") testMethod(t, r, http.MethodGet) fmt.Fprint(w, `{"id":1}`) }) want := &Project{ID: 1} project, _, err := client.Projects.GetProject("namespace/name", nil) if err != nil { t.Fatalf("Projects.GetProject returns an error: %v", err) } if !reflect.DeepEqual(want, project) { t.Errorf("Projects.GetProject returned %+v, want %+v", project, want) } } func TestGetProjectWithOptions(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, `{ "id":1, "statistics": { "commit_count": 37, "storage_size": 1038090, "repository_size": 1038090, "wiki_size": 10, "lfs_objects_size": 0, "job_artifacts_size": 0, "pipeline_artifacts_size": 0, "packages_size": 238906167, "snippets_size": 146800, "uploads_size": 6523619, "container_registry_size": 284453 }}`) }) want := &Project{ID: 1, Statistics: &Statistics{ CommitCount: 37, StorageSize: 1038090, RepositorySize: 1038090, WikiSize: 10, LFSObjectsSize: 0, JobArtifactsSize: 0, PipelineArtifactsSize: 0, PackagesSize: 238906167, SnippetsSize: 146800, UploadsSize: 6523619, ContainerRegistrySize: 284453, }} project, _, err := client.Projects.GetProject(1, &GetProjectOptions{Statistics: Ptr(true)}) if err != nil { t.Fatalf("Projects.GetProject returns an error: %v", err) } if !reflect.DeepEqual(want, project) { t.Errorf("Projects.GetProject returned %+v, want %+v", project, want) } } func TestCreateProject(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) fmt.Fprint(w, `{"id":1}`) }) opt := &CreateProjectOptions{ Name: Ptr("n"), MergeMethod: Ptr(RebaseMerge), } project, _, err := client.Projects.CreateProject(opt) if err != nil { t.Errorf("Projects.CreateProject returned error: %v", err) } want := &Project{ID: 1} if !reflect.DeepEqual(want, project) { t.Errorf("Projects.CreateProject returned %+v, want %+v", project, want) } } func TestUploadFile(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/uploads", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) if !strings.Contains(r.Header.Get("Content-Type"), "multipart/form-data;") { t.Fatalf("Projects.UploadFile request content-type %+v want multipart/form-data;", r.Header.Get("Content-Type")) } if r.ContentLength == -1 { t.Fatalf("Projects.UploadFile request content-length is -1") } fmt.Fprint(w, `{ "alt": "dk", "url": "/uploads/66dbcd21ec5d24ed6ea225176098d52b/dk.md", "markdown": "![dk](/uploads/66dbcd21ec5d24ed6ea225176098d52b/dk.png)" }`) }) want := &ProjectFile{ Alt: "dk", URL: "/uploads/66dbcd21ec5d24ed6ea225176098d52b/dk.md", Markdown: "![dk](/uploads/66dbcd21ec5d24ed6ea225176098d52b/dk.png)", } file := bytes.NewBufferString("dummy") projectFile, _, err := client.Projects.UploadFile(1, file, "test.txt") if err != nil { t.Fatalf("Projects.UploadFile returns an error: %v", err) } if !reflect.DeepEqual(want, projectFile) { t.Errorf("Projects.UploadFile returned %+v, want %+v", projectFile, want) } } func TestUploadFile_Retry(t *testing.T) { mux, client := setup(t) tf, _ := os.CreateTemp(os.TempDir(), "test") defer os.Remove(tf.Name()) isFirstRequest := true mux.HandleFunc("/api/v4/projects/1/uploads", func(w http.ResponseWriter, r *http.Request) { if isFirstRequest { http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) isFirstRequest = false return } if !strings.Contains(r.Header.Get("Content-Type"), "multipart/form-data;") { t.Fatalf("Projects.UploadFile request content-type %+v want multipart/form-data;", r.Header.Get("Content-Type")) } if r.ContentLength == -1 { t.Fatalf("Projects.UploadFile request content-length is -1") } fmt.Fprint(w, `{ "alt": "dk", "url": "/uploads/66dbcd21ec5d24ed6ea225176098d52b/dk.md", "markdown": "![dk](/uploads/66dbcd21ec5d24ed6ea225176098d52b/dk.png)" }`) }) want := &ProjectFile{ Alt: "dk", URL: "/uploads/66dbcd21ec5d24ed6ea225176098d52b/dk.md", Markdown: "![dk](/uploads/66dbcd21ec5d24ed6ea225176098d52b/dk.png)", } file := bytes.NewBufferString("dummy") projectFile, _, err := client.Projects.UploadFile(1, file, "test.txt") if err != nil { t.Fatalf("Projects.UploadFile returns an error: %v", err) } if !reflect.DeepEqual(want, projectFile) { t.Errorf("Projects.UploadFile returned %+v, want %+v", projectFile, want) } } func TestUploadAvatar(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPut) if !strings.Contains(r.Header.Get("Content-Type"), "multipart/form-data;") { t.Fatalf("Projects.UploadAvatar request content-type %+v want multipart/form-data;", r.Header.Get("Content-Type")) } if r.ContentLength == -1 { t.Fatalf("Projects.UploadAvatar request content-length is -1") } fmt.Fprint(w, `{}`) }) avatar := new(bytes.Buffer) _, _, err := client.Projects.UploadAvatar(1, avatar, "avatar.png") if err != nil { t.Fatalf("Projects.UploadAvatar returns an error: %v", err) } } func TestUploadAvatar_Retry(t *testing.T) { mux, client := setup(t) isFirstRequest := true mux.HandleFunc("/api/v4/projects/1", func(w http.ResponseWriter, r *http.Request) { if isFirstRequest { http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) isFirstRequest = false return } if !strings.Contains(r.Header.Get("Content-Type"), "multipart/form-data;") { t.Fatalf("Projects.UploadAvatar request content-type %+v want multipart/form-data;", r.Header.Get("Content-Type")) } if r.ContentLength == -1 { t.Fatalf("Projects.UploadAvatar request content-length is -1") } fmt.Fprint(w, `{}`) }) avatar := new(bytes.Buffer) _, _, err := client.Projects.UploadAvatar(1, avatar, "avatar.png") if err != nil { t.Fatalf("Projects.UploadAvatar returns an error: %v", err) } } func TestDownloadAvatar(t *testing.T) { mux, client := setup(t) ico, _ := base64.StdEncoding.DecodeString("AAABAAEAEBAAAAEAGABoAwAAFgAAACgAAAAQAAAAIAAAAAEAGAAAAAAAAAAAACABAAAgAQAAAAAAAAAAAAD9/f39/f39/f39/f39/f39/f3y9/x+u+9qsO3l8Pr9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3c7Plfq+xFnepFnepSo+vI4ff9/f39/f39/f39/f39/f39/f39/f39/f39/f26z/VLkupFnepFnepFnepFnepFlOmevPL7/P39/f39/f39/f39/f39/f34+vyPsvBAe+hAe+hCh+lFm+pFnepDjOlAe+hAe+h2oO3v8/v9/f39/f39/f3o7/pqmOxAe+hAe+hAe+hAe+hBf+dBgedAe+hAe+hAe+hAe+hYi+rX4/j9/f3u8/tXi+pAe+hAe+hAe+hAe+g/deU7X9w6Xds+ceRAe+hAe+hAe+hAe+hIgenZ5fmVtvFAe+hAe+hAe+hAe+g+b+M6XNs6W9o6W9o6W9o9a+FAe+hAe+hAe+hAe+hyne1hketAe+hAe+hAeug9aOA6W9o6W9o6W9o6W9o6W9o6W9o8ZN5AeedAe+hAe+hDfehajepAe+g/d+Y7Yt06W9o6W9o6W9o6W9o6W9o6W9o6W9o6W9o7X9w/dOVAe+hAe+iAoew8Z986XNo6W9o6W9o6W9o6W9o6W9o6W9o6W9o6W9o6W9o6W9o6W9o8ZN5chufDzfI6W9o6W9o6W9o6W9pTb95Wct9Wct9Wct9Wct9Wct88Xdo6W9o6W9o6W9qfr+z6+vxMat06W9o6W9pKaN37+/z9/f39/f39/f39/f39/f1sheM6W9o6W9o7XNrm6vj9/f2Qo+k6W9o6W9qFmef9/f39/f39/f39/f39/f39/f2puO46W9o6W9psheL9/f39/f3Y3/Y6W9o6W9rDzfL9/f39/f39/f39/f39/f39/f3m6vk7XNo6W9q0wO/9/f39/f39/f1eeeBDY9v3+Pz9/f39/f39/f39/f39/f39/f39/f1ifOFDYtvz9fv9/f39/f39/f2vvO6InOf9/f39/f39/f39/f39/f39/f39/f39/f2quO2NoOj9/f39/f0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA") mux.HandleFunc("/api/v4/projects/1/avatar", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) w.Header().Add("Content-length", strconv.Itoa(len(ico))) w.WriteHeader(http.StatusOK) _, _ = w.Write(ico) }, ) _, resp, err := client.Projects.DownloadAvatar(1) if err != nil { t.Fatalf("Projects.DownloadAvatar returned error: %v", err) } if resp.Status != "200 OK" { t.Fatalf("Projects.DownloadAvatar returned wrong status code: %v", resp.Status) } if int(resp.ContentLength) != len(ico) { t.Fatalf("Projects.DownloadAvatar returned wrong content length: %v", resp.ContentLength) } } func TestListProjectForks(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/", func(w http.ResponseWriter, r *http.Request) { testURL(t, r, "/api/v4/projects/namespace%2Fname/forks?archived=true&order_by=name&page=2&per_page=3&search=query&simple=true&sort=asc&visibility=public") testMethod(t, r, http.MethodGet) fmt.Fprint(w, `[{"id":1},{"id":2}]`) }) opt := &ListProjectsOptions{} opt.ListOptions = ListOptions{Page: 2, PerPage: 3} opt.Archived = Ptr(true) opt.OrderBy = Ptr("name") opt.Sort = Ptr("asc") opt.Search = Ptr("query") opt.Simple = Ptr(true) opt.Visibility = Ptr(PublicVisibility) projects, _, err := client.Projects.ListProjectForks("namespace/name", opt) if err != nil { t.Errorf("Projects.ListProjectForks returned error: %v", err) } want := []*Project{{ID: 1}, {ID: 2}} if !reflect.DeepEqual(want, projects) { t.Errorf("Projects.ListProjects returned %+v, want %+v", projects, want) } } func TestDeleteProject(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodDelete) }) opt := &DeleteProjectOptions{ FullPath: Ptr("group/project"), PermanentlyRemove: Ptr(true), } _, err := client.Projects.DeleteProject(1, opt) if err != nil { t.Errorf("Projects.DeleteProject returned error: %v", err) } } func TestShareProjectWithGroup(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/share", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) }) opt := &ShareWithGroupOptions{ GroupID: Ptr(1), GroupAccess: Ptr(AccessLevelValue(50)), } _, err := client.Projects.ShareProjectWithGroup(1, opt) if err != nil { t.Errorf("Projects.ShareProjectWithGroup returned error: %v", err) } } func TestDeleteSharedProjectFromGroup(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/share/2", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodDelete) }) _, err := client.Projects.DeleteSharedProjectFromGroup(1, 2) if err != nil { t.Errorf("Projects.DeleteSharedProjectFromGroup returned error: %v", err) } } func TestGetApprovalConfiguration(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/approvals", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, `{ "approvers": [], "approver_groups": [], "approvals_before_merge": 3, "reset_approvals_on_push": false, "disable_overriding_approvers_per_merge_request": false, "merge_requests_author_approval": true, "merge_requests_disable_committers_approval": true, "require_password_to_approve": true }`) }) approvals, _, err := client.Projects.GetApprovalConfiguration(1) if err != nil { t.Errorf("Projects.GetApprovalConfiguration returned error: %v", err) } want := &ProjectApprovals{ Approvers: []*MergeRequestApproverUser{}, ApproverGroups: []*MergeRequestApproverGroup{}, ApprovalsBeforeMerge: 3, ResetApprovalsOnPush: false, DisableOverridingApproversPerMergeRequest: false, MergeRequestsAuthorApproval: true, MergeRequestsDisableCommittersApproval: true, RequirePasswordToApprove: true, } if !reflect.DeepEqual(want, approvals) { t.Errorf("Projects.GetApprovalConfiguration returned %+v, want %+v", approvals, want) } } func TestChangeApprovalConfiguration(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/approvals", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) testBody(t, r, `{"approvals_before_merge":3}`) fmt.Fprint(w, `{ "approvers": [], "approver_groups": [], "approvals_before_merge": 3, "reset_approvals_on_push": false, "disable_overriding_approvers_per_merge_request": false, "merge_requests_author_approval": true, "merge_requests_disable_committers_approval": true, "require_password_to_approve": true }`) }) opt := &ChangeApprovalConfigurationOptions{ ApprovalsBeforeMerge: Ptr(3), } approvals, _, err := client.Projects.ChangeApprovalConfiguration(1, opt) if err != nil { t.Errorf("Projects.ChangeApprovalConfigurationOptions returned error: %v", err) } want := &ProjectApprovals{ Approvers: []*MergeRequestApproverUser{}, ApproverGroups: []*MergeRequestApproverGroup{}, ApprovalsBeforeMerge: 3, ResetApprovalsOnPush: false, DisableOverridingApproversPerMergeRequest: false, MergeRequestsAuthorApproval: true, MergeRequestsDisableCommittersApproval: true, RequirePasswordToApprove: true, } if !reflect.DeepEqual(want, approvals) { t.Errorf("Projects.ChangeApprovalConfigurationOptions returned %+v, want %+v", approvals, want) } } func TestChangeAllowedApprovers(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/approvers", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPut) testBody(t, r, `{"approver_group_ids":[1],"approver_ids":[2]}`) fmt.Fprint(w, `{ "approver_groups": [{"group":{"id":1}}], "approvers": [{"user":{"id":2}}] }`) }) opt := &ChangeAllowedApproversOptions{ ApproverGroupIDs: &[]int{1}, ApproverIDs: &[]int{2}, } approvals, _, err := client.Projects.ChangeAllowedApprovers(1, opt) if err != nil { t.Errorf("Projects.ChangeApproversConfigurationOptions returned error: %v", err) } want := &ProjectApprovals{ ApproverGroups: []*MergeRequestApproverGroup{ { Group: struct { ID int `json:"id"` Name string `json:"name"` Path string `json:"path"` Description string `json:"description"` Visibility string `json:"visibility"` AvatarURL string `json:"avatar_url"` WebURL string `json:"web_url"` FullName string `json:"full_name"` FullPath string `json:"full_path"` LFSEnabled bool `json:"lfs_enabled"` RequestAccessEnabled bool `json:"request_access_enabled"` }{ ID: 1, }, }, }, Approvers: []*MergeRequestApproverUser{ { User: &BasicUser{ ID: 2, }, }, }, } if !reflect.DeepEqual(want, approvals) { t.Errorf("Projects.ChangeAllowedApprovers returned %+v, want %+v", approvals, want) } } func TestForkProject(t *testing.T) { mux, client := setup(t) namespaceID := 42 name := "myreponame" path := "myrepopath" mux.HandleFunc("/api/v4/projects/1/fork", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) testBody(t, r, fmt.Sprintf(`{"name":"%s","namespace_id":%d,"path":"%s"}`, name, namespaceID, path)) fmt.Fprint(w, `{"id":2}`) }) project, _, err := client.Projects.ForkProject(1, &ForkProjectOptions{ NamespaceID: Ptr(namespaceID), Name: Ptr(name), Path: Ptr(path), }) if err != nil { t.Errorf("Projects.ForkProject returned error: %v", err) } want := &Project{ID: 2} if !reflect.DeepEqual(want, project) { t.Errorf("Projects.ForProject returned %+v, want %+v", project, want) } } func TestGetProjectApprovalRules(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/approval_rules", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, `[ { "id": 1, "name": "security", "rule_type": "regular", "eligible_approvers": [ { "id": 5, "name": "John Doe", "username": "jdoe", "state": "active", "avatar_url": "https://www.gravatar.com/avatar/0?s=80&d=identicon", "web_url": "http://localhost/jdoe" }, { "id": 50, "name": "Group Member 1", "username": "group_member_1", "state": "active", "avatar_url": "https://www.gravatar.com/avatar/0?s=80&d=identicon", "web_url": "http://localhost/group_member_1" } ], "approvals_required": 3, "users": [ { "id": 5, "name": "John Doe", "username": "jdoe", "state": "active", "avatar_url": "https://www.gravatar.com/avatar/0?s=80&d=identicon", "web_url": "http://localhost/jdoe" } ], "groups": [ { "id": 5, "name": "group1", "path": "group1", "description": "", "visibility": "public", "lfs_enabled": false, "avatar_url": null, "web_url": "http://localhost/groups/group1", "request_access_enabled": false, "full_name": "group1", "full_path": "group1", "parent_id": null, "ldap_cn": null, "ldap_access": null } ], "protected_branches": [ { "id": 1, "name": "master", "push_access_levels": [ { "access_level": 30, "access_level_description": "Developers + Maintainers" } ], "merge_access_levels": [ { "access_level": 30, "access_level_description": "Developers + Maintainers" } ], "unprotect_access_levels": [ { "access_level": 40, "access_level_description": "Maintainers" } ], "code_owner_approval_required": false } ], "contains_hidden_groups": false } ]`) }) approvals, _, err := client.Projects.GetProjectApprovalRules(1, nil) if err != nil { t.Errorf("Projects.GetProjectApprovalRules returned error: %v", err) } want := []*ProjectApprovalRule{ { ID: 1, Name: "security", RuleType: "regular", EligibleApprovers: []*BasicUser{ { ID: 5, Name: "John Doe", Username: "jdoe", State: "active", AvatarURL: "https://www.gravatar.com/avatar/0?s=80&d=identicon", WebURL: "http://localhost/jdoe", }, { ID: 50, Name: "Group Member 1", Username: "group_member_1", State: "active", AvatarURL: "https://www.gravatar.com/avatar/0?s=80&d=identicon", WebURL: "http://localhost/group_member_1", }, }, ApprovalsRequired: 3, Users: []*BasicUser{ { ID: 5, Name: "John Doe", Username: "jdoe", State: "active", AvatarURL: "https://www.gravatar.com/avatar/0?s=80&d=identicon", WebURL: "http://localhost/jdoe", }, }, Groups: []*Group{ { ID: 5, Name: "group1", Path: "group1", Description: "", Visibility: PublicVisibility, LFSEnabled: false, AvatarURL: "", WebURL: "http://localhost/groups/group1", RequestAccessEnabled: false, FullName: "group1", FullPath: "group1", }, }, ProtectedBranches: []*ProtectedBranch{ { ID: 1, Name: "master", PushAccessLevels: []*BranchAccessDescription{ { AccessLevel: 30, AccessLevelDescription: "Developers + Maintainers", }, }, MergeAccessLevels: []*BranchAccessDescription{ { AccessLevel: 30, AccessLevelDescription: "Developers + Maintainers", }, }, UnprotectAccessLevels: []*BranchAccessDescription{ { AccessLevel: 40, AccessLevelDescription: "Maintainers", }, }, AllowForcePush: false, CodeOwnerApprovalRequired: false, }, }, }, } if !reflect.DeepEqual(want, approvals) { t.Errorf("Projects.GetProjectApprovalRules returned %+v, want %+v", approvals, want) } } func TestGetProjectApprovalRule(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/approval_rules/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, `{ "id": 1, "name": "security", "rule_type": "regular", "eligible_approvers": [ { "id": 5, "name": "John Doe", "username": "jdoe", "state": "active", "avatar_url": "https://www.gravatar.com/avatar/0?s=80&d=identicon", "web_url": "http://localhost/jdoe" }, { "id": 50, "name": "Group Member 1", "username": "group_member_1", "state": "active", "avatar_url": "https://www.gravatar.com/avatar/0?s=80&d=identicon", "web_url": "http://localhost/group_member_1" } ], "approvals_required": 3, "users": [ { "id": 5, "name": "John Doe", "username": "jdoe", "state": "active", "avatar_url": "https://www.gravatar.com/avatar/0?s=80&d=identicon", "web_url": "http://localhost/jdoe" } ], "groups": [ { "id": 5, "name": "group1", "path": "group1", "description": "", "visibility": "public", "lfs_enabled": false, "avatar_url": null, "web_url": "http://localhost/groups/group1", "request_access_enabled": false, "full_name": "group1", "full_path": "group1", "parent_id": null, "ldap_cn": null, "ldap_access": null } ], "protected_branches": [ { "id": 1, "name": "master", "push_access_levels": [ { "access_level": 30, "access_level_description": "Developers + Maintainers" } ], "merge_access_levels": [ { "access_level": 30, "access_level_description": "Developers + Maintainers" } ], "unprotect_access_levels": [ { "access_level": 40, "access_level_description": "Maintainers" } ], "code_owner_approval_required": false } ], "contains_hidden_groups": false }`) }) approvals, _, err := client.Projects.GetProjectApprovalRule(1, 1) if err != nil { t.Errorf("Projects.GetProjectApprovalRule returned error: %v", err) } want := &ProjectApprovalRule{ ID: 1, Name: "security", RuleType: "regular", EligibleApprovers: []*BasicUser{ { ID: 5, Name: "John Doe", Username: "jdoe", State: "active", AvatarURL: "https://www.gravatar.com/avatar/0?s=80&d=identicon", WebURL: "http://localhost/jdoe", }, { ID: 50, Name: "Group Member 1", Username: "group_member_1", State: "active", AvatarURL: "https://www.gravatar.com/avatar/0?s=80&d=identicon", WebURL: "http://localhost/group_member_1", }, }, ApprovalsRequired: 3, Users: []*BasicUser{ { ID: 5, Name: "John Doe", Username: "jdoe", State: "active", AvatarURL: "https://www.gravatar.com/avatar/0?s=80&d=identicon", WebURL: "http://localhost/jdoe", }, }, Groups: []*Group{ { ID: 5, Name: "group1", Path: "group1", Description: "", Visibility: PublicVisibility, LFSEnabled: false, AvatarURL: "", WebURL: "http://localhost/groups/group1", RequestAccessEnabled: false, FullName: "group1", FullPath: "group1", }, }, ProtectedBranches: []*ProtectedBranch{ { ID: 1, Name: "master", PushAccessLevels: []*BranchAccessDescription{ { AccessLevel: 30, AccessLevelDescription: "Developers + Maintainers", }, }, MergeAccessLevels: []*BranchAccessDescription{ { AccessLevel: 30, AccessLevelDescription: "Developers + Maintainers", }, }, UnprotectAccessLevels: []*BranchAccessDescription{ { AccessLevel: 40, AccessLevelDescription: "Maintainers", }, }, AllowForcePush: false, CodeOwnerApprovalRequired: false, }, }, } if !reflect.DeepEqual(want, approvals) { t.Errorf("Projects.GetProjectApprovalRule returned %+v, want %+v", approvals, want) } } func TestCreateProjectApprovalRule(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/approval_rules", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) fmt.Fprint(w, `{ "id": 1, "name": "security", "rule_type": "regular", "eligible_approvers": [ { "id": 5, "name": "John Doe", "username": "jdoe", "state": "active", "avatar_url": "https://www.gravatar.com/avatar/0?s=80&d=identicon", "web_url": "http://localhost/jdoe" }, { "id": 50, "name": "Group Member 1", "username": "group_member_1", "state": "active", "avatar_url": "https://www.gravatar.com/avatar/0?s=80&d=identicon", "web_url": "http://localhost/group_member_1" } ], "approvals_required": 3, "users": [ { "id": 5, "name": "John Doe", "username": "jdoe", "state": "active", "avatar_url": "https://www.gravatar.com/avatar/0?s=80&d=identicon", "web_url": "http://localhost/jdoe" } ], "groups": [ { "id": 5, "name": "group1", "path": "group1", "description": "", "visibility": "public", "lfs_enabled": false, "avatar_url": null, "web_url": "http://localhost/groups/group1", "request_access_enabled": false, "full_name": "group1", "full_path": "group1", "parent_id": null, "ldap_cn": null, "ldap_access": null } ], "protected_branches": [ { "id": 1, "name": "master", "push_access_levels": [ { "access_level": 30, "access_level_description": "Developers + Maintainers" } ], "merge_access_levels": [ { "access_level": 30, "access_level_description": "Developers + Maintainers" } ], "unprotect_access_levels": [ { "access_level": 40, "access_level_description": "Maintainers" } ], "code_owner_approval_required": false } ], "contains_hidden_groups": false }`) }) opt := &CreateProjectLevelRuleOptions{ Name: Ptr("security"), ApprovalsRequired: Ptr(3), UserIDs: &[]int{5, 50}, GroupIDs: &[]int{5}, ReportType: String("code_coverage"), } rule, _, err := client.Projects.CreateProjectApprovalRule(1, opt) if err != nil { t.Errorf("Projects.CreateProjectApprovalRule returned error: %v", err) } want := &ProjectApprovalRule{ ID: 1, Name: "security", RuleType: "regular", EligibleApprovers: []*BasicUser{ { ID: 5, Name: "John Doe", Username: "jdoe", State: "active", AvatarURL: "https://www.gravatar.com/avatar/0?s=80&d=identicon", WebURL: "http://localhost/jdoe", }, { ID: 50, Name: "Group Member 1", Username: "group_member_1", State: "active", AvatarURL: "https://www.gravatar.com/avatar/0?s=80&d=identicon", WebURL: "http://localhost/group_member_1", }, }, ApprovalsRequired: 3, Users: []*BasicUser{ { ID: 5, Name: "John Doe", Username: "jdoe", State: "active", AvatarURL: "https://www.gravatar.com/avatar/0?s=80&d=identicon", WebURL: "http://localhost/jdoe", }, }, Groups: []*Group{ { ID: 5, Name: "group1", Path: "group1", Description: "", Visibility: PublicVisibility, LFSEnabled: false, AvatarURL: "", WebURL: "http://localhost/groups/group1", RequestAccessEnabled: false, FullName: "group1", FullPath: "group1", }, }, ProtectedBranches: []*ProtectedBranch{ { ID: 1, Name: "master", PushAccessLevels: []*BranchAccessDescription{ { AccessLevel: 30, AccessLevelDescription: "Developers + Maintainers", }, }, MergeAccessLevels: []*BranchAccessDescription{ { AccessLevel: 30, AccessLevelDescription: "Developers + Maintainers", }, }, UnprotectAccessLevels: []*BranchAccessDescription{ { AccessLevel: 40, AccessLevelDescription: "Maintainers", }, }, AllowForcePush: false, CodeOwnerApprovalRequired: false, }, }, } if !reflect.DeepEqual(want, rule) { t.Errorf("Projects.CreateProjectApprovalRule returned %+v, want %+v", rule, want) } } func TestGetProjectPullMirrorDetails(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/mirror/pull", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, `{ "id": 101486, "last_error": null, "last_successful_update_at": "2020-01-06T17:32:02.823Z", "last_update_at": "2020-01-06T17:32:02.823Z", "last_update_started_at": "2020-01-06T17:31:55.864Z", "update_status": "finished", "url": "https://*****:*****@gitlab.com/gitlab-org/security/gitlab.git" }`) }) pullMirror, _, err := client.Projects.GetProjectPullMirrorDetails(1) if err != nil { t.Errorf("Projects.GetProjectPullMirrorDetails returned error: %v", err) } wantLastSuccessfulUpdateAtTimestamp := time.Date(2020, 0o1, 0o6, 17, 32, 0o2, 823000000, time.UTC) wantLastUpdateAtTimestamp := time.Date(2020, 0o1, 0o6, 17, 32, 0o2, 823000000, time.UTC) wantLastUpdateStartedAtTimestamp := time.Date(2020, 0o1, 0o6, 17, 31, 55, 864000000, time.UTC) want := &ProjectPullMirrorDetails{ ID: 101486, LastError: "", LastSuccessfulUpdateAt: &wantLastSuccessfulUpdateAtTimestamp, LastUpdateAt: &wantLastUpdateAtTimestamp, LastUpdateStartedAt: &wantLastUpdateStartedAtTimestamp, UpdateStatus: "finished", URL: "https://*****:*****@gitlab.com/gitlab-org/security/gitlab.git", } if !reflect.DeepEqual(want, pullMirror) { t.Errorf("Projects.GetProjectPullMirrorDetails returned %+v, want %+v", pullMirror, want) } } func TestCreateProjectApprovalRuleEligibleApprovers(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/approval_rules", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) fmt.Fprint(w, `{ "id": 1, "name": "Any name", "rule_type": "any_approver", "eligible_approvers": [], "approvals_required": 1, "users": [], "groups": [], "contains_hidden_groups": false, "protected_branches": [] }`) }) opt := &CreateProjectLevelRuleOptions{ Name: Ptr("Any name"), ApprovalsRequired: Ptr(1), } rule, _, err := client.Projects.CreateProjectApprovalRule(1, opt) if err != nil { t.Errorf("Projects.CreateProjectApprovalRule returned error: %v", err) } want := &ProjectApprovalRule{ ID: 1, Name: "Any name", RuleType: "any_approver", EligibleApprovers: []*BasicUser{}, ApprovalsRequired: 1, Users: []*BasicUser{}, Groups: []*Group{}, ProtectedBranches: []*ProtectedBranch{}, } if !reflect.DeepEqual(want, rule) { t.Errorf("Projects.CreateProjectApprovalRule returned %+v, want %+v", rule, want) } } func TestProjectModelsOptionalMergeAttribute(t *testing.T) { // Create a `CreateProjectOptions` struct, ensure that merge attribute doesn't serialize jsonString, err := json.Marshal(&CreateProjectOptions{ Name: Ptr("testProject"), }) if err != nil { t.Fatal("Failed to marshal object", err) } assert.False(t, strings.Contains(string(jsonString), "only_allow_merge_if_all_status_checks_passed")) // Test the same thing but for `EditProjectOptions` struct jsonString, err = json.Marshal(&EditProjectOptions{ Name: Ptr("testProject"), }) if err != nil { t.Fatal("Failed to marshal object", err) } assert.False(t, strings.Contains(string(jsonString), "only_allow_merge_if_all_status_checks_passed")) } func TestListProjectHooks(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/hooks", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, `[ { "id": 1, "url": "http://example.com/hook", "name": "This is the name of an example hook", "description": "This is the description of an example hook", "confidential_note_events": true, "project_id": 1, "push_events": true, "push_events_branch_filter": "main", "issues_events": true, "confidential_issues_events": true, "merge_requests_events": true, "tag_push_events": true, "note_events": true, "job_events": true, "pipeline_events": true, "wiki_page_events": true, "deployment_events": true, "releases_events": true, "enable_ssl_verification": true, "alert_status": "executable", "created_at": "2024-10-13T13:37:00Z", "resource_access_token_events": true, "custom_webhook_template": "my custom template", "custom_headers": [ {"key": "Authorization"}, {"key": "OtherHeader"} ] } ]`) }) hooks, _, err := client.Projects.ListProjectHooks(1, nil) if err != nil { t.Errorf("Projects.ListProjectHooks returned error: %v", err) } createdAt := time.Date(2024, 10, 13, 13, 37, 0, 0, time.UTC) want := []*ProjectHook{{ ID: 1, URL: "http://example.com/hook", Name: "This is the name of an example hook", Description: "This is the description of an example hook", ConfidentialNoteEvents: true, ProjectID: 1, PushEvents: true, PushEventsBranchFilter: "main", IssuesEvents: true, ConfidentialIssuesEvents: true, MergeRequestsEvents: true, TagPushEvents: true, NoteEvents: true, JobEvents: true, PipelineEvents: true, WikiPageEvents: true, DeploymentEvents: true, ReleasesEvents: true, EnableSSLVerification: true, CreatedAt: &createdAt, AlertStatus: "executable", ResourceAccessTokenEvents: true, CustomWebhookTemplate: "my custom template", CustomHeaders: []*HookCustomHeader{ { Key: "Authorization", }, { Key: "OtherHeader", }, }, }} if !reflect.DeepEqual(hooks, want) { t.Errorf("Projects.ListProjectHooks returned \ngot:\n%v\nwant:\n%v", Stringify(hooks), Stringify(want)) } } // Test that the "CustomWebhookTemplate" serializes properly func TestProjectAddWebhook_CustomTemplateStuff(t *testing.T) { mux, client := setup(t) customWebhookSet := false authValueSet := false mux.HandleFunc("/api/v4/projects/1/hooks", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) w.WriteHeader(http.StatusCreated) body, err := io.ReadAll(r.Body) if err != nil { t.Fatalf("Unable to read body properly. Error: %v", err) } customWebhookSet = strings.Contains(string(body), "custom_webhook_template") authValueSet = strings.Contains(string(body), `"value":"stuff"`) fmt.Fprint(w, `{ "custom_webhook_template": "testValue", "custom_headers": [ { "key": "Authorization" }, { "key": "Favorite-Pet" } ] }`) }, ) hook, resp, err := client.Projects.AddProjectHook(1, &AddProjectHookOptions{ CustomWebhookTemplate: Ptr(`{"example":"{{object_kind}}"}`), CustomHeaders: &[]*HookCustomHeader{ { Key: "Authorization", Value: "stuff", }, { Key: "Favorite-Pet", Value: "Cats", }, }, }) assert.NoError(t, err) assert.Equal(t, http.StatusCreated, resp.StatusCode) assert.Equal(t, true, customWebhookSet) assert.Equal(t, true, authValueSet) assert.Equal(t, "testValue", hook.CustomWebhookTemplate) assert.Equal(t, 2, len(hook.CustomHeaders)) } // Test that the "CustomWebhookTemplate" serializes properly when editing func TestProjectEditWebhook_CustomTemplateStuff(t *testing.T) { mux, client := setup(t) customWebhookSet := false authValueSet := false mux.HandleFunc("/api/v4/projects/1/hooks/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPut) w.WriteHeader(http.StatusOK) body, err := io.ReadAll(r.Body) if err != nil { t.Fatalf("Unable to read body properly. Error: %v", err) } customWebhookSet = strings.Contains(string(body), "custom_webhook_template") authValueSet = strings.Contains(string(body), `"value":"stuff"`) fmt.Fprint(w, `{ "custom_webhook_template": "testValue", "custom_headers": [ { "key": "Authorization" }, { "key": "Favorite-Pet" } ]}`) }, ) hook, resp, err := client.Projects.EditProjectHook(1, 1, &EditProjectHookOptions{ CustomWebhookTemplate: Ptr(`{"example":"{{object_kind}}"}`), CustomHeaders: &[]*HookCustomHeader{ { Key: "Authorization", Value: "stuff", }, { Key: "Favorite-Pet", Value: "Cats", }, }, }) assert.NoError(t, err) assert.Equal(t, http.StatusOK, resp.StatusCode) assert.Equal(t, true, customWebhookSet) assert.Equal(t, true, authValueSet) assert.Equal(t, "testValue", hook.CustomWebhookTemplate) assert.Equal(t, 2, len(hook.CustomHeaders)) } func TestGetProjectPushRules(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/push_rule", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, `{ "id": 1, "commit_message_regex": "Fixes \\d+\\..*", "commit_message_negative_regex": "ssh\\:\\/\\/", "branch_name_regex": "(feat|fix)\\/*", "deny_delete_tag": false, "member_check": false, "prevent_secrets": false, "author_email_regex": "@company.com$", "file_name_regex": "(jar|exe)$", "max_file_size": 5, "commit_committer_check": false, "commit_committer_name_check": false, "reject_unsigned_commits": false, "reject_non_dco_commits": false }`) }) rule, _, err := client.Projects.GetProjectPushRules(1) if err != nil { t.Errorf("Projects.GetProjectPushRules returned error: %v", err) } want := &ProjectPushRules{ ID: 1, CommitMessageRegex: "Fixes \\d+\\..*", CommitMessageNegativeRegex: "ssh\\:\\/\\/", BranchNameRegex: "(feat|fix)\\/*", DenyDeleteTag: false, MemberCheck: false, PreventSecrets: false, AuthorEmailRegex: "@company.com$", FileNameRegex: "(jar|exe)$", MaxFileSize: 5, CommitCommitterCheck: false, CommitCommitterNameCheck: false, RejectUnsignedCommits: false, RejectNonDCOCommits: false, } if !reflect.DeepEqual(want, rule) { t.Errorf("Projects.GetProjectPushRules returned %+v, want %+v", rule, want) } } func TestAddProjectPushRules(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/push_rule", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) fmt.Fprint(w, `{ "id": 1, "commit_message_regex": "Fixes \\d+\\..*", "commit_message_negative_regex": "ssh\\:\\/\\/", "branch_name_regex": "(feat|fix)\\/*", "deny_delete_tag": false, "member_check": false, "prevent_secrets": false, "author_email_regex": "@company.com$", "file_name_regex": "(jar|exe)$", "max_file_size": 5, "commit_committer_check": false, "commit_committer_name_check": false, "reject_unsigned_commits": false, "reject_non_dco_commits": false }`) }) opt := &AddProjectPushRuleOptions{ CommitMessageRegex: Ptr("Fixes \\d+\\..*"), CommitMessageNegativeRegex: Ptr("ssh\\:\\/\\/"), BranchNameRegex: Ptr("(feat|fix)\\/*"), DenyDeleteTag: Ptr(false), MemberCheck: Ptr(false), PreventSecrets: Ptr(false), AuthorEmailRegex: Ptr("@company.com$"), FileNameRegex: Ptr("(jar|exe)$"), MaxFileSize: Ptr(5), CommitCommitterCheck: Ptr(false), CommitCommitterNameCheck: Ptr(false), RejectUnsignedCommits: Ptr(false), RejectNonDCOCommits: Ptr(false), } rule, _, err := client.Projects.AddProjectPushRule(1, opt) if err != nil { t.Errorf("Projects.AddProjectPushRule returned error: %v", err) } want := &ProjectPushRules{ ID: 1, CommitMessageRegex: "Fixes \\d+\\..*", CommitMessageNegativeRegex: "ssh\\:\\/\\/", BranchNameRegex: "(feat|fix)\\/*", DenyDeleteTag: false, MemberCheck: false, PreventSecrets: false, AuthorEmailRegex: "@company.com$", FileNameRegex: "(jar|exe)$", MaxFileSize: 5, CommitCommitterCheck: false, CommitCommitterNameCheck: false, RejectUnsignedCommits: false, RejectNonDCOCommits: false, } if !reflect.DeepEqual(want, rule) { t.Errorf("Projects.AddProjectPushRule returned %+v, want %+v", rule, want) } } func TestEditProjectPushRules(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/push_rule", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPut) fmt.Fprint(w, `{ "id": 1, "commit_message_regex": "Fixes \\d+\\..*", "commit_message_negative_regex": "ssh\\:\\/\\/", "branch_name_regex": "(feat|fix)\\/*", "deny_delete_tag": false, "member_check": false, "prevent_secrets": false, "author_email_regex": "@company.com$", "file_name_regex": "(jar|exe)$", "max_file_size": 5, "commit_committer_check": false, "commit_committer_name_check": false, "reject_unsigned_commits": false, "reject_non_dco_commits": false }`) }) opt := &EditProjectPushRuleOptions{ CommitMessageRegex: Ptr("Fixes \\d+\\..*"), CommitMessageNegativeRegex: Ptr("ssh\\:\\/\\/"), BranchNameRegex: Ptr("(feat|fix)\\/*"), DenyDeleteTag: Ptr(false), MemberCheck: Ptr(false), PreventSecrets: Ptr(false), AuthorEmailRegex: Ptr("@company.com$"), FileNameRegex: Ptr("(jar|exe)$"), MaxFileSize: Ptr(5), CommitCommitterCheck: Ptr(false), CommitCommitterNameCheck: Ptr(false), RejectUnsignedCommits: Ptr(false), RejectNonDCOCommits: Ptr(false), } rule, _, err := client.Projects.EditProjectPushRule(1, opt) if err != nil { t.Errorf("Projects.EditProjectPushRule returned error: %v", err) } want := &ProjectPushRules{ ID: 1, CommitMessageRegex: "Fixes \\d+\\..*", CommitMessageNegativeRegex: "ssh\\:\\/\\/", BranchNameRegex: "(feat|fix)\\/*", DenyDeleteTag: false, MemberCheck: false, PreventSecrets: false, AuthorEmailRegex: "@company.com$", FileNameRegex: "(jar|exe)$", MaxFileSize: 5, CommitCommitterCheck: false, CommitCommitterNameCheck: false, RejectUnsignedCommits: false, RejectNonDCOCommits: false, } if !reflect.DeepEqual(want, rule) { t.Errorf("Projects.EditProjectPushRule returned %+v, want %+v", rule, want) } } func TestGetProjectWebhookHeader(t *testing.T) { mux, client := setup(t) // Removed most of the arguments to keep test slim mux.HandleFunc("/api/v4/projects/1/hooks/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, `{ "id": 1, "custom_webhook_template": "{\"event\":\"{{object_kind}}\"}", "custom_headers": [ { "key": "Authorization" }, { "key": "OtherKey" } ] }`) }) hook, _, err := client.Projects.GetProjectHook(1, 1) if err != nil { t.Errorf("Projects.GetProjectHook returned error: %v", err) } want := &ProjectHook{ ID: 1, CustomWebhookTemplate: "{\"event\":\"{{object_kind}}\"}", CustomHeaders: []*HookCustomHeader{ { Key: "Authorization", }, { Key: "OtherKey", }, }, } if !reflect.DeepEqual(want, hook) { t.Errorf("Projects.GetProjectHook returned %+v, want %+v", hook, want) } } func TestSetProjectWebhookHeader(t *testing.T) { mux, client := setup(t) var bodyJson map[string]interface{} // Removed most of the arguments to keep test slim mux.HandleFunc("/api/v4/projects/1/hooks/1/custom_headers/Authorization", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPut) w.WriteHeader(http.StatusNoContent) // validate that the `value` body is sent properly body, err := io.ReadAll(r.Body) if err != nil { t.Fatalf("Unable to read body properly. Error: %v", err) } // Unmarshal the body into JSON so we can check it _ = json.Unmarshal(body, &bodyJson) fmt.Fprint(w, ``) }) req, err := client.Projects.SetProjectCustomHeader(1, 1, "Authorization", &SetHookCustomHeaderOptions{Value: Ptr("testValue")}) if err != nil { t.Errorf("Projects.SetProjectCustomHeader returned error: %v", err) } assert.Equal(t, bodyJson["value"], "testValue") assert.Equal(t, http.StatusNoContent, req.StatusCode) } func TestDeleteProjectWebhookHeader(t *testing.T) { mux, client := setup(t) // Removed most of the arguments to keep test slim mux.HandleFunc("/api/v4/projects/1/hooks/1/custom_headers/Authorization", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodDelete) w.WriteHeader(http.StatusNoContent) fmt.Fprint(w, ``) }) req, err := client.Projects.DeleteProjectCustomHeader(1, 1, "Authorization") if err != nil { t.Errorf("Projects.DeleteProjectCustomHeader returned error: %v", err) } assert.Equal(t, http.StatusNoContent, req.StatusCode) } golang-gitlab-gitlab-org-api-client-go-0.123.0/protected_branches.go000066400000000000000000000244461475761473200252560ustar00rootroot00000000000000// // Copyright 2021, Sander van Harmelen, Michael Lihs // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "fmt" "net/http" "net/url" ) // ProtectedBranchesService handles communication with the protected branch // related methods of the GitLab API. // // GitLab API docs: // https://docs.gitlab.com/ee/api/protected_branches.html type ProtectedBranchesService struct { client *Client } // ProtectedBranch represents a protected branch. // // GitLab API docs: // https://docs.gitlab.com/ee/api/protected_branches.html#list-protected-branches type ProtectedBranch struct { ID int `json:"id"` Name string `json:"name"` PushAccessLevels []*BranchAccessDescription `json:"push_access_levels"` MergeAccessLevels []*BranchAccessDescription `json:"merge_access_levels"` UnprotectAccessLevels []*BranchAccessDescription `json:"unprotect_access_levels"` AllowForcePush bool `json:"allow_force_push"` CodeOwnerApprovalRequired bool `json:"code_owner_approval_required"` } // BranchAccessDescription represents the access description for a protected // branch. // // GitLab API docs: // https://docs.gitlab.com/ee/api/protected_branches.html#list-protected-branches type BranchAccessDescription struct { ID int `json:"id"` AccessLevel AccessLevelValue `json:"access_level"` AccessLevelDescription string `json:"access_level_description"` DeployKeyID int `json:"deploy_key_id"` UserID int `json:"user_id"` GroupID int `json:"group_id"` } // ListProtectedBranchesOptions represents the available ListProtectedBranches() // options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/protected_branches.html#list-protected-branches type ListProtectedBranchesOptions struct { ListOptions Search *string `url:"search,omitempty" json:"search,omitempty"` } // ListProtectedBranches gets a list of protected branches from a project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/protected_branches.html#list-protected-branches func (s *ProtectedBranchesService) ListProtectedBranches(pid interface{}, opt *ListProtectedBranchesOptions, options ...RequestOptionFunc) ([]*ProtectedBranch, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/protected_branches", PathEscape(project)) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } var p []*ProtectedBranch resp, err := s.client.Do(req, &p) if err != nil { return nil, resp, err } return p, resp, nil } // GetProtectedBranch gets a single protected branch or wildcard protected branch. // // GitLab API docs: // https://docs.gitlab.com/ee/api/protected_branches.html#get-a-single-protected-branch-or-wildcard-protected-branch func (s *ProtectedBranchesService) GetProtectedBranch(pid interface{}, branch string, options ...RequestOptionFunc) (*ProtectedBranch, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/protected_branches/%s", PathEscape(project), url.PathEscape(branch)) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } p := new(ProtectedBranch) resp, err := s.client.Do(req, p) if err != nil { return nil, resp, err } return p, resp, nil } // ProtectRepositoryBranchesOptions represents the available // ProtectRepositoryBranches() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/protected_branches.html#protect-repository-branches type ProtectRepositoryBranchesOptions struct { Name *string `url:"name,omitempty" json:"name,omitempty"` PushAccessLevel *AccessLevelValue `url:"push_access_level,omitempty" json:"push_access_level,omitempty"` MergeAccessLevel *AccessLevelValue `url:"merge_access_level,omitempty" json:"merge_access_level,omitempty"` UnprotectAccessLevel *AccessLevelValue `url:"unprotect_access_level,omitempty" json:"unprotect_access_level,omitempty"` AllowForcePush *bool `url:"allow_force_push,omitempty" json:"allow_force_push,omitempty"` AllowedToPush *[]*BranchPermissionOptions `url:"allowed_to_push,omitempty" json:"allowed_to_push,omitempty"` AllowedToMerge *[]*BranchPermissionOptions `url:"allowed_to_merge,omitempty" json:"allowed_to_merge,omitempty"` AllowedToUnprotect *[]*BranchPermissionOptions `url:"allowed_to_unprotect,omitempty" json:"allowed_to_unprotect,omitempty"` CodeOwnerApprovalRequired *bool `url:"code_owner_approval_required,omitempty" json:"code_owner_approval_required,omitempty"` } // BranchPermissionOptions represents a branch permission option. // // GitLab API docs: // https://docs.gitlab.com/ee/api/protected_branches.html#protect-repository-branches type BranchPermissionOptions struct { ID *int `url:"id,omitempty" json:"id,omitempty"` UserID *int `url:"user_id,omitempty" json:"user_id,omitempty"` GroupID *int `url:"group_id,omitempty" json:"group_id,omitempty"` DeployKeyID *int `url:"deploy_key_id,omitempty" json:"deploy_key_id,omitempty"` AccessLevel *AccessLevelValue `url:"access_level,omitempty" json:"access_level,omitempty"` Destroy *bool `url:"_destroy,omitempty" json:"_destroy,omitempty"` } // ProtectRepositoryBranches protects a single repository branch or several // project repository branches using a wildcard protected branch. // // GitLab API docs: // https://docs.gitlab.com/ee/api/protected_branches.html#protect-repository-branches func (s *ProtectedBranchesService) ProtectRepositoryBranches(pid interface{}, opt *ProtectRepositoryBranchesOptions, options ...RequestOptionFunc) (*ProtectedBranch, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/protected_branches", PathEscape(project)) req, err := s.client.NewRequest(http.MethodPost, u, opt, options) if err != nil { return nil, nil, err } p := new(ProtectedBranch) resp, err := s.client.Do(req, p) if err != nil { return nil, resp, err } return p, resp, nil } // UnprotectRepositoryBranches unprotects the given protected branch or wildcard // protected branch. // // GitLab API docs: // https://docs.gitlab.com/ee/api/protected_branches.html#unprotect-repository-branches func (s *ProtectedBranchesService) UnprotectRepositoryBranches(pid interface{}, branch string, options ...RequestOptionFunc) (*Response, error) { project, err := parseID(pid) if err != nil { return nil, err } u := fmt.Sprintf("projects/%s/protected_branches/%s", PathEscape(project), url.PathEscape(branch)) req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } // UpdateProtectedBranchOptions represents the available // UpdateProtectedBranch() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/protected_branches.html#update-a-protected-branch type UpdateProtectedBranchOptions struct { Name *string `url:"name,omitempty" json:"name,omitempty"` AllowForcePush *bool `url:"allow_force_push,omitempty" json:"allow_force_push,omitempty"` CodeOwnerApprovalRequired *bool `url:"code_owner_approval_required,omitempty" json:"code_owner_approval_required,omitempty"` AllowedToPush *[]*BranchPermissionOptions `url:"allowed_to_push,omitempty" json:"allowed_to_push,omitempty"` AllowedToMerge *[]*BranchPermissionOptions `url:"allowed_to_merge,omitempty" json:"allowed_to_merge,omitempty"` AllowedToUnprotect *[]*BranchPermissionOptions `url:"allowed_to_unprotect,omitempty" json:"allowed_to_unprotect,omitempty"` } // UpdateProtectedBranch updates a protected branch. // // Gitlab API docs: // https://docs.gitlab.com/ee/api/protected_branches.html#update-a-protected-branch func (s *ProtectedBranchesService) UpdateProtectedBranch(pid interface{}, branch string, opt *UpdateProtectedBranchOptions, options ...RequestOptionFunc) (*ProtectedBranch, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/protected_branches/%s", PathEscape(project), url.PathEscape(branch)) req, err := s.client.NewRequest(http.MethodPatch, u, opt, options) if err != nil { return nil, nil, err } p := new(ProtectedBranch) resp, err := s.client.Do(req, p) if err != nil { return nil, resp, err } return p, resp, nil } // RequireCodeOwnerApprovalsOptions represents the available // RequireCodeOwnerApprovals() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/protected_branches.html#update-a-protected-branch type RequireCodeOwnerApprovalsOptions struct { CodeOwnerApprovalRequired *bool `url:"code_owner_approval_required,omitempty" json:"code_owner_approval_required,omitempty"` } // RequireCodeOwnerApprovals updates the code owner approval option. // // Deprecated: Use UpdateProtectedBranch() instead. // // Gitlab API docs: // https://docs.gitlab.com/ee/api/protected_branches.html#update-a-protected-branch func (s *ProtectedBranchesService) RequireCodeOwnerApprovals(pid interface{}, branch string, opt *RequireCodeOwnerApprovalsOptions, options ...RequestOptionFunc) (*Response, error) { updateOptions := &UpdateProtectedBranchOptions{ CodeOwnerApprovalRequired: opt.CodeOwnerApprovalRequired, } _, req, err := s.UpdateProtectedBranch(pid, branch, updateOptions, options...) return req, err } golang-gitlab-gitlab-org-api-client-go-0.123.0/protected_branches_test.go000066400000000000000000000146011475761473200263050ustar00rootroot00000000000000// // Copyright 2021, Sander van Harmelen // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "fmt" "net/http" "reflect" "testing" ) func TestListProtectedBranches(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/protected_branches", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, `[ { "id":1, "name":"master", "push_access_levels":[{ "id":1, "access_level":40, "access_level_description":"Maintainers", "deploy_key_id":null, "user_id":null, "group_id":null },{ "id":2, "access_level":30, "access_level_description":"User name", "deploy_key_id":null, "user_id":123, "group_id":null },{ "id":3, "access_level":40, "access_level_description":"deploy key", "deploy_key_id":456, "user_id":null, "group_id":null }], "merge_access_levels":[{ "id":1, "access_level":40, "access_level_description":"Maintainers", "user_id":null, "group_id":null }], "code_owner_approval_required":false } ]`) }) opt := &ListProtectedBranchesOptions{} protectedBranches, _, err := client.ProtectedBranches.ListProtectedBranches("1", opt) if err != nil { t.Errorf("ProtectedBranches.ListProtectedBranches returned error: %v", err) } want := []*ProtectedBranch{ { ID: 1, Name: "master", PushAccessLevels: []*BranchAccessDescription{ { ID: 1, AccessLevel: 40, AccessLevelDescription: "Maintainers", }, { ID: 2, AccessLevel: 30, AccessLevelDescription: "User name", UserID: 123, }, { ID: 3, AccessLevel: 40, AccessLevelDescription: "deploy key", DeployKeyID: 456, }, }, MergeAccessLevels: []*BranchAccessDescription{ { ID: 1, AccessLevel: 40, AccessLevelDescription: "Maintainers", }, }, AllowForcePush: false, CodeOwnerApprovalRequired: false, }, } if !reflect.DeepEqual(want, protectedBranches) { t.Errorf("ProtectedBranches.ListProtectedBranches returned %+v, want %+v", protectedBranches, want) } } func TestListProtectedBranchesWithoutCodeOwnerApproval(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/protected_branches", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, `[ { "id":1, "name":"master", "push_access_levels":[{ "access_level":40, "access_level_description":"Maintainers" }], "merge_access_levels":[{ "access_level":40, "access_level_description":"Maintainers" }] } ]`) }) opt := &ListProtectedBranchesOptions{} protectedBranches, _, err := client.ProtectedBranches.ListProtectedBranches("1", opt) if err != nil { t.Errorf("ProtectedBranches.ListProtectedBranches returned error: %v", err) } want := []*ProtectedBranch{ { ID: 1, Name: "master", PushAccessLevels: []*BranchAccessDescription{ { AccessLevel: 40, AccessLevelDescription: "Maintainers", }, }, MergeAccessLevels: []*BranchAccessDescription{ { AccessLevel: 40, AccessLevelDescription: "Maintainers", }, }, AllowForcePush: false, CodeOwnerApprovalRequired: false, }, } if !reflect.DeepEqual(want, protectedBranches) { t.Errorf("Projects.ListProjects returned %+v, want %+v", protectedBranches, want) } } func TestProtectRepositoryBranches(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/protected_branches", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) fmt.Fprint(w, ` { "id":1, "name":"master", "push_access_levels":[{ "access_level":40, "access_level_description":"Maintainers" }], "merge_access_levels":[{ "access_level":40, "access_level_description":"Maintainers" }], "allow_force_push":true, "code_owner_approval_required":true }`) }) opt := &ProtectRepositoryBranchesOptions{ Name: Ptr("master"), PushAccessLevel: Ptr(MaintainerPermissions), MergeAccessLevel: Ptr(MaintainerPermissions), AllowForcePush: Ptr(true), CodeOwnerApprovalRequired: Ptr(true), } projects, _, err := client.ProtectedBranches.ProtectRepositoryBranches("1", opt) if err != nil { t.Errorf("ProtectedBranches.ProtectRepositoryBranches returned error: %v", err) } want := &ProtectedBranch{ ID: 1, Name: "master", PushAccessLevels: []*BranchAccessDescription{ { AccessLevel: 40, AccessLevelDescription: "Maintainers", }, }, MergeAccessLevels: []*BranchAccessDescription{ { AccessLevel: 40, AccessLevelDescription: "Maintainers", }, }, AllowForcePush: true, CodeOwnerApprovalRequired: true, } if !reflect.DeepEqual(want, projects) { t.Errorf("Projects.ListProjects returned %+v, want %+v", projects, want) } } func TestUpdateRepositoryBranches(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/protected_branches/master", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPatch) testBody(t, r, `{"code_owner_approval_required":true}`) fmt.Fprintf(w, `{ "name": "master", "code_owner_approval_required": true }`) }) opt := &UpdateProtectedBranchOptions{ CodeOwnerApprovalRequired: Ptr(true), } protectedBranch, _, err := client.ProtectedBranches.UpdateProtectedBranch("1", "master", opt) if err != nil { t.Errorf("ProtectedBranches.UpdateProtectedBranch returned error: %v", err) } want := &ProtectedBranch{ Name: "master", CodeOwnerApprovalRequired: true, } if !reflect.DeepEqual(want, protectedBranch) { t.Errorf("ProtectedBranches.UpdateProtectedBranch returned %+v, want %+v", protectedBranch, want) } } golang-gitlab-gitlab-org-api-client-go-0.123.0/protected_environments.go000066400000000000000000000277161475761473200262230ustar00rootroot00000000000000// // Copyright 2021, Sander van Harmelen // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "fmt" "net/http" ) // ProtectedEnvironmentsService handles communication with the protected // environment methods of the GitLab API. // // GitLab API docs: // https://docs.gitlab.com/ee/api/protected_environments.html type ProtectedEnvironmentsService struct { client *Client } // ProtectedEnvironment represents a protected environment. // // GitLab API docs: // https://docs.gitlab.com/ee/api/protected_environments.html type ProtectedEnvironment struct { Name string `json:"name"` DeployAccessLevels []*EnvironmentAccessDescription `json:"deploy_access_levels"` RequiredApprovalCount int `json:"required_approval_count"` ApprovalRules []*EnvironmentApprovalRule `json:"approval_rules"` } // EnvironmentAccessDescription represents the access decription for a protected // environment. // // GitLab API docs: // https://docs.gitlab.com/ee/api/protected_environments.html type EnvironmentAccessDescription struct { ID int `json:"id"` AccessLevel AccessLevelValue `json:"access_level"` AccessLevelDescription string `json:"access_level_description"` UserID int `json:"user_id"` GroupID int `json:"group_id"` GroupInheritanceType int `json:"group_inheritance_type"` } // EnvironmentApprovalRule represents the approval rules for a protected // environment. // // GitLab API docs: // https://docs.gitlab.com/ee/api/protected_environments.html#protect-a-single-environment type EnvironmentApprovalRule struct { ID int `json:"id"` UserID int `json:"user_id"` GroupID int `json:"group_id"` AccessLevel AccessLevelValue `json:"access_level"` AccessLevelDescription string `json:"access_level_description"` RequiredApprovalCount int `json:"required_approvals"` GroupInheritanceType int `json:"group_inheritance_type"` } // ListProtectedEnvironmentsOptions represents the available // ListProtectedEnvironments() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/protected_environments.html#list-protected-environments type ListProtectedEnvironmentsOptions ListOptions // ListProtectedEnvironments returns a list of protected environments from a // project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/protected_environments.html#list-protected-environments func (s *ProtectedEnvironmentsService) ListProtectedEnvironments(pid interface{}, opt *ListProtectedEnvironmentsOptions, options ...RequestOptionFunc) ([]*ProtectedEnvironment, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/protected_environments", PathEscape(project)) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } var pes []*ProtectedEnvironment resp, err := s.client.Do(req, &pes) if err != nil { return nil, resp, err } return pes, resp, nil } // GetProtectedEnvironment returns a single protected environment or wildcard // protected environment. // // GitLab API docs: // https://docs.gitlab.com/ee/api/protected_environments.html#get-a-single-protected-environment func (s *ProtectedEnvironmentsService) GetProtectedEnvironment(pid interface{}, environment string, options ...RequestOptionFunc) (*ProtectedEnvironment, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/protected_environments/%s", PathEscape(project), PathEscape(environment)) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } pe := new(ProtectedEnvironment) resp, err := s.client.Do(req, pe) if err != nil { return nil, resp, err } return pe, resp, nil } // ProtectRepositoryEnvironmentsOptions represents the available // ProtectRepositoryEnvironments() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/protected_environments.html#protect-a-single-environment type ProtectRepositoryEnvironmentsOptions struct { Name *string `url:"name,omitempty" json:"name,omitempty"` DeployAccessLevels *[]*EnvironmentAccessOptions `url:"deploy_access_levels,omitempty" json:"deploy_access_levels,omitempty"` RequiredApprovalCount *int `url:"required_approval_count,omitempty" json:"required_approval_count,omitempty"` ApprovalRules *[]*EnvironmentApprovalRuleOptions `url:"approval_rules,omitempty" json:"approval_rules,omitempty"` } // EnvironmentAccessOptions represents the options for an access decription for // a protected environment. // // GitLab API docs: // https://docs.gitlab.com/ee/api/protected_environments.html#protect-a-single-environment type EnvironmentAccessOptions struct { AccessLevel *AccessLevelValue `url:"access_level,omitempty" json:"access_level,omitempty"` UserID *int `url:"user_id,omitempty" json:"user_id,omitempty"` GroupID *int `url:"group_id,omitempty" json:"group_id,omitempty"` GroupInheritanceType *int `url:"group_inheritance_type,omitempty" json:"group_inheritance_type,omitempty"` } // EnvironmentApprovalRuleOptions represents the approval rules for a protected // environment. // // GitLab API docs: // https://docs.gitlab.com/ee/api/protected_environments.html#protect-a-single-environment type EnvironmentApprovalRuleOptions struct { UserID *int `url:"user_id,omitempty" json:"user_id,omitempty"` GroupID *int `url:"group_id,omitempty" json:"group_id,omitempty"` AccessLevel *AccessLevelValue `url:"access_level,omitempty" json:"access_level,omitempty"` AccessLevelDescription *string `url:"access_level_description,omitempty" json:"access_level_description,omitempty"` RequiredApprovalCount *int `url:"required_approvals,omitempty" json:"required_approvals,omitempty"` GroupInheritanceType *int `url:"group_inheritance_type,omitempty" json:"group_inheritance_type,omitempty"` } // ProtectRepositoryEnvironments protects a single repository environment or // several project repository environments using wildcard protected environment. // // GitLab API docs: // https://docs.gitlab.com/ee/api/protected_environments.html#protect-a-single-environment func (s *ProtectedEnvironmentsService) ProtectRepositoryEnvironments(pid interface{}, opt *ProtectRepositoryEnvironmentsOptions, options ...RequestOptionFunc) (*ProtectedEnvironment, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/protected_environments", PathEscape(project)) req, err := s.client.NewRequest(http.MethodPost, u, opt, options) if err != nil { return nil, nil, err } pe := new(ProtectedEnvironment) resp, err := s.client.Do(req, pe) if err != nil { return nil, resp, err } return pe, resp, nil } // UpdateProtectedEnvironmentsOptions represents the available // UpdateProtectedEnvironments() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/protected_environments.html#update-a-protected-environment type UpdateProtectedEnvironmentsOptions struct { Name *string `url:"name,omitempty" json:"name,omitempty"` DeployAccessLevels *[]*UpdateEnvironmentAccessOptions `url:"deploy_access_levels,omitempty" json:"deploy_access_levels,omitempty"` RequiredApprovalCount *int `url:"required_approval_count,omitempty" json:"required_approval_count,omitempty"` ApprovalRules *[]*UpdateEnvironmentApprovalRuleOptions `url:"approval_rules,omitempty" json:"approval_rules,omitempty"` } // UpdateEnvironmentAccessOptions represents the options for updates to an // access decription for a protected environment. // // GitLab API docs: // https://docs.gitlab.com/ee/api/protected_environments.html#update-a-protected-environment type UpdateEnvironmentAccessOptions struct { AccessLevel *AccessLevelValue `url:"access_level,omitempty" json:"access_level,omitempty"` ID *int `url:"id,omitempty" json:"id,omitempty"` UserID *int `url:"user_id,omitempty" json:"user_id,omitempty"` GroupID *int `url:"group_id,omitempty" json:"group_id,omitempty"` GroupInheritanceType *int `url:"group_inheritance_type,omitempty" json:"group_inheritance_type,omitempty"` Destroy *bool `url:"_destroy,omitempty" json:"_destroy,omitempty"` } // UpdateEnvironmentApprovalRuleOptions represents the updates to the approval // rules for a protected environment. // // GitLab API docs: // https://docs.gitlab.com/ee/api/protected_environments.html#update-a-protected-environment type UpdateEnvironmentApprovalRuleOptions struct { ID *int `url:"id,omitempty" json:"id,omitempty"` UserID *int `url:"user_id,omitempty" json:"user_id,omitempty"` GroupID *int `url:"group_id,omitempty" json:"group_id,omitempty"` AccessLevel *AccessLevelValue `url:"access_level,omitempty" json:"access_level,omitempty"` AccessLevelDescription *string `url:"access_level_description,omitempty" json:"access_level_description,omitempty"` RequiredApprovalCount *int `url:"required_approvals,omitempty" json:"required_approvals,omitempty"` GroupInheritanceType *int `url:"group_inheritance_type,omitempty" json:"group_inheritance_type,omitempty"` Destroy *bool `url:"_destroy,omitempty" json:"_destroy,omitempty"` } // UpdateProtectedEnvironments updates a single repository environment or // several project repository environments using wildcard protected environment. // // GitLab API docs: // https://docs.gitlab.com/ee/api/protected_environments.html#update-a-protected-environment func (s *ProtectedEnvironmentsService) UpdateProtectedEnvironments(pid interface{}, environment string, opt *UpdateProtectedEnvironmentsOptions, options ...RequestOptionFunc) (*ProtectedEnvironment, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/protected_environments/%s", PathEscape(project), PathEscape(environment)) req, err := s.client.NewRequest(http.MethodPut, u, opt, options) if err != nil { return nil, nil, err } pe := new(ProtectedEnvironment) resp, err := s.client.Do(req, pe) if err != nil { return nil, resp, err } return pe, resp, nil } // UnprotectEnvironment unprotects the given protected environment or wildcard // protected environment. // // GitLab API docs: // https://docs.gitlab.com/ee/api/protected_environments.html#unprotect-a-single-environment func (s *ProtectedEnvironmentsService) UnprotectEnvironment(pid interface{}, environment string, options ...RequestOptionFunc) (*Response, error) { project, err := parseID(pid) if err != nil { return nil, err } u := fmt.Sprintf("projects/%s/protected_environments/%s", PathEscape(project), PathEscape(environment)) req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } golang-gitlab-gitlab-org-api-client-go-0.123.0/protected_environments_test.go000066400000000000000000000420571475761473200272550ustar00rootroot00000000000000// // Copyright 2021, Sander van Harmelen // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "fmt" "net/http" "testing" "github.com/stretchr/testify/assert" ) func TestListProtectedEnvironments(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/protected_environments", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, `[{ "name":"1.0.0", "deploy_access_levels": [ { "access_level": 40, "access_level_description": "Maintainers", "group_inheritance_type": 1 } ], "required_approval_count": 1, "approval_rules": [ { "id": 38, "user_id": 42, "group_id": null, "access_level": null, "access_level_description": "qa-group", "required_approvals": 1, "group_inheritance_type": 0 }, { "id": 39, "user_id": null, "group_id": 135, "access_level": 30, "access_level_description": "security-group", "required_approvals": 2, "group_inheritance_type": 1 } ] },{ "name":"*-release", "deploy_access_levels": [ { "access_level": 30, "access_level_description": "Developers + Maintainers" } ] }]`) }) expected := []*ProtectedEnvironment{ { Name: "1.0.0", DeployAccessLevels: []*EnvironmentAccessDescription{ { AccessLevel: 40, AccessLevelDescription: "Maintainers", GroupInheritanceType: 1, }, }, RequiredApprovalCount: 1, ApprovalRules: []*EnvironmentApprovalRule{ { ID: 38, UserID: 42, AccessLevelDescription: "qa-group", RequiredApprovalCount: 1, }, { ID: 39, GroupID: 135, AccessLevel: 30, AccessLevelDescription: "security-group", RequiredApprovalCount: 2, GroupInheritanceType: 1, }, }, }, { Name: "*-release", DeployAccessLevels: []*EnvironmentAccessDescription{ { AccessLevel: 30, AccessLevelDescription: "Developers + Maintainers", }, }, }, } opt := &ListProtectedEnvironmentsOptions{} environments, _, err := client.ProtectedEnvironments.ListProtectedEnvironments(1, opt) assert.NoError(t, err, "failed to get response") assert.Equal(t, expected, environments) } func TestGetProtectedEnvironment(t *testing.T) { mux, client := setup(t) // Test with RequiredApprovalCount environmentName := "my-awesome-environment" mux.HandleFunc(fmt.Sprintf("/api/v4/projects/1/protected_environments/%s", environmentName), func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, `{ "name":"my-awesome-environment", "deploy_access_levels": [ { "access_level": 30, "access_level_description": "Developers + Maintainers" } ], "required_approval_count": 1, "approval_rules": [ { "id": 1, "user_id": null, "group_id": 10, "access_level": 5, "access_level_description": "devops", "required_approvals": 0, "group_inheritance_type": 0 } ] }`) }) expected := &ProtectedEnvironment{ Name: environmentName, DeployAccessLevels: []*EnvironmentAccessDescription{ { AccessLevel: 30, AccessLevelDescription: "Developers + Maintainers", }, }, RequiredApprovalCount: 1, ApprovalRules: []*EnvironmentApprovalRule{ { ID: 1, GroupID: 10, AccessLevel: 5, AccessLevelDescription: "devops", }, }, } environment, _, err := client.ProtectedEnvironments.GetProtectedEnvironment(1, environmentName) assert.NoError(t, err, "failed to get response") assert.Equal(t, expected, environment) // Test without RequiredApprovalCount nor ApprovalRules environmentName = "my-awesome-environment2" mux.HandleFunc(fmt.Sprintf("/api/v4/projects/2/protected_environments/%s", environmentName), func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, `{ "name":"my-awesome-environment2", "deploy_access_levels": [ { "access_level": 30, "access_level_description": "Developers + Maintainers" } ] }`) }) expected = &ProtectedEnvironment{ Name: environmentName, DeployAccessLevels: []*EnvironmentAccessDescription{ { AccessLevel: 30, AccessLevelDescription: "Developers + Maintainers", }, }, } environment, _, err = client.ProtectedEnvironments.GetProtectedEnvironment(2, environmentName) assert.NoError(t, err, "failed to get response") assert.Equal(t, expected, environment) } func TestProtectRepositoryEnvironments(t *testing.T) { mux, client := setup(t) // Test with RequiredApprovalCount and ApprovalRules mux.HandleFunc("/api/v4/projects/1/protected_environments", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) fmt.Fprint(w, `{ "name":"my-awesome-environment", "deploy_access_levels": [ { "access_level": 30, "access_level_description": "Developers + Maintainers", "group_inheritance_type": 1 } ], "required_approval_count": 2, "approval_rules": [ { "id": 1, "user_id": null, "group_id": 10, "access_level": 5, "access_level_description": "devops", "required_approvals": 0, "group_inheritance_type": 0 } ] }`) }) expected := &ProtectedEnvironment{ Name: "my-awesome-environment", DeployAccessLevels: []*EnvironmentAccessDescription{ { AccessLevel: 30, AccessLevelDescription: "Developers + Maintainers", GroupInheritanceType: 1, }, }, RequiredApprovalCount: 2, ApprovalRules: []*EnvironmentApprovalRule{ { ID: 1, GroupID: 10, AccessLevel: 5, AccessLevelDescription: "devops", }, }, } opt := &ProtectRepositoryEnvironmentsOptions{ Name: Ptr("my-awesome-environment"), DeployAccessLevels: &[]*EnvironmentAccessOptions{ {AccessLevel: Ptr(AccessLevelValue(30))}, }, RequiredApprovalCount: Ptr(2), ApprovalRules: &[]*EnvironmentApprovalRuleOptions{ { GroupID: Ptr(10), AccessLevel: Ptr(AccessLevelValue(0)), AccessLevelDescription: Ptr("devops"), }, }, } environment, _, err := client.ProtectedEnvironments.ProtectRepositoryEnvironments(1, opt) assert.NoError(t, err, "failed to get response") assert.Equal(t, expected, environment) // Test without RequiredApprovalCount nor ApprovalRules mux.HandleFunc("/api/v4/projects/2/protected_environments", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) fmt.Fprint(w, `{ "name":"my-awesome-environment2", "deploy_access_levels": [ { "access_level": 30, "access_level_description": "Developers + Maintainers" } ] }`) }) expected = &ProtectedEnvironment{ Name: "my-awesome-environment2", DeployAccessLevels: []*EnvironmentAccessDescription{ { AccessLevel: 30, AccessLevelDescription: "Developers + Maintainers", }, }, } opt = &ProtectRepositoryEnvironmentsOptions{ Name: Ptr("my-awesome-environment2"), DeployAccessLevels: &[]*EnvironmentAccessOptions{ {AccessLevel: Ptr(AccessLevelValue(30))}, }, } environment, _, err = client.ProtectedEnvironments.ProtectRepositoryEnvironments(2, opt) assert.NoError(t, err, "failed to get response") assert.Equal(t, expected, environment) } func TestUpdateProtectedEnvironments(t *testing.T) { mux, client := setup(t) // Test with DeployAccessLevels, RequiredApprovalCount, and ApprovalRules as if adding new to existing protected environment environmentName := "dev-test" mux.HandleFunc(fmt.Sprintf("/api/v4/projects/1/protected_environments/%s", environmentName), func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPut) fmt.Fprintf(w, `{ "name":"%s", "deploy_access_levels": [ { "id": 42, "access_level": 30, "access_level_description": "Developers + Maintainers", "group_inheritance_type": 1 } ], "required_approval_count": 2, "approval_rules": [ { "id": 1, "user_id": null, "group_id": 10, "access_level": 5, "access_level_description": "devops", "required_approvals": 0, "group_inheritance_type": 0 } ] }`, environmentName) }) expected := &ProtectedEnvironment{ Name: environmentName, DeployAccessLevels: []*EnvironmentAccessDescription{ { ID: 42, AccessLevel: 30, AccessLevelDescription: "Developers + Maintainers", GroupInheritanceType: 1, }, }, RequiredApprovalCount: 2, ApprovalRules: []*EnvironmentApprovalRule{ { ID: 1, GroupID: 10, AccessLevel: 5, AccessLevelDescription: "devops", }, }, } opt := &UpdateProtectedEnvironmentsOptions{ Name: Ptr(environmentName), DeployAccessLevels: &[]*UpdateEnvironmentAccessOptions{ { AccessLevel: Ptr(AccessLevelValue(30)), GroupInheritanceType: Ptr(1), }, }, RequiredApprovalCount: Ptr(2), ApprovalRules: &[]*UpdateEnvironmentApprovalRuleOptions{ { GroupID: Ptr(10), AccessLevel: Ptr(AccessLevelValue(0)), AccessLevelDescription: Ptr("devops"), }, }, } environment, _, err := client.ProtectedEnvironments.UpdateProtectedEnvironments(1, environmentName, opt) assert.NoError(t, err, "failed to get response") assert.Equal(t, expected, environment) // Test with DeployAccessLevels only, as if adding new to existing protected environment mux.HandleFunc(fmt.Sprintf("/api/v4/projects/2/protected_environments/%s", environmentName), func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPut) fmt.Fprintf(w, `{ "name":"%s", "deploy_access_levels": [ { "id": 42, "access_level": 30, "access_level_description": "Developers + Maintainers", "group_inheritance_type": 1 } ] }`, environmentName) }) expected = &ProtectedEnvironment{ Name: environmentName, DeployAccessLevels: []*EnvironmentAccessDescription{ { ID: 42, AccessLevel: 30, AccessLevelDescription: "Developers + Maintainers", GroupInheritanceType: 1, }, }, } opt = &UpdateProtectedEnvironmentsOptions{ Name: Ptr(environmentName), DeployAccessLevels: &[]*UpdateEnvironmentAccessOptions{ {AccessLevel: Ptr(AccessLevelValue(30))}, }, } environment, _, err = client.ProtectedEnvironments.UpdateProtectedEnvironments(2, environmentName, opt) assert.NoError(t, err, "failed to get response") assert.Equal(t, expected, environment) // Test update to DeployAccessLevel mux.HandleFunc(fmt.Sprintf("/api/v4/projects/3/protected_environments/%s", environmentName), func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPut) fmt.Fprintf(w, `{ "name":"%s", "deploy_access_levels": [ { "id": 42, "access_level": 30, "access_level_description": "Developers + Maintainers", "group_inheritance_type": 0 } ], "required_approval_count": 2 }`, environmentName) }) expected = &ProtectedEnvironment{ Name: environmentName, DeployAccessLevels: []*EnvironmentAccessDescription{ { ID: 42, AccessLevel: 30, AccessLevelDescription: "Developers + Maintainers", }, }, RequiredApprovalCount: 2, } opt = &UpdateProtectedEnvironmentsOptions{ Name: Ptr(environmentName), DeployAccessLevels: &[]*UpdateEnvironmentAccessOptions{ { ID: Ptr(42), AccessLevel: Ptr(AccessLevelValue(30)), }, }, } environment, _, err = client.ProtectedEnvironments.UpdateProtectedEnvironments(3, environmentName, opt) assert.NoError(t, err, "failed to get response") assert.Equal(t, expected, environment) // Test update to ApprovalRules mux.HandleFunc(fmt.Sprintf("/api/v4/projects/4/protected_environments/%s", environmentName), func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPut) fmt.Fprintf(w, `{ "name":"%s", "deploy_access_levels": [ { "id": 42, "access_level": 30, "access_level_description": "Developers + Maintainers" } ], "required_approval_count": 2, "approval_rules": [ { "id": 1, "user_id": null, "group_id": 10, "access_level": 5, "access_level_description": "devops", "required_approvals": 0, "group_inheritance_type": 0 } ] }`, environmentName) }) expected = &ProtectedEnvironment{ Name: environmentName, DeployAccessLevels: []*EnvironmentAccessDescription{ { ID: 42, AccessLevel: 30, AccessLevelDescription: "Developers + Maintainers", }, }, RequiredApprovalCount: 2, ApprovalRules: []*EnvironmentApprovalRule{ { ID: 1, GroupID: 10, AccessLevel: 5, AccessLevelDescription: "devops", }, }, } opt = &UpdateProtectedEnvironmentsOptions{ Name: Ptr(environmentName), ApprovalRules: &[]*UpdateEnvironmentApprovalRuleOptions{ { ID: Ptr(1), GroupID: Ptr(10), AccessLevel: Ptr(AccessLevelValue(0)), AccessLevelDescription: Ptr("devops"), }, }, } environment, _, err = client.ProtectedEnvironments.UpdateProtectedEnvironments(4, environmentName, opt) assert.NoError(t, err, "failed to get response") assert.Equal(t, expected, environment) // Test destroy ApprovalRule mux.HandleFunc(fmt.Sprintf("/api/v4/projects/5/protected_environments/%s", environmentName), func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPut) fmt.Fprintf(w, `{ "name":"%s", "deploy_access_levels": [ { "id": 42, "access_level": 30, "access_level_description": "Developers + Maintainers" } ], "required_approval_count": 0, "approval_rules": [] }`, environmentName) }) expected = &ProtectedEnvironment{ Name: environmentName, DeployAccessLevels: []*EnvironmentAccessDescription{ { ID: 42, AccessLevel: 30, AccessLevelDescription: "Developers + Maintainers", }, }, RequiredApprovalCount: 0, ApprovalRules: []*EnvironmentApprovalRule{}, } opt = &UpdateProtectedEnvironmentsOptions{ Name: Ptr(environmentName), ApprovalRules: &[]*UpdateEnvironmentApprovalRuleOptions{ { ID: Ptr(1), Destroy: Ptr(true), }, }, RequiredApprovalCount: Ptr(0), } environment, _, err = client.ProtectedEnvironments.UpdateProtectedEnvironments(5, environmentName, opt) assert.NoError(t, err, "failed to get response") assert.Equal(t, expected, environment) } func TestUpdateRepositoryEnvironmentsEscapesURL(t *testing.T) { mux, client := setup(t) rawRequest := "" // Use a "/" in the environment name, so it needs encoding mux.HandleFunc("/api/v4/projects/1/protected_environments/test%2Fenvironment", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPut) // Store the raw request so we're sure it's encoded properly rawRequest = r.URL.RawPath fmt.Fprintf(w, `{ "name": "test/environment" }`) }) _, resp, err := client.ProtectedEnvironments.UpdateProtectedEnvironments(1, "test/environment", &UpdateProtectedEnvironmentsOptions{}) assert.NoError(t, err, "failed to get response") assert.Equal(t, http.StatusOK, resp.StatusCode) assert.Equal(t, rawRequest, "/api/v4/projects/1/protected_environments/test%2Fenvironment") } func TestUnprotectRepositoryEnvironments(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/protected_environments/my-awesome-environment", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodDelete) }) resp, err := client.ProtectedEnvironments.UnprotectEnvironment(1, "my-awesome-environment") assert.NoError(t, err, "failed to get response") assert.Equal(t, http.StatusOK, resp.StatusCode) } golang-gitlab-gitlab-org-api-client-go-0.123.0/protected_tags.go000066400000000000000000000132601475761473200244170ustar00rootroot00000000000000// // Copyright 2021, Sander van Harmelen // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "fmt" "net/http" ) // ProtectedTagsService handles communication with the protected tag methods // of the GitLab API. // // GitLab API docs: // https://docs.gitlab.com/ee/api/protected_tags.html type ProtectedTagsService struct { client *Client } // ProtectedTag represents a protected tag. // // GitLab API docs: // https://docs.gitlab.com/ee/api/protected_tags.html type ProtectedTag struct { Name string `json:"name"` CreateAccessLevels []*TagAccessDescription `json:"create_access_levels"` } // TagAccessDescription reperesents the access decription for a protected tag. // // GitLab API docs: // https://docs.gitlab.com/ee/api/protected_tags.html type TagAccessDescription struct { ID int `json:"id"` UserID int `json:"user_id"` GroupID int `json:"group_id"` AccessLevel AccessLevelValue `json:"access_level"` AccessLevelDescription string `json:"access_level_description"` } // ListProtectedTagsOptions represents the available ListProtectedTags() // options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/protected_tags.html#list-protected-tags type ListProtectedTagsOptions ListOptions // ListProtectedTags returns a list of protected tags from a project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/protected_tags.html#list-protected-tags func (s *ProtectedTagsService) ListProtectedTags(pid interface{}, opt *ListProtectedTagsOptions, options ...RequestOptionFunc) ([]*ProtectedTag, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/protected_tags", PathEscape(project)) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } var pts []*ProtectedTag resp, err := s.client.Do(req, &pts) if err != nil { return nil, resp, err } return pts, resp, nil } // GetProtectedTag returns a single protected tag or wildcard protected tag. // // GitLab API docs: // https://docs.gitlab.com/ee/api/protected_tags.html#get-a-single-protected-tag-or-wildcard-protected-tag func (s *ProtectedTagsService) GetProtectedTag(pid interface{}, tag string, options ...RequestOptionFunc) (*ProtectedTag, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/protected_tags/%s", PathEscape(project), PathEscape(tag)) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } pt := new(ProtectedTag) resp, err := s.client.Do(req, pt) if err != nil { return nil, resp, err } return pt, resp, nil } // ProtectRepositoryTagsOptions represents the available ProtectRepositoryTags() // options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/protected_tags.html#protect-repository-tags type ProtectRepositoryTagsOptions struct { Name *string `url:"name,omitempty" json:"name,omitempty"` CreateAccessLevel *AccessLevelValue `url:"create_access_level,omitempty" json:"create_access_level,omitempty"` AllowedToCreate *[]*TagsPermissionOptions `url:"allowed_to_create,omitempty" json:"allowed_to_create,omitempty"` } // TagsPermissionOptions represents a protected tag permission option. // // GitLab API docs: // https://docs.gitlab.com/ee/api/protected_tags.html#protect-repository-tags type TagsPermissionOptions struct { UserID *int `url:"user_id,omitempty" json:"user_id,omitempty"` GroupID *int `url:"group_id,omitempty" json:"group_id,omitempty"` AccessLevel *AccessLevelValue `url:"access_level,omitempty" json:"access_level,omitempty"` } // ProtectRepositoryTags protects a single repository tag or several project // repository tags using a wildcard protected tag. // // GitLab API docs: // https://docs.gitlab.com/ee/api/protected_tags.html#protect-repository-tags func (s *ProtectedTagsService) ProtectRepositoryTags(pid interface{}, opt *ProtectRepositoryTagsOptions, options ...RequestOptionFunc) (*ProtectedTag, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/protected_tags", PathEscape(project)) req, err := s.client.NewRequest(http.MethodPost, u, opt, options) if err != nil { return nil, nil, err } pt := new(ProtectedTag) resp, err := s.client.Do(req, pt) if err != nil { return nil, resp, err } return pt, resp, nil } // UnprotectRepositoryTags unprotects the given protected tag or wildcard // protected tag. // // GitLab API docs: // https://docs.gitlab.com/ee/api/protected_tags.html#unprotect-repository-tags func (s *ProtectedTagsService) UnprotectRepositoryTags(pid interface{}, tag string, options ...RequestOptionFunc) (*Response, error) { project, err := parseID(pid) if err != nil { return nil, err } u := fmt.Sprintf("projects/%s/protected_tags/%s", PathEscape(project), PathEscape(tag)) req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } golang-gitlab-gitlab-org-api-client-go-0.123.0/protected_tags_test.go000066400000000000000000000102531475761473200254550ustar00rootroot00000000000000// // Copyright 2021, Sander van Harmelen // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "fmt" "net/http" "testing" "github.com/stretchr/testify/assert" ) func TestListProtectedTags(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/protected_tags", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, `[{"name":"1.0.0", "create_access_levels": [{"access_level": 40, "access_level_description": "Maintainers"}]},{"name":"*-release", "create_access_levels": [{"access_level": 30, "access_level_description": "Developers + Maintainers"}]}]`) }) expected := []*ProtectedTag{ { Name: "1.0.0", CreateAccessLevels: []*TagAccessDescription{ { AccessLevel: 40, AccessLevelDescription: "Maintainers", }, }, }, { Name: "*-release", CreateAccessLevels: []*TagAccessDescription{ { AccessLevel: 30, AccessLevelDescription: "Developers + Maintainers", }, }, }, } opt := &ListProtectedTagsOptions{} tags, _, err := client.ProtectedTags.ListProtectedTags(1, opt) assert.NoError(t, err, "failed to get response") assert.Equal(t, expected, tags) } func TestGetProtectedTag(t *testing.T) { mux, client := setup(t) tagName := "my-awesome-tag" mux.HandleFunc(fmt.Sprintf("/api/v4/projects/1/protected_tags/%s", tagName), func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, `{"name":"my-awesome-tag", "create_access_levels": [{"access_level": 30, "access_level_description": "Developers + Maintainers"},{"access_level": 40, "access_level_description": "Sample Group", "group_id": 300}]}`) }) expected := &ProtectedTag{ Name: tagName, CreateAccessLevels: []*TagAccessDescription{ { AccessLevel: 30, AccessLevelDescription: "Developers + Maintainers", }, { AccessLevel: 40, GroupID: 300, AccessLevelDescription: "Sample Group", }, }, } tag, _, err := client.ProtectedTags.GetProtectedTag(1, tagName) assert.NoError(t, err, "failed to get response") assert.Equal(t, expected, tag) } func TestProtectRepositoryTags(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/protected_tags", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) fmt.Fprint(w, `{"name":"my-awesome-tag", "create_access_levels": [{"access_level": 30, "access_level_description": "Developers + Maintainers"},{"access_level": 40, "access_level_description": "Sample Group", "group_id": 300}]}`) }) expected := &ProtectedTag{ Name: "my-awesome-tag", CreateAccessLevels: []*TagAccessDescription{ { AccessLevel: 30, AccessLevelDescription: "Developers + Maintainers", }, { AccessLevel: 40, GroupID: 300, AccessLevelDescription: "Sample Group", }, }, } opt := &ProtectRepositoryTagsOptions{ Name: Ptr("my-awesome-tag"), CreateAccessLevel: Ptr(AccessLevelValue(30)), AllowedToCreate: &[]*TagsPermissionOptions{ { GroupID: Ptr(300), }, }, } tag, _, err := client.ProtectedTags.ProtectRepositoryTags(1, opt) assert.NoError(t, err, "failed to get response") assert.Equal(t, expected, tag) } func TestUnprotectRepositoryTags(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/protected_tags/my-awesome-tag", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodDelete) }) resp, err := client.ProtectedTags.UnprotectRepositoryTags(1, "my-awesome-tag") assert.NoError(t, err, "failed to get response") assert.Equal(t, http.StatusOK, resp.StatusCode) } golang-gitlab-gitlab-org-api-client-go-0.123.0/releaselinks.go000066400000000000000000000145671475761473200241040ustar00rootroot00000000000000// // Copyright 2021, Sander van Harmelen // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "fmt" "net/http" ) // ReleaseLinksService handles communication with the release link methods // of the GitLab API. // // GitLab API docs: https://docs.gitlab.com/ee/api/releases/links.html type ReleaseLinksService struct { client *Client } // ReleaseLink represents a release link. // // GitLab API docs: https://docs.gitlab.com/ee/api/releases/links.html type ReleaseLink struct { ID int `json:"id"` Name string `json:"name"` URL string `json:"url"` DirectAssetURL string `json:"direct_asset_url"` External bool `json:"external"` LinkType LinkTypeValue `json:"link_type"` } // ListReleaseLinksOptions represents ListReleaseLinks() options. // // GitLab API docs: https://docs.gitlab.com/ee/api/releases/links.html#list-links-of-a-release type ListReleaseLinksOptions ListOptions // ListReleaseLinks gets assets as links from a Release. // // GitLab API docs: https://docs.gitlab.com/ee/api/releases/links.html#list-links-of-a-release func (s *ReleaseLinksService) ListReleaseLinks(pid interface{}, tagName string, opt *ListReleaseLinksOptions, options ...RequestOptionFunc) ([]*ReleaseLink, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/releases/%s/assets/links", PathEscape(project), PathEscape(tagName)) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } var rls []*ReleaseLink resp, err := s.client.Do(req, &rls) if err != nil { return nil, resp, err } return rls, resp, nil } // GetReleaseLink returns a link from release assets. // // GitLab API docs: https://docs.gitlab.com/ee/api/releases/links.html#get-a-release-link func (s *ReleaseLinksService) GetReleaseLink(pid interface{}, tagName string, link int, options ...RequestOptionFunc) (*ReleaseLink, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/releases/%s/assets/links/%d", PathEscape(project), PathEscape(tagName), link) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } rl := new(ReleaseLink) resp, err := s.client.Do(req, rl) if err != nil { return nil, resp, err } return rl, resp, nil } // CreateReleaseLinkOptions represents CreateReleaseLink() options. // // GitLab API docs: https://docs.gitlab.com/ee/api/releases/links.html#create-a-release-link type CreateReleaseLinkOptions struct { Name *string `url:"name,omitempty" json:"name,omitempty"` URL *string `url:"url,omitempty" json:"url,omitempty"` FilePath *string `url:"filepath,omitempty" json:"filepath,omitempty"` DirectAssetPath *string `url:"direct_asset_path,omitempty" json:"direct_asset_path,omitempty"` LinkType *LinkTypeValue `url:"link_type,omitempty" json:"link_type,omitempty"` } // CreateReleaseLink creates a link. // // GitLab API docs: https://docs.gitlab.com/ee/api/releases/links.html#create-a-release-link func (s *ReleaseLinksService) CreateReleaseLink(pid interface{}, tagName string, opt *CreateReleaseLinkOptions, options ...RequestOptionFunc) (*ReleaseLink, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/releases/%s/assets/links", PathEscape(project), PathEscape(tagName)) req, err := s.client.NewRequest(http.MethodPost, u, opt, options) if err != nil { return nil, nil, err } rl := new(ReleaseLink) resp, err := s.client.Do(req, rl) if err != nil { return nil, resp, err } return rl, resp, nil } // UpdateReleaseLinkOptions represents UpdateReleaseLink() options. // // You have to specify at least one of Name of URL. // // GitLab API docs: https://docs.gitlab.com/ee/api/releases/links.html#update-a-release-link type UpdateReleaseLinkOptions struct { Name *string `url:"name,omitempty" json:"name,omitempty"` URL *string `url:"url,omitempty" json:"url,omitempty"` FilePath *string `url:"filepath,omitempty" json:"filepath,omitempty"` DirectAssetPath *string `url:"direct_asset_path,omitempty" json:"direct_asset_path,omitempty"` LinkType *LinkTypeValue `url:"link_type,omitempty" json:"link_type,omitempty"` } // UpdateReleaseLink updates an asset link. // // GitLab API docs: https://docs.gitlab.com/ee/api/releases/links.html#update-a-release-link func (s *ReleaseLinksService) UpdateReleaseLink(pid interface{}, tagName string, link int, opt *UpdateReleaseLinkOptions, options ...RequestOptionFunc) (*ReleaseLink, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/releases/%s/assets/links/%d", PathEscape(project), PathEscape(tagName), link) req, err := s.client.NewRequest(http.MethodPut, u, opt, options) if err != nil { return nil, nil, err } rl := new(ReleaseLink) resp, err := s.client.Do(req, rl) if err != nil { return nil, resp, err } return rl, resp, nil } // DeleteReleaseLink deletes a link from release. // // GitLab API docs: https://docs.gitlab.com/ee/api/releases/links.html#delete-a-release-link func (s *ReleaseLinksService) DeleteReleaseLink(pid interface{}, tagName string, link int, options ...RequestOptionFunc) (*ReleaseLink, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/releases/%s/assets/links/%d", PathEscape(project), PathEscape(tagName), link, ) req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) if err != nil { return nil, nil, err } rl := new(ReleaseLink) resp, err := s.client.Do(req, rl) if err != nil { return nil, resp, err } return rl, resp, nil } golang-gitlab-gitlab-org-api-client-go-0.123.0/releaselinks_test.go000066400000000000000000000132031475761473200251250ustar00rootroot00000000000000// // Copyright 2021, Sander van Harmelen // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "fmt" "net/http" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestReleaseLinksService_ListReleaseLinks(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/releases/v0.1/assets/links", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, exampleReleaseLinkList) }) releaseLinks, _, err := client.ReleaseLinks.ListReleaseLinks( 1, exampleTagName, &ListReleaseLinksOptions{}, ) require.NoError(t, err) expectedReleaseLinks := []*ReleaseLink{ { ID: 2, Name: "awesome-v0.2.msi", URL: "http://192.168.10.15:3000/msi", External: true, }, { ID: 1, Name: "awesome-v0.2.dmg", URL: "http://192.168.10.15:3000", DirectAssetURL: "http://192.168.10.15:3000/namespace/example/-/releases/v0.1/downloads/awesome-v0.2.dmg", External: false, LinkType: OtherLinkType, }, } assert.Equal(t, expectedReleaseLinks, releaseLinks) } func TestReleaseLinksService_CreateReleaseLink(t *testing.T) { testCases := []struct { description string options *CreateReleaseLinkOptions response string want *ReleaseLink }{ { description: "Mandatory Attributes", options: &CreateReleaseLinkOptions{ Name: Ptr("awesome-v0.2.dmg"), URL: Ptr("http://192.168.10.15:3000"), }, response: `{ "id":1, "name":"awesome-v0.2.dmg", "url":"http://192.168.10.15:3000", "external":true }`, want: &ReleaseLink{ ID: 1, Name: "awesome-v0.2.dmg", URL: "http://192.168.10.15:3000", External: true, }, }, { description: "Optional Attributes", options: &CreateReleaseLinkOptions{ Name: Ptr("release-notes.md"), URL: Ptr("http://192.168.10.15:3000"), DirectAssetPath: Ptr("docs/release-notes.md"), LinkType: Ptr(OtherLinkType), }, response: `{ "id":1, "name":"release-notes.md", "url":"http://192.168.10.15:3000", "direct_asset_url": "http://192.168.10.15:3000/namespace/example/-/releases/v0.1/downloads/docs/release-notes.md", "external": false, "link_type": "other" }`, want: &ReleaseLink{ ID: 1, Name: "release-notes.md", URL: "http://192.168.10.15:3000", DirectAssetURL: "http://192.168.10.15:3000/namespace/example/-/releases/v0.1/downloads/docs/release-notes.md", External: false, LinkType: OtherLinkType, }, }, } for _, tc := range testCases { t.Run(tc.description, func(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/releases/v0.1/assets/links", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) fmt.Fprint(w, tc.response) }) releaseLink, _, err := client.ReleaseLinks.CreateReleaseLink(1, exampleTagName, tc.options) require.NoError(t, err) assert.Equal(t, tc.want, releaseLink) }) } } func TestReleaseLinksService_GetReleaseLink(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/releases/v0.1/assets/links/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, exampleReleaseLink) }) releaseLink, _, err := client.ReleaseLinks.GetReleaseLink(1, exampleTagName, 1) if err != nil { t.Error(err) } if releaseLink.Name != exampleReleaseName { t.Errorf("release link name, expected '%s', got '%s'", exampleReleaseName, releaseLink.Name) } } func TestReleaseLinksService_UpdateReleaseLink(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/releases/v0.1/assets/links/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPut) fmt.Fprint(w, exampleReleaseLink) }) releaseLink, _, err := client.ReleaseLinks.UpdateReleaseLink( 1, exampleTagName, 1, &UpdateReleaseLinkOptions{ Name: Ptr(exampleReleaseName), DirectAssetPath: Ptr("http://192.168.10.15:3000/namespace/example/-/releases/v0.1/downloads/awesome-v0.2.dmg"), LinkType: Ptr(OtherLinkType), }) require.NoError(t, err) expectedRelease := &ReleaseLink{ ID: 1, Name: "awesome-v0.2.dmg", URL: "http://192.168.10.15:3000", DirectAssetURL: "http://192.168.10.15:3000/namespace/example/-/releases/v0.1/downloads/awesome-v0.2.dmg", External: true, LinkType: OtherLinkType, } assert.Equal(t, expectedRelease, releaseLink) } func TestReleaseLinksService_DeleteReleaseLink(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/releases/v0.1/assets/links/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodDelete) fmt.Fprint(w, exampleReleaseLink) }) releaseLink, _, err := client.ReleaseLinks.DeleteReleaseLink(1, exampleTagName, 1) if err != nil { t.Error(err) } if releaseLink.Name != exampleReleaseName { t.Errorf("release link name, expected '%s', got '%s'", exampleReleaseName, releaseLink.Name) } } golang-gitlab-gitlab-org-api-client-go-0.123.0/releases.go000066400000000000000000000252661475761473200232240ustar00rootroot00000000000000// // Copyright 2021, Sander van Harmelen // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "fmt" "net/http" "time" ) // ReleasesService handles communication with the releases methods // of the GitLab API. // // GitLab API docs: https://docs.gitlab.com/ee/api/releases/index.html type ReleasesService struct { client *Client } // Release represents a project release. // // GitLab API docs: // https://docs.gitlab.com/ee/api/releases/index.html#list-releases type Release struct { TagName string `json:"tag_name"` Name string `json:"name"` Description string `json:"description"` DescriptionHTML string `json:"description_html"` CreatedAt *time.Time `json:"created_at"` ReleasedAt *time.Time `json:"released_at"` Author struct { ID int `json:"id"` Name string `json:"name"` Username string `json:"username"` State string `json:"state"` AvatarURL string `json:"avatar_url"` WebURL string `json:"web_url"` } `json:"author"` Commit Commit `json:"commit"` Milestones []*ReleaseMilestone `json:"milestones"` UpcomingRelease bool `json:"upcoming_release"` CommitPath string `json:"commit_path"` TagPath string `json:"tag_path"` Assets struct { Count int `json:"count"` Sources []struct { Format string `json:"format"` URL string `json:"url"` } `json:"sources"` Links []*ReleaseLink `json:"links"` EvidenceFilePath string `json:"evidence_file_path"` } `json:"assets"` Evidences []*ReleaseEvidence `json:"evidences"` Links struct { ClosedIssueURL string `json:"closed_issues_url"` ClosedMergeRequest string `json:"closed_merge_requests_url"` EditURL string `json:"edit_url"` MergedMergeRequest string `json:"merged_merge_requests_url"` OpenedIssues string `json:"opened_issues_url"` OpenedMergeRequest string `json:"opened_merge_requests_url"` Self string `json:"self"` } `json:"_links"` } // ReleaseMilestone represents a project release milestone. // // GitLab API docs: // https://docs.gitlab.com/ee/api/releases/index.html#list-releases type ReleaseMilestone struct { ID int `json:"id"` IID int `json:"iid"` ProjectID int `json:"project_id"` Title string `json:"title"` Description string `json:"description"` State string `json:"state"` CreatedAt *time.Time `json:"created_at"` UpdatedAt *time.Time `json:"updated_at"` DueDate *ISOTime `json:"due_date"` StartDate *ISOTime `json:"start_date"` WebURL string `json:"web_url"` IssueStats *ReleaseMilestoneIssueStats `json:"issue_stats"` } // ReleaseMilestoneIssueStats represents a project release milestone's // related issues statistics. // // GitLab API docs: // https://docs.gitlab.com/ee/api/releases/index.html#list-releases type ReleaseMilestoneIssueStats struct { Total int `json:"total"` Closed int `json:"closed"` } // ReleaseEvidence represents a project release's evidence. // // GitLab API docs: // https://docs.gitlab.com/ee/api/releases/index.html#list-releases type ReleaseEvidence struct { SHA string `json:"sha"` Filepath string `json:"filepath"` CollectedAt *time.Time `json:"collected_at"` } // ListReleasesOptions represents ListReleases() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/releases/index.html#list-releases type ListReleasesOptions struct { ListOptions OrderBy *string `url:"order_by,omitempty" json:"order_by,omitempty"` Sort *string `url:"sort,omitempty" json:"sort,omitempty"` IncludeHTMLDescription *bool `url:"include_html_description,omitempty" json:"include_html_description,omitempty"` } // ListReleases gets a pagenated of releases accessible by the authenticated user. // // GitLab API docs: // https://docs.gitlab.com/ee/api/releases/index.html#list-releases func (s *ReleasesService) ListReleases(pid interface{}, opt *ListReleasesOptions, options ...RequestOptionFunc) ([]*Release, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/releases", PathEscape(project)) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } var rs []*Release resp, err := s.client.Do(req, &rs) if err != nil { return nil, resp, err } return rs, resp, nil } // GetRelease returns a single release, identified by a tag name. // // GitLab API docs: // https://docs.gitlab.com/ee/api/releases/index.html#get-a-release-by-a-tag-name func (s *ReleasesService) GetRelease(pid interface{}, tagName string, options ...RequestOptionFunc) (*Release, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/releases/%s", PathEscape(project), PathEscape(tagName)) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } r := new(Release) resp, err := s.client.Do(req, r) if err != nil { return nil, resp, err } return r, resp, nil } // GetLatestRelease returns the latest release for the project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/releases/#get-the-latest-release func (s *ReleasesService) GetLatestRelease(pid interface{}, options ...RequestOptionFunc) (*Release, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/releases/permalink/latest", PathEscape(project)) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } r := new(Release) resp, err := s.client.Do(req, r) if err != nil { return nil, resp, err } return r, resp, err } // CreateReleaseOptions represents CreateRelease() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/releases/index.html#create-a-release type CreateReleaseOptions struct { Name *string `url:"name,omitempty" json:"name,omitempty"` TagName *string `url:"tag_name,omitempty" json:"tag_name,omitempty"` TagMessage *string `url:"tag_message,omitempty" json:"tag_message,omitempty"` Description *string `url:"description,omitempty" json:"description,omitempty"` Ref *string `url:"ref,omitempty" json:"ref,omitempty"` Milestones *[]string `url:"milestones,omitempty" json:"milestones,omitempty"` Assets *ReleaseAssetsOptions `url:"assets,omitempty" json:"assets,omitempty"` ReleasedAt *time.Time `url:"released_at,omitempty" json:"released_at,omitempty"` } // ReleaseAssetsOptions represents release assets in CreateRelease() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/releases/index.html#create-a-release type ReleaseAssetsOptions struct { Links []*ReleaseAssetLinkOptions `url:"links,omitempty" json:"links,omitempty"` } // ReleaseAssetLinkOptions represents release asset link in CreateRelease() // options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/releases/index.html#create-a-release type ReleaseAssetLinkOptions struct { Name *string `url:"name,omitempty" json:"name,omitempty"` URL *string `url:"url,omitempty" json:"url,omitempty"` FilePath *string `url:"filepath,omitempty" json:"filepath,omitempty"` DirectAssetPath *string `url:"direct_asset_path,omitempty" json:"direct_asset_path,omitempty"` LinkType *LinkTypeValue `url:"link_type,omitempty" json:"link_type,omitempty"` } // CreateRelease creates a release. // // GitLab API docs: // https://docs.gitlab.com/ee/api/releases/index.html#create-a-release func (s *ReleasesService) CreateRelease(pid interface{}, opts *CreateReleaseOptions, options ...RequestOptionFunc) (*Release, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/releases", PathEscape(project)) req, err := s.client.NewRequest(http.MethodPost, u, opts, options) if err != nil { return nil, nil, err } r := new(Release) resp, err := s.client.Do(req, r) if err != nil { return nil, resp, err } return r, resp, nil } // UpdateReleaseOptions represents UpdateRelease() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/releases/index.html#update-a-release type UpdateReleaseOptions struct { Name *string `url:"name" json:"name"` Description *string `url:"description" json:"description"` Milestones *[]string `url:"milestones,omitempty" json:"milestones,omitempty"` ReleasedAt *time.Time `url:"released_at,omitempty" json:"released_at,omitempty"` } // UpdateRelease updates a release. // // GitLab API docs: // https://docs.gitlab.com/ee/api/releases/index.html#update-a-release func (s *ReleasesService) UpdateRelease(pid interface{}, tagName string, opts *UpdateReleaseOptions, options ...RequestOptionFunc) (*Release, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/releases/%s", PathEscape(project), PathEscape(tagName)) req, err := s.client.NewRequest(http.MethodPut, u, opts, options) if err != nil { return nil, nil, err } r := new(Release) resp, err := s.client.Do(req, &r) if err != nil { return nil, resp, err } return r, resp, nil } // DeleteRelease deletes a release. // // GitLab API docs: // https://docs.gitlab.com/ee/api/releases/index.html#delete-a-release func (s *ReleasesService) DeleteRelease(pid interface{}, tagName string, options ...RequestOptionFunc) (*Release, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/releases/%s", PathEscape(project), PathEscape(tagName)) req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) if err != nil { return nil, nil, err } r := new(Release) resp, err := s.client.Do(req, r) if err != nil { return nil, resp, err } return r, resp, nil } golang-gitlab-gitlab-org-api-client-go-0.123.0/releases_test.go000066400000000000000000000274111475761473200242550ustar00rootroot00000000000000// // Copyright 2021, Sander van Harmelen // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "fmt" "io" "net/http" "strings" "testing" "time" ) func TestReleasesService_ListReleases(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/releases", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, exampleReleaseListResponse) }) opt := &ListReleasesOptions{} releases, _, err := client.Releases.ListReleases(1, opt) if err != nil { t.Error(err) } if len(releases) != 2 { t.Error("expected 2 releases") } } func TestReleasesService_GetRelease(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/releases/v0.1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, exampleReleaseResponse) }) release, _, err := client.Releases.GetRelease(1, exampleTagName) if err != nil { t.Error(err) } if release.TagName != exampleTagName { t.Errorf("expected tag %s, got %s", exampleTagName, release.TagName) } } func TestReleasesService_CreateRelease(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/releases", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) b, err := io.ReadAll(r.Body) if err != nil { t.Fatalf("unable to read request body") } if !strings.Contains(string(b), exampleTagName) { t.Errorf("expected request body to contain %s, got %s", exampleTagName, string(b)) } if strings.Contains(string(b), "assets") { t.Errorf("expected request body not to have assets, got %s", string(b)) } if strings.Contains(string(b), "milestones") { t.Errorf("expected request body not to have milestones, got %s", string(b)) } if strings.Contains(string(b), "released_at") { t.Errorf("expected request body not to have released_at, got %s", string(b)) } fmt.Fprint(w, exampleReleaseResponse) }) opts := &CreateReleaseOptions{ Name: Ptr("name"), TagName: Ptr(exampleTagName), Description: Ptr("Description"), } release, _, err := client.Releases.CreateRelease(1, opts) if err != nil { t.Error(err) } if release.TagName != exampleTagName { t.Errorf("expected tag %s, got %s", exampleTagName, release.TagName) } } func TestReleasesService_CreateReleaseWithAsset(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/releases", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) b, err := io.ReadAll(r.Body) if err != nil { t.Fatalf("unable to read request body") } if !strings.Contains(string(b), exampleTagName) { t.Errorf("expected request body to contain %s, got %s", exampleTagName, string(b)) } if !strings.Contains(string(b), "assets") { t.Errorf("expected request body to have assets, got %s", string(b)) } if strings.Contains(string(b), "milestones") { t.Errorf("expected request body not to have milestones, got %s", string(b)) } if strings.Contains(string(b), "released_at") { t.Errorf("expected request body not to have released_at, got %s", string(b)) } fmt.Fprint(w, exampleReleaseResponse) }) opts := &CreateReleaseOptions{ Name: Ptr("name"), TagName: Ptr(exampleTagName), Description: Ptr("Description"), Assets: &ReleaseAssetsOptions{ Links: []*ReleaseAssetLinkOptions{ {Ptr("sldkf"), Ptr("sldkfj"), Ptr("sldkfh"), Ptr("direct-asset-path"), Ptr(OtherLinkType)}, }, }, } release, _, err := client.Releases.CreateRelease(1, opts) if err != nil { t.Error(err) } if release.TagName != exampleTagName { t.Errorf("expected tag %s, got %s", exampleTagName, release.TagName) } } func TestReleasesService_CreateReleaseWithAssetAndNameMetadata(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/releases", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) b, err := io.ReadAll(r.Body) if err != nil { t.Fatalf("unable to read request body") } if !strings.Contains(string(b), exampleTagNameWithMetadata) { t.Errorf("expected request body to contain %s, got %s", exampleTagNameWithMetadata, string(b)) } if !strings.Contains(string(b), "assets") { t.Errorf("expected request body to have assets, got %s", string(b)) } if strings.Contains(string(b), "milestones") { t.Errorf("expected request body not to have milestones, got %s", string(b)) } if strings.Contains(string(b), "released_at") { t.Errorf("expected request body not to have released_at, got %s", string(b)) } fmt.Fprint(w, exampleReleaseWithMetadataResponse) }) opts := &CreateReleaseOptions{ Name: Ptr("name"), TagName: Ptr(exampleTagNameWithMetadata), Description: Ptr("Description"), Assets: &ReleaseAssetsOptions{ Links: []*ReleaseAssetLinkOptions{ {Ptr("sldkf"), Ptr("sldkfj"), Ptr("sldkfh"), Ptr("direct-asset-path"), Ptr(OtherLinkType)}, }, }, } release, _, err := client.Releases.CreateRelease(1, opts) if err != nil { t.Error(err) } if release.TagName != exampleTagNameWithMetadata { t.Errorf("expected tag %s, got %s", exampleTagNameWithMetadata, release.TagName) } } func TestReleasesService_CreateReleaseWithMilestones(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/releases", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) b, err := io.ReadAll(r.Body) if err != nil { t.Fatalf("unable to read request body") } if !strings.Contains(string(b), exampleTagName) { t.Errorf("expected request body to contain %s, got %s", exampleTagName, string(b)) } if strings.Contains(string(b), "assets") { t.Errorf("expected request body not to have assets, got %s", string(b)) } if !strings.Contains(string(b), "milestones") { t.Errorf("expected request body to have milestones, got %s", string(b)) } if strings.Contains(string(b), "released_at") { t.Errorf("expected request body not to have released_at, got %s", string(b)) } fmt.Fprint(w, exampleReleaseResponse) }) opts := &CreateReleaseOptions{ Name: Ptr("name"), TagName: Ptr(exampleTagName), Description: Ptr("Description"), Milestones: &[]string{exampleTagName, "v0.1.0"}, } release, _, err := client.Releases.CreateRelease(1, opts) if err != nil { t.Error(err) } if release.TagName != exampleTagName { t.Errorf("expected tag %s, got %s", exampleTagName, release.TagName) } } func TestReleasesService_CreateReleaseWithReleasedAt(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/releases", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) b, err := io.ReadAll(r.Body) if err != nil { t.Fatalf("unable to read request body") } if !strings.Contains(string(b), exampleTagName) { t.Errorf("expected request body to contain %s, got %s", exampleTagName, string(b)) } if strings.Contains(string(b), "assets") { t.Errorf("expected request body not to have assets, got %s", string(b)) } if strings.Contains(string(b), "milestones") { t.Errorf("expected request body not to have milestones, got %s", string(b)) } if !strings.Contains(string(b), "released_at") { t.Errorf("expected request body to have released_at, got %s", string(b)) } fmt.Fprint(w, exampleReleaseResponse) }) opts := &CreateReleaseOptions{ Name: Ptr("name"), TagName: Ptr(exampleTagName), Description: Ptr("Description"), ReleasedAt: &time.Time{}, } release, _, err := client.Releases.CreateRelease(1, opts) if err != nil { t.Error(err) } if release.TagName != exampleTagName { t.Errorf("expected tag %s, got %s", exampleTagName, release.TagName) } } func TestReleasesService_UpdateRelease(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/releases/v0.1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPut) b, err := io.ReadAll(r.Body) if err != nil { t.Fatalf("unable to read request body") } if strings.Contains(string(b), "milestones") { t.Errorf("expected request body not to have milestones, got %s", string(b)) } if strings.Contains(string(b), "released_at") { t.Errorf("expected request body not to have released_at, got %s", string(b)) } fmt.Fprint(w, exampleReleaseResponse) }) opts := &UpdateReleaseOptions{ Name: Ptr("name"), Description: Ptr("Description"), } release, _, err := client.Releases.UpdateRelease(1, exampleTagName, opts) if err != nil { t.Error(err) } if release.TagName != exampleTagName { t.Errorf("expected tag %s, got %s", exampleTagName, release.TagName) } } func TestReleasesService_UpdateReleaseWithMilestones(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/releases/v0.1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPut) b, err := io.ReadAll(r.Body) if err != nil { t.Fatalf("unable to read request body") } if !strings.Contains(string(b), "milestones") { t.Errorf("expected request body to have milestones, got %s", string(b)) } if strings.Contains(string(b), "released_at") { t.Errorf("expected request body not to have released_at, got %s", string(b)) } fmt.Fprint(w, exampleReleaseResponse) }) opts := &UpdateReleaseOptions{ Name: Ptr("name"), Description: Ptr("Description"), Milestones: &[]string{exampleTagName, "v0.1.0"}, } release, _, err := client.Releases.UpdateRelease(1, exampleTagName, opts) if err != nil { t.Error(err) } if release.TagName != exampleTagName { t.Errorf("expected tag %s, got %s", exampleTagName, release.TagName) } } func TestReleasesService_UpdateReleaseWithReleasedAt(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/releases/v0.1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPut) b, err := io.ReadAll(r.Body) if err != nil { t.Fatalf("unable to read request body") } if strings.Contains(string(b), "milestones") { t.Errorf("expected request body not to have milestones, got %s", string(b)) } if !strings.Contains(string(b), "released_at") { t.Errorf("expected request body to have released_at, got %s", string(b)) } fmt.Fprint(w, exampleReleaseResponse) }) opts := &UpdateReleaseOptions{ Name: Ptr("name"), Description: Ptr("Description"), ReleasedAt: &time.Time{}, } release, _, err := client.Releases.UpdateRelease(1, exampleTagName, opts) if err != nil { t.Error(err) } if release.TagName != exampleTagName { t.Errorf("expected tag %s, got %s", exampleTagName, release.TagName) } } func TestReleasesService_DeleteRelease(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/releases/v0.1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodDelete) fmt.Fprint(w, exampleReleaseResponse) }) release, _, err := client.Releases.DeleteRelease(1, exampleTagName) if err != nil { t.Error(err) } if release.TagName != exampleTagName { t.Errorf("expected tag %s, got %s", exampleTagName, release.TagName) } } golang-gitlab-gitlab-org-api-client-go-0.123.0/repositories.go000066400000000000000000000311561475761473200241430ustar00rootroot00000000000000// // Copyright 2021, Sander van Harmelen // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "bytes" "fmt" "io" "net/http" "net/url" ) // RepositoriesService handles communication with the repositories related // methods of the GitLab API. // // GitLab API docs: https://docs.gitlab.com/ee/api/repositories.html type RepositoriesService struct { client *Client } // TreeNode represents a GitLab repository file or directory. // // GitLab API docs: https://docs.gitlab.com/ee/api/repositories.html type TreeNode struct { ID string `json:"id"` Name string `json:"name"` Type string `json:"type"` Path string `json:"path"` Mode string `json:"mode"` } func (t TreeNode) String() string { return Stringify(t) } // ListTreeOptions represents the available ListTree() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/repositories.html#list-repository-tree type ListTreeOptions struct { ListOptions Path *string `url:"path,omitempty" json:"path,omitempty"` Ref *string `url:"ref,omitempty" json:"ref,omitempty"` Recursive *bool `url:"recursive,omitempty" json:"recursive,omitempty"` } // ListTree gets a list of repository files and directories in a project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/repositories.html#list-repository-tree func (s *RepositoriesService) ListTree(pid interface{}, opt *ListTreeOptions, options ...RequestOptionFunc) ([]*TreeNode, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/repository/tree", PathEscape(project)) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } var t []*TreeNode resp, err := s.client.Do(req, &t) if err != nil { return nil, resp, err } return t, resp, nil } // Blob gets information about blob in repository like size and content. Note // that blob content is Base64 encoded. // // GitLab API docs: // https://docs.gitlab.com/ee/api/repositories.html#get-a-blob-from-repository func (s *RepositoriesService) Blob(pid interface{}, sha string, options ...RequestOptionFunc) ([]byte, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/repository/blobs/%s", PathEscape(project), url.PathEscape(sha)) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } var b bytes.Buffer resp, err := s.client.Do(req, &b) if err != nil { return nil, resp, err } return b.Bytes(), resp, err } // RawBlobContent gets the raw file contents for a blob by blob SHA. // // GitLab API docs: // https://docs.gitlab.com/ee/api/repositories.html#raw-blob-content func (s *RepositoriesService) RawBlobContent(pid interface{}, sha string, options ...RequestOptionFunc) ([]byte, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/repository/blobs/%s/raw", PathEscape(project), url.PathEscape(sha)) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } var b bytes.Buffer resp, err := s.client.Do(req, &b) if err != nil { return nil, resp, err } return b.Bytes(), resp, err } // ArchiveOptions represents the available Archive() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/repositories.html#get-file-archive type ArchiveOptions struct { Format *string `url:"-" json:"-"` Path *string `url:"path,omitempty" json:"path,omitempty"` SHA *string `url:"sha,omitempty" json:"sha,omitempty"` } // Archive gets an archive of the repository. // // GitLab API docs: // https://docs.gitlab.com/ee/api/repositories.html#get-file-archive func (s *RepositoriesService) Archive(pid interface{}, opt *ArchiveOptions, options ...RequestOptionFunc) ([]byte, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/repository/archive", PathEscape(project)) // Set an optional format for the archive. if opt != nil && opt.Format != nil { u = fmt.Sprintf("%s.%s", u, *opt.Format) } req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } var b bytes.Buffer resp, err := s.client.Do(req, &b) if err != nil { return nil, resp, err } return b.Bytes(), resp, err } // StreamArchive streams an archive of the repository to the provided // io.Writer. // // GitLab API docs: // https://docs.gitlab.com/ee/api/repositories.html#get-file-archive func (s *RepositoriesService) StreamArchive(pid interface{}, w io.Writer, opt *ArchiveOptions, options ...RequestOptionFunc) (*Response, error) { project, err := parseID(pid) if err != nil { return nil, err } u := fmt.Sprintf("projects/%s/repository/archive", PathEscape(project)) // Set an optional format for the archive. if opt != nil && opt.Format != nil { u = fmt.Sprintf("%s.%s", u, *opt.Format) } req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, err } return s.client.Do(req, w) } // Compare represents the result of a comparison of branches, tags or commits. // // GitLab API docs: // https://docs.gitlab.com/ee/api/repositories.html#compare-branches-tags-or-commits type Compare struct { Commit *Commit `json:"commit"` Commits []*Commit `json:"commits"` Diffs []*Diff `json:"diffs"` CompareTimeout bool `json:"compare_timeout"` CompareSameRef bool `json:"compare_same_ref"` WebURL string `json:"web_url"` } func (c Compare) String() string { return Stringify(c) } // CompareOptions represents the available Compare() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/repositories.html#compare-branches-tags-or-commits type CompareOptions struct { From *string `url:"from,omitempty" json:"from,omitempty"` To *string `url:"to,omitempty" json:"to,omitempty"` Straight *bool `url:"straight,omitempty" json:"straight,omitempty"` Unidiff *bool `url:"unidiff,omitempty" json:"unidiff,omitempty"` } // Compare compares branches, tags or commits. // // GitLab API docs: // https://docs.gitlab.com/ee/api/repositories.html#compare-branches-tags-or-commits func (s *RepositoriesService) Compare(pid interface{}, opt *CompareOptions, options ...RequestOptionFunc) (*Compare, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/repository/compare", PathEscape(project)) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } c := new(Compare) resp, err := s.client.Do(req, c) if err != nil { return nil, resp, err } return c, resp, nil } // Contributor represents a GitLap contributor. // // GitLab API docs: https://docs.gitlab.com/ee/api/repositories.html#contributors type Contributor struct { Name string `json:"name"` Email string `json:"email"` Commits int `json:"commits"` Additions int `json:"additions"` Deletions int `json:"deletions"` } func (c Contributor) String() string { return Stringify(c) } // ListContributorsOptions represents the available ListContributors() options. // // GitLab API docs: https://docs.gitlab.com/ee/api/repositories.html#contributors type ListContributorsOptions struct { ListOptions OrderBy *string `url:"order_by,omitempty" json:"order_by,omitempty"` Sort *string `url:"sort,omitempty" json:"sort,omitempty"` } // Contributors gets the repository contributors list. // // GitLab API docs: https://docs.gitlab.com/ee/api/repositories.html#contributors func (s *RepositoriesService) Contributors(pid interface{}, opt *ListContributorsOptions, options ...RequestOptionFunc) ([]*Contributor, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/repository/contributors", PathEscape(project)) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } var c []*Contributor resp, err := s.client.Do(req, &c) if err != nil { return nil, resp, err } return c, resp, nil } // MergeBaseOptions represents the available MergeBase() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/repositories.html#merge-base type MergeBaseOptions struct { Ref *[]string `url:"refs[],omitempty" json:"refs,omitempty"` } // MergeBase gets the common ancestor for 2 refs (commit SHAs, branch // names or tags). // // GitLab API docs: // https://docs.gitlab.com/ee/api/repositories.html#merge-base func (s *RepositoriesService) MergeBase(pid interface{}, opt *MergeBaseOptions, options ...RequestOptionFunc) (*Commit, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/repository/merge_base", PathEscape(project)) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } c := new(Commit) resp, err := s.client.Do(req, c) if err != nil { return nil, resp, err } return c, resp, nil } // AddChangelogOptions represents the available AddChangelog() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/repositories.html#add-changelog-data-to-a-changelog-file type AddChangelogOptions struct { Version *string `url:"version,omitempty" json:"version,omitempty"` Branch *string `url:"branch,omitempty" json:"branch,omitempty"` ConfigFile *string `url:"config_file,omitempty" json:"config_file,omitempty"` Date *ISOTime `url:"date,omitempty" json:"date,omitempty"` File *string `url:"file,omitempty" json:"file,omitempty"` From *string `url:"from,omitempty" json:"from,omitempty"` Message *string `url:"message,omitempty" json:"message,omitempty"` To *string `url:"to,omitempty" json:"to,omitempty"` Trailer *string `url:"trailer,omitempty" json:"trailer,omitempty"` } // AddChangelog generates changelog data based on commits in a repository. // // Gitlab API docs: // https://docs.gitlab.com/ee/api/repositories.html#add-changelog-data-to-a-changelog-file func (s *RepositoriesService) AddChangelog(pid interface{}, opt *AddChangelogOptions, options ...RequestOptionFunc) (*Response, error) { project, err := parseID(pid) if err != nil { return nil, err } u := fmt.Sprintf("projects/%s/repository/changelog", PathEscape(project)) req, err := s.client.NewRequest(http.MethodPost, u, opt, options) if err != nil { return nil, err } return s.client.Do(req, nil) } // ChangelogData represents the generated changelog data. // // GitLab API docs: // https://docs.gitlab.com/ee/api/repositories.html#generate-changelog-data type ChangelogData struct { Notes string `json:"notes"` } func (c ChangelogData) String() string { return Stringify(c) } // GenerateChangelogDataOptions represents the available GenerateChangelogData() // options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/repositories.html#generate-changelog-data type GenerateChangelogDataOptions struct { Version *string `url:"version,omitempty" json:"version,omitempty"` ConfigFile *string `url:"config_file,omitempty" json:"config_file,omitempty"` Date *ISOTime `url:"date,omitempty" json:"date,omitempty"` From *string `url:"from,omitempty" json:"from,omitempty"` To *string `url:"to,omitempty" json:"to,omitempty"` Trailer *string `url:"trailer,omitempty" json:"trailer,omitempty"` } // GenerateChangelogData generates changelog data based on commits in a // repository, without committing them to a changelog file. // // Gitlab API docs: // https://docs.gitlab.com/ee/api/repositories.html#generate-changelog-data func (s *RepositoriesService) GenerateChangelogData(pid interface{}, opt GenerateChangelogDataOptions, options ...RequestOptionFunc) (*ChangelogData, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/repository/changelog", project) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } cd := new(ChangelogData) resp, err := s.client.Do(req, cd) if err != nil { return nil, resp, err } return cd, resp, nil } golang-gitlab-gitlab-org-api-client-go-0.123.0/repositories_test.go000066400000000000000000000334041475761473200252000ustar00rootroot00000000000000package gitlab import ( "fmt" "net/http" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestRepositoriesService_ListTree(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/repository/tree", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprintf(w, ` [ { "id": "a1e8f8d745cc87e3a9248358d9352bb7f9a0aeba", "name": "html", "type": "tree", "path": "files/html", "mode": "040000" } ] `) }) want := []*TreeNode{ { ID: "a1e8f8d745cc87e3a9248358d9352bb7f9a0aeba", Name: "html", Type: "tree", Path: "files/html", Mode: "040000", }, } lto := ListTreeOptions{ ListOptions: ListOptions{ PerPage: 1, PageToken: "a1e8f8d745cc87e3a9248358d9352bb7f9a0aeba", }, } tns, resp, err := client.Repositories.ListTree(1, nil) require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, want, tns) tns, resp, err = client.Repositories.ListTree(1.01, nil) require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string") require.Nil(t, resp) require.Nil(t, tns) tns, resp, err = client.Repositories.ListTree(1, nil, errorOption) require.EqualError(t, err, "RequestOptionFunc returns an error") require.Nil(t, resp) require.Nil(t, tns) tns, resp, err = client.Repositories.ListTree(2, nil) require.Error(t, err) require.Nil(t, tns) require.Equal(t, http.StatusNotFound, resp.StatusCode) tns, resp, err = client.Repositories.ListTree(1, <o) require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, want, tns) } func TestRepositoriesService_Blob(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/repository/blobs/2dc6aa325a317eda67812f05600bdf0fcdc70ab0", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprintf(w, "{"+ "size: 100"+ "content: content"+ "}", ) }) want := []byte("{" + "size: 100" + "content: content" + "}") b, resp, err := client.Repositories.Blob(1, "2dc6aa325a317eda67812f05600bdf0fcdc70ab0", nil) require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, want, b) b, resp, err = client.Repositories.Blob(1.01, "2dc6aa325a317eda67812f05600bdf0fcdc70ab0", nil) require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string") require.Nil(t, resp) require.Nil(t, b) b, resp, err = client.Repositories.Blob(1, "2dc6aa325a317eda67812f05600bdf0fcdc70ab0", nil, errorOption) require.EqualError(t, err, "RequestOptionFunc returns an error") require.Nil(t, resp) require.Nil(t, b) b, resp, err = client.Repositories.Blob(2, "2dc6aa325a317eda67812f05600bdf0fcdc70ab0", nil) require.Error(t, err) require.Nil(t, b) require.Equal(t, http.StatusNotFound, resp.StatusCode) } func TestRepositoriesService_RawBlobContent(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/repository/blobs/2dc6aa325a317eda67812f05600bdf0fcdc70ab0/raw", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprintf(w, "{"+ "size: 100"+ "content: content"+ "}", ) }) want := []byte("{" + "size: 100" + "content: content" + "}") b, resp, err := client.Repositories.RawBlobContent(1, "2dc6aa325a317eda67812f05600bdf0fcdc70ab0", nil) require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, want, b) b, resp, err = client.Repositories.RawBlobContent(1.01, "2dc6aa325a317eda67812f05600bdf0fcdc70ab0", nil) require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string") require.Nil(t, resp) require.Nil(t, b) b, resp, err = client.Repositories.RawBlobContent(1, "2dc6aa325a317eda67812f05600bdf0fcdc70ab0", nil, errorOption) require.EqualError(t, err, "RequestOptionFunc returns an error") require.Nil(t, resp) require.Nil(t, b) b, resp, err = client.Repositories.RawBlobContent(2, "2dc6aa325a317eda67812f05600bdf0fcdc70ab0", nil) require.Error(t, err) require.Nil(t, b) require.Equal(t, http.StatusNotFound, resp.StatusCode) } func TestRepositoriesService_Archive(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/repository/archive.gz", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprintf(w, "{"+ "size: 100"+ "content: content"+ "}", ) }) opt := &ArchiveOptions{Format: Ptr("gz")} want := []byte("{" + "size: 100" + "content: content" + "}") b, resp, err := client.Repositories.Archive(1, opt, nil) require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, want, b) b, resp, err = client.Repositories.Archive(1.01, opt, nil) require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string") require.Nil(t, resp) require.Nil(t, b) b, resp, err = client.Repositories.Archive(1, opt, nil, errorOption) require.EqualError(t, err, "RequestOptionFunc returns an error") require.Nil(t, resp) require.Nil(t, b) b, resp, err = client.Repositories.Archive(2, opt, nil) require.Error(t, err) require.Nil(t, b) require.Equal(t, http.StatusNotFound, resp.StatusCode) } func TestRepositoriesService_StreamArchive(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/repository/archive.gz", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) }) var w http.ResponseWriter opt := &ArchiveOptions{Format: Ptr("gz")} resp, err := client.Repositories.StreamArchive(1, w, opt, nil) require.NoError(t, err) require.NotNil(t, resp) resp, err = client.Repositories.StreamArchive(1.01, w, opt, nil) require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string") require.Nil(t, resp) resp, err = client.Repositories.StreamArchive(1, w, opt, nil, errorOption) require.EqualError(t, err, "RequestOptionFunc returns an error") require.Nil(t, resp) resp, err = client.Repositories.StreamArchive(2, w, opt, nil) require.Error(t, err) require.Equal(t, http.StatusNotFound, resp.StatusCode) } func TestRepositoriesService_Compare(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/12d65c8dd2b2676fa3ac47d955accc085a37a9c1/repository/compare", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprintf(w, ` { "commit": { "id": "12d65c8dd2b2676fa3ac47d955accc085a37a9c1", "short_id": "12d65c8dd2b", "title": "JS fix", "author_name": "Example User", "author_email": "user@example.com" }, "commits": [{ "id": "12d65c8dd2b2676fa3ac47d955accc085a37a9c1", "short_id": "12d65c8dd2b", "title": "JS fix", "author_name": "Example User", "author_email": "user@example.com" }], "diffs": [{ "old_path": "files/js/application.js", "new_path": "files/js/application.js", "a_mode": null, "b_mode": "100644", "diff": "--- a/files/js/application.js\n+++ c/files/js/application.js\n@@ -24,8 +24,10 @@\n //= require g.raphael-min\n //= require g.bar-min\n //= require branch-graph\n-//= require highlightjs.min\n-//= require ace/ace\n //= require_tree .\n //= require d3\n //= require underscore\n+\n+function fix() { \n+ alert(\"Fixed\")\n+}", "new_file": false, "renamed_file": false, "deleted_file": false }], "compare_timeout": false, "compare_same_ref": false, "web_url": "https://gitlab.example.com/thedude/gitlab-foss/-/compare/ae73cb07c9eeaf35924a10f713b364d32b2dd34f...0b4bc9a49b562e85de7cc9e834518ea6828729b9" } `) }) opt := &CompareOptions{ From: Ptr("master"), To: Ptr("feature"), } want := &Compare{ Commit: &Commit{ ID: "12d65c8dd2b2676fa3ac47d955accc085a37a9c1", ShortID: "12d65c8dd2b", Title: "JS fix", AuthorName: "Example User", AuthorEmail: "user@example.com", }, Commits: []*Commit{{ ID: "12d65c8dd2b2676fa3ac47d955accc085a37a9c1", ShortID: "12d65c8dd2b", Title: "JS fix", AuthorName: "Example User", AuthorEmail: "user@example.com", }}, Diffs: []*Diff{{ Diff: "--- a/files/js/application.js\n+++ c/files/js/application.js\n@@ -24,8 +24,10 @@\n //= require g.raphael-min\n //= require g.bar-min\n //= require branch-graph\n-//= require highlightjs.min\n-//= require ace/ace\n //= require_tree .\n //= require d3\n //= require underscore\n+\n+function fix() { \n+ alert(\"Fixed\")\n+}", NewPath: "files/js/application.js", OldPath: "files/js/application.js", AMode: "", BMode: "100644", NewFile: false, RenamedFile: false, DeletedFile: false, }}, CompareTimeout: false, CompareSameRef: false, WebURL: "https://gitlab.example.com/thedude/gitlab-foss/-/compare/ae73cb07c9eeaf35924a10f713b364d32b2dd34f...0b4bc9a49b562e85de7cc9e834518ea6828729b9", } c, resp, err := client.Repositories.Compare("12d65c8dd2b2676fa3ac47d955accc085a37a9c1", opt, nil) require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, want, c) c, resp, err = client.Repositories.Compare(1.01, opt, nil) require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string") require.Nil(t, resp) require.Nil(t, c) c, resp, err = client.Repositories.Compare("12d65c8dd2b2676fa3ac47d955accc085a37a9c1", opt, nil, errorOption) require.EqualError(t, err, "RequestOptionFunc returns an error") require.Nil(t, resp) require.Nil(t, c) c, resp, err = client.Repositories.Compare("12d65c8dd2b2676fa3ac47d955accc085a37a9c2", opt, nil) require.Error(t, err) require.Nil(t, c) require.Equal(t, http.StatusNotFound, resp.StatusCode) } func TestRepositoriesService_Contributors(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/12d65c8dd2b2676fa3ac47d955accc085a37a9c1/repository/contributors", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprintf(w, ` [{ "name": "Example User", "email": "example@example.com", "commits": 117, "additions": 0, "deletions": 0 }] `) }) want := []*Contributor{{ Name: "Example User", Email: "example@example.com", Commits: 117, Additions: 0, Deletions: 0, }} cs, resp, err := client.Repositories.Contributors("12d65c8dd2b2676fa3ac47d955accc085a37a9c1", nil, nil) require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, want, cs) cs, resp, err = client.Repositories.Contributors(1.01, nil, nil) require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string") require.Nil(t, resp) require.Nil(t, cs) cs, resp, err = client.Repositories.Contributors("12d65c8dd2b2676fa3ac47d955accc085a37a9c1", nil, nil, errorOption) require.EqualError(t, err, "RequestOptionFunc returns an error") require.Nil(t, resp) require.Nil(t, cs) cs, resp, err = client.Repositories.Contributors("12d65c8dd2b2676fa3ac47d955accc085a37a9c2", nil, nil) require.Error(t, err) require.Nil(t, cs) require.Equal(t, http.StatusNotFound, resp.StatusCode) } func TestRepositoriesService_MergeBase(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1a0b36b3cdad1d2ee32457c102a8c0b7056fa863/repository/merge_base", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprintf(w, ` { "id": "1a0b36b3cdad1d2ee32457c102a8c0b7056fa863", "short_id": "1a0b36b3", "title": "Initial commit", "parent_ids": [], "message": "Initial commit\n", "author_name": "Example User", "author_email": "user@example.com", "committer_name": "Example User", "committer_email": "user@example.com" } `) }) want := &Commit{ ID: "1a0b36b3cdad1d2ee32457c102a8c0b7056fa863", ShortID: "1a0b36b3", Title: "Initial commit", AuthorName: "Example User", AuthorEmail: "user@example.com", CommitterName: "Example User", CommitterEmail: "user@example.com", Message: "Initial commit\n", ParentIDs: []string{}, } c, resp, err := client.Repositories.MergeBase("1a0b36b3cdad1d2ee32457c102a8c0b7056fa863", nil, nil) require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, want, c) c, resp, err = client.Repositories.MergeBase(1.01, nil, nil) require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string") require.Nil(t, resp) require.Nil(t, c) c, resp, err = client.Repositories.MergeBase("1a0b36b3cdad1d2ee32457c102a8c0b7056fa863", nil, nil, errorOption) require.EqualError(t, err, "RequestOptionFunc returns an error") require.Nil(t, resp) require.Nil(t, c) c, resp, err = client.Repositories.MergeBase("1a0b36b3cdad1d2ee32457c102a8c0b7056fa865", nil, nil) require.Error(t, err) require.Nil(t, c) require.Equal(t, http.StatusNotFound, resp.StatusCode) } func TestAddChangelogData(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/repository/changelog", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) w.WriteHeader(http.StatusOK) }) resp, err := client.Repositories.AddChangelog( 1, &AddChangelogOptions{ Version: Ptr("1.0.0"), }) require.NoError(t, err) assert.Equal(t, http.StatusOK, resp.StatusCode) } func TestGenerateChangelogData(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/repository/changelog", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, exampleChangelogResponse) }) want := &ChangelogData{ Notes: "## 1.0.0 (2021-11-17)\n\n### feature (2 changes)\n\n- [Title 2](namespace13/project13@ad608eb642124f5b3944ac0ac772fecaf570a6bf) ([merge request](namespace13/project13!2))\n- [Title 1](namespace13/project13@3c6b80ff7034fa0d585314e1571cc780596ce3c8) ([merge request](namespace13/project13!1))\n", } notes, _, err := client.Repositories.GenerateChangelogData( 1, GenerateChangelogDataOptions{ Version: Ptr("1.0.0"), }, ) require.NoError(t, err) assert.Equal(t, want, notes) } golang-gitlab-gitlab-org-api-client-go-0.123.0/repository_files.go000066400000000000000000000303371475761473200250150ustar00rootroot00000000000000// // Copyright 2021, Sander van Harmelen // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "bytes" "fmt" "net/http" "strconv" "time" ) // RepositoryFilesService handles communication with the repository files // related methods of the GitLab API. // // GitLab API docs: https://docs.gitlab.com/ee/api/repository_files.html type RepositoryFilesService struct { client *Client } // File represents a GitLab repository file. // // GitLab API docs: https://docs.gitlab.com/ee/api/repository_files.html type File struct { FileName string `json:"file_name"` FilePath string `json:"file_path"` Size int `json:"size"` Encoding string `json:"encoding"` Content string `json:"content"` ExecuteFilemode bool `json:"execute_filemode"` Ref string `json:"ref"` BlobID string `json:"blob_id"` CommitID string `json:"commit_id"` SHA256 string `json:"content_sha256"` LastCommitID string `json:"last_commit_id"` } func (r File) String() string { return Stringify(r) } // GetFileOptions represents the available GetFile() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/repository_files.html#get-file-from-repository type GetFileOptions struct { Ref *string `url:"ref,omitempty" json:"ref,omitempty"` } // GetFile allows you to receive information about a file in repository like // name, size, content. Note that file content is Base64 encoded. // // GitLab API docs: // https://docs.gitlab.com/ee/api/repository_files.html#get-file-from-repository func (s *RepositoryFilesService) GetFile(pid interface{}, fileName string, opt *GetFileOptions, options ...RequestOptionFunc) (*File, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf( "projects/%s/repository/files/%s", PathEscape(project), PathEscape(fileName), ) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } f := new(File) resp, err := s.client.Do(req, f) if err != nil { return nil, resp, err } return f, resp, nil } // GetFileMetaDataOptions represents the available GetFileMetaData() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/repository_files.html#get-file-from-repository type GetFileMetaDataOptions struct { Ref *string `url:"ref,omitempty" json:"ref,omitempty"` } // GetFileMetaData allows you to receive meta information about a file in // repository like name, size. // // GitLab API docs: // https://docs.gitlab.com/ee/api/repository_files.html#get-file-from-repository func (s *RepositoryFilesService) GetFileMetaData(pid interface{}, fileName string, opt *GetFileMetaDataOptions, options ...RequestOptionFunc) (*File, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf( "projects/%s/repository/files/%s", PathEscape(project), PathEscape(fileName), ) req, err := s.client.NewRequest(http.MethodHead, u, opt, options) if err != nil { return nil, nil, err } resp, err := s.client.Do(req, nil) if err != nil { return nil, resp, err } f := &File{ BlobID: resp.Header.Get("X-Gitlab-Blob-Id"), CommitID: resp.Header.Get("X-Gitlab-Commit-Id"), Encoding: resp.Header.Get("X-Gitlab-Encoding"), FileName: resp.Header.Get("X-Gitlab-File-Name"), FilePath: resp.Header.Get("X-Gitlab-File-Path"), ExecuteFilemode: resp.Header.Get("X-Gitlab-Execute-Filemode") == "true", Ref: resp.Header.Get("X-Gitlab-Ref"), SHA256: resp.Header.Get("X-Gitlab-Content-Sha256"), LastCommitID: resp.Header.Get("X-Gitlab-Last-Commit-Id"), } if sizeString := resp.Header.Get("X-Gitlab-Size"); sizeString != "" { f.Size, err = strconv.Atoi(sizeString) if err != nil { return nil, resp, err } } return f, resp, nil } // FileBlameRange represents one item of blame information. // // GitLab API docs: https://docs.gitlab.com/ee/api/repository_files.html type FileBlameRange struct { Commit struct { ID string `json:"id"` ParentIDs []string `json:"parent_ids"` Message string `json:"message"` AuthoredDate *time.Time `json:"authored_date"` AuthorName string `json:"author_name"` AuthorEmail string `json:"author_email"` CommittedDate *time.Time `json:"committed_date"` CommitterName string `json:"committer_name"` CommitterEmail string `json:"committer_email"` } `json:"commit"` Lines []string `json:"lines"` } func (b FileBlameRange) String() string { return Stringify(b) } // GetFileBlameOptions represents the available GetFileBlame() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/repository_files.html#get-file-blame-from-repository type GetFileBlameOptions struct { Ref *string `url:"ref,omitempty" json:"ref,omitempty"` RangeStart *int `url:"range[start],omitempty" json:"range[start],omitempty"` RangeEnd *int `url:"range[end],omitempty" json:"range[end],omitempty"` } // GetFileBlame allows you to receive blame information. Each blame range // contains lines and corresponding commit info. // // GitLab API docs: // https://docs.gitlab.com/ee/api/repository_files.html#get-file-blame-from-repository func (s *RepositoryFilesService) GetFileBlame(pid interface{}, file string, opt *GetFileBlameOptions, options ...RequestOptionFunc) ([]*FileBlameRange, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf( "projects/%s/repository/files/%s/blame", PathEscape(project), PathEscape(file), ) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } var br []*FileBlameRange resp, err := s.client.Do(req, &br) if err != nil { return nil, resp, err } return br, resp, nil } // GetRawFileOptions represents the available GetRawFile() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/repository_files.html#get-raw-file-from-repository type GetRawFileOptions struct { Ref *string `url:"ref,omitempty" json:"ref,omitempty"` LFS *bool `url:"lfs,omitempty" json:"lfs,omitempty"` } // GetRawFile allows you to receive the raw file in repository. // // GitLab API docs: // https://docs.gitlab.com/ee/api/repository_files.html#get-raw-file-from-repository func (s *RepositoryFilesService) GetRawFile(pid interface{}, fileName string, opt *GetRawFileOptions, options ...RequestOptionFunc) ([]byte, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf( "projects/%s/repository/files/%s/raw", PathEscape(project), PathEscape(fileName), ) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } var f bytes.Buffer resp, err := s.client.Do(req, &f) if err != nil { return nil, resp, err } return f.Bytes(), resp, err } // FileInfo represents file details of a GitLab repository file. // // GitLab API docs: https://docs.gitlab.com/ee/api/repository_files.html type FileInfo struct { FilePath string `json:"file_path"` Branch string `json:"branch"` } func (r FileInfo) String() string { return Stringify(r) } // CreateFileOptions represents the available CreateFile() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/repository_files.html#create-new-file-in-repository type CreateFileOptions struct { Branch *string `url:"branch,omitempty" json:"branch,omitempty"` StartBranch *string `url:"start_branch,omitempty" json:"start_branch,omitempty"` Encoding *string `url:"encoding,omitempty" json:"encoding,omitempty"` AuthorEmail *string `url:"author_email,omitempty" json:"author_email,omitempty"` AuthorName *string `url:"author_name,omitempty" json:"author_name,omitempty"` Content *string `url:"content,omitempty" json:"content,omitempty"` CommitMessage *string `url:"commit_message,omitempty" json:"commit_message,omitempty"` ExecuteFilemode *bool `url:"execute_filemode,omitempty" json:"execute_filemode,omitempty"` } // CreateFile creates a new file in a repository. // // GitLab API docs: // https://docs.gitlab.com/ee/api/repository_files.html#create-new-file-in-repository func (s *RepositoryFilesService) CreateFile(pid interface{}, fileName string, opt *CreateFileOptions, options ...RequestOptionFunc) (*FileInfo, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf( "projects/%s/repository/files/%s", PathEscape(project), PathEscape(fileName), ) req, err := s.client.NewRequest(http.MethodPost, u, opt, options) if err != nil { return nil, nil, err } f := new(FileInfo) resp, err := s.client.Do(req, f) if err != nil { return nil, resp, err } return f, resp, nil } // UpdateFileOptions represents the available UpdateFile() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/repository_files.html#update-existing-file-in-repository type UpdateFileOptions struct { Branch *string `url:"branch,omitempty" json:"branch,omitempty"` StartBranch *string `url:"start_branch,omitempty" json:"start_branch,omitempty"` Encoding *string `url:"encoding,omitempty" json:"encoding,omitempty"` AuthorEmail *string `url:"author_email,omitempty" json:"author_email,omitempty"` AuthorName *string `url:"author_name,omitempty" json:"author_name,omitempty"` Content *string `url:"content,omitempty" json:"content,omitempty"` CommitMessage *string `url:"commit_message,omitempty" json:"commit_message,omitempty"` LastCommitID *string `url:"last_commit_id,omitempty" json:"last_commit_id,omitempty"` ExecuteFilemode *bool `url:"execute_filemode,omitempty" json:"execute_filemode,omitempty"` } // UpdateFile updates an existing file in a repository // // GitLab API docs: // https://docs.gitlab.com/ee/api/repository_files.html#update-existing-file-in-repository func (s *RepositoryFilesService) UpdateFile(pid interface{}, fileName string, opt *UpdateFileOptions, options ...RequestOptionFunc) (*FileInfo, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf( "projects/%s/repository/files/%s", PathEscape(project), PathEscape(fileName), ) req, err := s.client.NewRequest(http.MethodPut, u, opt, options) if err != nil { return nil, nil, err } f := new(FileInfo) resp, err := s.client.Do(req, f) if err != nil { return nil, resp, err } return f, resp, nil } // DeleteFileOptions represents the available DeleteFile() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/repository_files.html#delete-existing-file-in-repository type DeleteFileOptions struct { Branch *string `url:"branch,omitempty" json:"branch,omitempty"` StartBranch *string `url:"start_branch,omitempty" json:"start_branch,omitempty"` AuthorEmail *string `url:"author_email,omitempty" json:"author_email,omitempty"` AuthorName *string `url:"author_name,omitempty" json:"author_name,omitempty"` CommitMessage *string `url:"commit_message,omitempty" json:"commit_message,omitempty"` LastCommitID *string `url:"last_commit_id,omitempty" json:"last_commit_id,omitempty"` } // DeleteFile deletes an existing file in a repository // // GitLab API docs: // https://docs.gitlab.com/ee/api/repository_files.html#delete-existing-file-in-repository func (s *RepositoryFilesService) DeleteFile(pid interface{}, fileName string, opt *DeleteFileOptions, options ...RequestOptionFunc) (*Response, error) { project, err := parseID(pid) if err != nil { return nil, err } u := fmt.Sprintf( "projects/%s/repository/files/%s", PathEscape(project), PathEscape(fileName), ) req, err := s.client.NewRequest(http.MethodDelete, u, opt, options) if err != nil { return nil, err } return s.client.Do(req, nil) } golang-gitlab-gitlab-org-api-client-go-0.123.0/repository_files_test.go000066400000000000000000000307171475761473200260560ustar00rootroot00000000000000package gitlab import ( "fmt" "net/http" "testing" "time" "github.com/stretchr/testify/require" ) func TestRepositoryFilesService_GetFile(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/13083/repository/files/app%2Fmodels%2Fkey%2Erb?ref=master", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprintf(w, ` { "file_name": "key.rb", "file_path": "app/models/key.rb", "size": 1476, "encoding": "base64", "content": "IyA9PSBTY2hlbWEgSW5mb3...", "content_sha256": "4c294617b60715c1d218e61164a3abd4808a4284cbc30e6728a01ad9aada4481", "execute_filemode": true, "ref": "master", "blob_id": "79f7bbd25901e8334750839545a9bd021f0e4c83", "commit_id": "d5a3ff139356ce33e37e73add446f16869741b50", "last_commit_id": "570e7b2abdd848b95f2f578043fc23bd6f6fd24d" } `) }) want := &File{ FileName: "key.rb", FilePath: "app/models/key.rb", Size: 1476, Encoding: "base64", Content: "IyA9PSBTY2hlbWEgSW5mb3...", ExecuteFilemode: true, Ref: "master", BlobID: "79f7bbd25901e8334750839545a9bd021f0e4c83", CommitID: "d5a3ff139356ce33e37e73add446f16869741b50", SHA256: "4c294617b60715c1d218e61164a3abd4808a4284cbc30e6728a01ad9aada4481", LastCommitID: "570e7b2abdd848b95f2f578043fc23bd6f6fd24d", } f, resp, err := client.RepositoryFiles.GetFile(13083, "app/models/key.rb?ref=master", nil) require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, want, f) f, resp, err = client.RepositoryFiles.GetFile(13083.01, "app/models/key.rb?ref=master", nil) require.EqualError(t, err, "invalid ID type 13083.01, the ID must be an int or a string") require.Nil(t, resp) require.Nil(t, f) f, resp, err = client.RepositoryFiles.GetFile(13083, "app/models/key.rb?ref=master", nil, errorOption) require.EqualError(t, err, "RequestOptionFunc returns an error") require.Nil(t, resp) require.Nil(t, f) f, resp, err = client.RepositoryFiles.GetFile(13084, "app/models/key.rb?ref=master", nil) require.Error(t, err) require.Nil(t, f) require.Equal(t, http.StatusNotFound, resp.StatusCode) } func TestRepositoryFilesService_GetFileMetaData(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/13083/repository/files/app%2Fmodels%2Fkey%2Erb?ref=master", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodHead) w.Header().Set("X-Gitlab-Blob-Id", "79f7bbd25901e8334750839545a9bd021f0e4c83") w.Header().Set("X-Gitlab-Commit-Id", "d5a3ff139356ce33e37e73add446f16869741b50") w.Header().Set("X-Gitlab-Content-Sha256", "4c294617b60715c1d218e61164a3abd4808a4284cbc30e6728a01ad9aada4481") w.Header().Set("X-Gitlab-Encoding", "base64") w.Header().Set("X-Gitlab-File-Name", "key.rb") w.Header().Set("X-Gitlab-File-Path", "app/models/key.rb") w.Header().Set("X-Gitlab-Execute-Filemode", "true") w.Header().Set("X-Gitlab-Last-Commit-Id", "570e7b2abdd848b95f2f578043fc23bd6f6fd24d") w.Header().Set("X-Gitlab-Ref", "master") w.Header().Set("X-Gitlab-Size", "1476") }) want := &File{ FileName: "key.rb", FilePath: "app/models/key.rb", Size: 1476, Encoding: "base64", ExecuteFilemode: true, Ref: "master", BlobID: "79f7bbd25901e8334750839545a9bd021f0e4c83", CommitID: "d5a3ff139356ce33e37e73add446f16869741b50", SHA256: "4c294617b60715c1d218e61164a3abd4808a4284cbc30e6728a01ad9aada4481", LastCommitID: "570e7b2abdd848b95f2f578043fc23bd6f6fd24d", } f, resp, err := client.RepositoryFiles.GetFileMetaData(13083, "app/models/key.rb?ref=master", nil) require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, want, f) f, resp, err = client.RepositoryFiles.GetFileMetaData(13083.01, "app/models/key.rb?ref=master", nil) require.EqualError(t, err, "invalid ID type 13083.01, the ID must be an int or a string") require.Nil(t, resp) require.Nil(t, f) f, resp, err = client.RepositoryFiles.GetFileMetaData(13083, "app/models/key.rb?ref=master", nil, errorOption) require.EqualError(t, err, "RequestOptionFunc returns an error") require.Nil(t, resp) require.Nil(t, f) f, resp, err = client.RepositoryFiles.GetFileMetaData(13084, "app/models/key.rb?ref=master", nil) require.Error(t, err) require.Nil(t, f) require.Equal(t, http.StatusNotFound, resp.StatusCode) } func TestRepositoryFilesService_GetFileBlame(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/13083/repository/files/path%2Fto%2Ffile.rb/blame", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprintf(w, ` [ { "commit": { "id": "d42409d56517157c48bf3bd97d3f75974dde19fb", "message": "Add feature also fix bug", "parent_ids": [ "cc6e14f9328fa6d7b5a0d3c30dc2002a3f2a3822" ], "author_name": "Venkatesh Thalluri", "author_email": "venkatesh.thalluri@example.com", "committer_name": "Venkatesh Thalluri", "committer_email": "venkatesh.thalluri@example.com" }, "lines": [ "require 'fileutils'", "require 'open3'" ] } ] `) }) want := []*FileBlameRange{ { Commit: struct { ID string `json:"id"` ParentIDs []string `json:"parent_ids"` Message string `json:"message"` AuthoredDate *time.Time `json:"authored_date"` AuthorName string `json:"author_name"` AuthorEmail string `json:"author_email"` CommittedDate *time.Time `json:"committed_date"` CommitterName string `json:"committer_name"` CommitterEmail string `json:"committer_email"` }{ ID: "d42409d56517157c48bf3bd97d3f75974dde19fb", ParentIDs: []string{"cc6e14f9328fa6d7b5a0d3c30dc2002a3f2a3822"}, Message: "Add feature also fix bug", AuthorName: "Venkatesh Thalluri", AuthorEmail: "venkatesh.thalluri@example.com", CommitterName: "Venkatesh Thalluri", CommitterEmail: "venkatesh.thalluri@example.com", }, Lines: []string{"require 'fileutils'", "require 'open3'"}, }, } fbr, resp, err := client.RepositoryFiles.GetFileBlame(13083, "path/to/file.rb", nil) require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, want, fbr) fbr, resp, err = client.RepositoryFiles.GetFileBlame(13083.01, "path/to/file.rb", nil) require.EqualError(t, err, "invalid ID type 13083.01, the ID must be an int or a string") require.Nil(t, resp) require.Nil(t, fbr) fbr, resp, err = client.RepositoryFiles.GetFileBlame(13083, "path/to/file.rb", nil, errorOption) require.EqualError(t, err, "RequestOptionFunc returns an error") require.Nil(t, resp) require.Nil(t, fbr) fbr, resp, err = client.RepositoryFiles.GetFileBlame(13084, "path/to/file.rb", nil) require.Error(t, err) require.Nil(t, fbr) require.Equal(t, http.StatusNotFound, resp.StatusCode) } func TestRepositoryFilesService_GetRawFile(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/13083/repository/files/app%2Fmodels%2Fkey%2Erb/raw", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprintf(w, "HTTP/1.1 200 OK"+ "..."+ "X-Gitlab-Blob-Id: 79f7bbd25901e8334750839545a9bd021f0e4c83"+ "X-Gitlab-Commit-Id: d5a3ff139356ce33e37e73add446f16869741b50"+ "X-Gitlab-Content-Sha256: 4c294617b60715c1d218e61164a3abd4808a4284cbc30e6728a01ad9aada4481"+ "X-Gitlab-Encoding: base64"+ "X-Gitlab-File-Name: file.rb"+ "X-Gitlab-File-Path: path/to/file.rb"+ "X-Gitlab-Last-Commit-Id: 570e7b2abdd848b95f2f578043fc23bd6f6fd24d"+ "X-Gitlab-Ref: master"+ "X-Gitlab-Size: 1476"+ "...") }) want := []byte("HTTP/1.1 200 OK" + "..." + "X-Gitlab-Blob-Id: 79f7bbd25901e8334750839545a9bd021f0e4c83" + "X-Gitlab-Commit-Id: d5a3ff139356ce33e37e73add446f16869741b50" + "X-Gitlab-Content-Sha256: 4c294617b60715c1d218e61164a3abd4808a4284cbc30e6728a01ad9aada4481" + "X-Gitlab-Encoding: base64" + "X-Gitlab-File-Name: file.rb" + "X-Gitlab-File-Path: path/to/file.rb" + "X-Gitlab-Last-Commit-Id: 570e7b2abdd848b95f2f578043fc23bd6f6fd24d" + "X-Gitlab-Ref: master" + "X-Gitlab-Size: 1476" + "...", ) b, resp, err := client.RepositoryFiles.GetRawFile(13083, "app/models/key.rb", nil) require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, want, b) b, resp, err = client.RepositoryFiles.GetRawFile(13083.01, "app/models/key.rb", nil) require.EqualError(t, err, "invalid ID type 13083.01, the ID must be an int or a string") require.Nil(t, resp) require.Nil(t, b) b, resp, err = client.RepositoryFiles.GetRawFile(13083, "app/models/key.rb", nil, errorOption) require.EqualError(t, err, "RequestOptionFunc returns an error") require.Nil(t, resp) require.Nil(t, b) b, resp, err = client.RepositoryFiles.GetRawFile(13084, "app/models/key.rb", nil) require.Error(t, err) require.Nil(t, b) require.Equal(t, http.StatusNotFound, resp.StatusCode) } func TestRepositoryFilesService_CreateFile(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/13083/repository/files/app%2Fproject%2Erb", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) fmt.Fprintf(w, ` { "file_path": "app/project.rb", "branch": "master" } `) }) want := &FileInfo{ FilePath: "app/project.rb", Branch: "master", } fi, resp, err := client.RepositoryFiles.CreateFile(13083, "app/project.rb", nil) require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, want, fi) fi, resp, err = client.RepositoryFiles.CreateFile(13083, "app/project.rb", &CreateFileOptions{ExecuteFilemode: Ptr(true)}) require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, want, fi) fi, resp, err = client.RepositoryFiles.CreateFile(13083.01, "app/project.rb", nil) require.EqualError(t, err, "invalid ID type 13083.01, the ID must be an int or a string") require.Nil(t, resp) require.Nil(t, fi) fi, resp, err = client.RepositoryFiles.CreateFile(13083, "app/project.rb", nil, errorOption) require.EqualError(t, err, "RequestOptionFunc returns an error") require.Nil(t, resp) require.Nil(t, fi) fi, resp, err = client.RepositoryFiles.CreateFile(13084, "app/project.rb", nil) require.Error(t, err) require.Nil(t, fi) require.Equal(t, http.StatusNotFound, resp.StatusCode) } func TestRepositoryFilesService_UpdateFile(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/13083/repository/files/app%2Fproject%2Erb", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPut) fmt.Fprintf(w, ` { "file_path": "app/project.rb", "branch": "master" } `) }) want := &FileInfo{ FilePath: "app/project.rb", Branch: "master", } fi, resp, err := client.RepositoryFiles.UpdateFile(13083, "app/project.rb", nil) require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, want, fi) fi, resp, err = client.RepositoryFiles.UpdateFile(13083, "app/project.rb", &UpdateFileOptions{ExecuteFilemode: Ptr(true)}) require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, want, fi) fi, resp, err = client.RepositoryFiles.UpdateFile(13083.01, "app/project.rb", nil) require.EqualError(t, err, "invalid ID type 13083.01, the ID must be an int or a string") require.Nil(t, resp) require.Nil(t, fi) fi, resp, err = client.RepositoryFiles.UpdateFile(13083, "app/project.rb", nil, errorOption) require.EqualError(t, err, "RequestOptionFunc returns an error") require.Nil(t, resp) require.Nil(t, fi) fi, resp, err = client.RepositoryFiles.UpdateFile(13084, "app/project.rb", nil) require.Error(t, err) require.Nil(t, fi) require.Equal(t, http.StatusNotFound, resp.StatusCode) } func TestRepositoryFilesService_DeleteFile(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/13083/repository/files/app%2Fproject%2Erb", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodDelete) }) resp, err := client.RepositoryFiles.DeleteFile(13083, "app/project.rb", nil) require.NoError(t, err) require.NotNil(t, resp) resp, err = client.RepositoryFiles.DeleteFile(13083.01, "app/project.rb", nil) require.EqualError(t, err, "invalid ID type 13083.01, the ID must be an int or a string") require.Nil(t, resp) resp, err = client.RepositoryFiles.DeleteFile(13083, "app/project.rb", nil, errorOption) require.EqualError(t, err, "RequestOptionFunc returns an error") require.Nil(t, resp) resp, err = client.RepositoryFiles.DeleteFile(13084, "app/project.rb", nil) require.Error(t, err) require.Equal(t, http.StatusNotFound, resp.StatusCode) } golang-gitlab-gitlab-org-api-client-go-0.123.0/repository_submodules.go000066400000000000000000000061141475761473200260710ustar00rootroot00000000000000// // Copyright 2021, Sander van Harmelen // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "fmt" "net/http" "time" ) // RepositorySubmodulesService handles communication with the repository // submodules related methods of the GitLab API. // // GitLab API docs: https://docs.gitlab.com/ee/api/repository_submodules.html type RepositorySubmodulesService struct { client *Client } // SubmoduleCommit represents a GitLab submodule commit. // // GitLab API docs: https://docs.gitlab.com/ee/api/repository_submodules.html type SubmoduleCommit struct { ID string `json:"id"` ShortID string `json:"short_id"` Title string `json:"title"` AuthorName string `json:"author_name"` AuthorEmail string `json:"author_email"` CommitterName string `json:"committer_name"` CommitterEmail string `json:"committer_email"` CreatedAt *time.Time `json:"created_at"` Message string `json:"message"` ParentIDs []string `json:"parent_ids"` CommittedDate *time.Time `json:"committed_date"` AuthoredDate *time.Time `json:"authored_date"` Status *BuildStateValue `json:"status"` } func (r SubmoduleCommit) String() string { return Stringify(r) } // UpdateSubmoduleOptions represents the available UpdateSubmodule() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/repository_submodules.html#update-existing-submodule-reference-in-repository type UpdateSubmoduleOptions struct { Branch *string `url:"branch,omitempty" json:"branch,omitempty"` CommitSHA *string `url:"commit_sha,omitempty" json:"commit_sha,omitempty"` CommitMessage *string `url:"commit_message,omitempty" json:"commit_message,omitempty"` } // UpdateSubmodule updates an existing submodule reference. // // GitLab API docs: // https://docs.gitlab.com/ee/api/repository_submodules.html#update-existing-submodule-reference-in-repository func (s *RepositorySubmodulesService) UpdateSubmodule(pid interface{}, submodule string, opt *UpdateSubmoduleOptions, options ...RequestOptionFunc) (*SubmoduleCommit, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf( "projects/%s/repository/submodules/%s", PathEscape(project), PathEscape(submodule), ) req, err := s.client.NewRequest(http.MethodPut, u, opt, options) if err != nil { return nil, nil, err } sc := new(SubmoduleCommit) resp, err := s.client.Do(req, sc) if err != nil { return nil, resp, err } return sc, resp, nil } golang-gitlab-gitlab-org-api-client-go-0.123.0/repository_submodules_test.go000066400000000000000000000026141475761473200271310ustar00rootroot00000000000000package gitlab import ( "fmt" "net/http" "testing" "github.com/stretchr/testify/require" ) func TestRepositorySubmodulesService_UpdateSubmodule(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/13083/repository/submodules/app%2Fproject", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPut) fmt.Fprintf(w, ` { "id": "6104942438c14ec7bd21c6cd5bd995272b3faff6", "short_id": "6104942438c", "title": "Update my submodule", "author_name": "popdabubbles", "author_email": "noty@gmail.com", "committer_name": "Will", "committer_email": "noty@gmail.com", "message": "Update my submodule", "parent_ids": [ "ae1d9fb46aa2b07ee9836d49862ec4e2c46fbbba" ], "status": "running" } `) }) want := &SubmoduleCommit{ ID: "6104942438c14ec7bd21c6cd5bd995272b3faff6", ShortID: "6104942438c", Title: "Update my submodule", AuthorName: "popdabubbles", AuthorEmail: "noty@gmail.com", CommitterName: "Will", CommitterEmail: "noty@gmail.com", Message: "Update my submodule", ParentIDs: []string{"ae1d9fb46aa2b07ee9836d49862ec4e2c46fbbba"}, Status: Ptr(Running), } sc, resp, err := client.RepositorySubmodules.UpdateSubmodule(13083, "app/project", nil) require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, want, sc) } golang-gitlab-gitlab-org-api-client-go-0.123.0/request_options.go000066400000000000000000000055621475761473200246610ustar00rootroot00000000000000// // Copyright 2021, Sander van Harmelen // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "context" "net/url" retryablehttp "github.com/hashicorp/go-retryablehttp" ) // RequestOptionFunc can be passed to all API requests to customize the API request. type RequestOptionFunc func(*retryablehttp.Request) error // WithContext runs the request with the provided context func WithContext(ctx context.Context) RequestOptionFunc { return func(req *retryablehttp.Request) error { *req = *req.WithContext(ctx) return nil } } // WithHeader takes a header name and value and appends it to the request headers. func WithHeader(name, value string) RequestOptionFunc { return func(req *retryablehttp.Request) error { req.Header.Set(name, value) return nil } } // WithHeaders takes a map of header name/value pairs and appends them to the // request headers. func WithHeaders(headers map[string]string) RequestOptionFunc { return func(req *retryablehttp.Request) error { for k, v := range headers { req.Header.Set(k, v) } return nil } } // WithKeysetPaginationParameters takes a "next" link from the Link header of a // response to a keyset-based paginated request and modifies the values of each // query parameter in the request with its corresponding response parameter. func WithKeysetPaginationParameters(nextLink string) RequestOptionFunc { return func(req *retryablehttp.Request) error { nextUrl, err := url.Parse(nextLink) if err != nil { return err } q := req.URL.Query() for k, values := range nextUrl.Query() { q.Del(k) for _, v := range values { q.Add(k, v) } } req.URL.RawQuery = q.Encode() return nil } } // WithSudo takes either a username or user ID and sets the SUDO request header. func WithSudo(uid interface{}) RequestOptionFunc { return func(req *retryablehttp.Request) error { user, err := parseID(uid) if err != nil { return err } req.Header.Set("SUDO", user) return nil } } // WithToken takes a token which is then used when making this one request. func WithToken(authType AuthType, token string) RequestOptionFunc { return func(req *retryablehttp.Request) error { switch authType { case JobToken: req.Header.Set("JOB-TOKEN", token) case OAuthToken: req.Header.Set("Authorization", "Bearer "+token) case PrivateToken: req.Header.Set("PRIVATE-TOKEN", token) } return nil } } golang-gitlab-gitlab-org-api-client-go-0.123.0/request_options_test.go000066400000000000000000000136611475761473200257170ustar00rootroot00000000000000// Copyright 2021, Sander van Harmelen // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package gitlab import ( "fmt" "net/http" "testing" "time" "github.com/hashicorp/go-retryablehttp" "github.com/stretchr/testify/assert" ) func TestWithHeader(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/without-header", func(w http.ResponseWriter, r *http.Request) { assert.Empty(t, r.Header.Get("X-CUSTOM-HEADER")) w.WriteHeader(http.StatusOK) fmt.Fprintf(w, `{"X-CUSTOM-HEADER": %s`, r.Header.Get("X-CUSTOM-HEADER")) }) mux.HandleFunc("/api/v4/with-header", func(w http.ResponseWriter, r *http.Request) { assert.Equal(t, "randomtokenstring", r.Header.Get("X-CUSTOM-HEADER")) w.WriteHeader(http.StatusOK) fmt.Fprintf(w, `{"X-CUSTOM-HEADER": %s`, r.Header.Get("X-CUSTOM-HEADER")) }) // ensure that X-CUSTOM-HEADER hasn't been set at all req, err := client.NewRequest(http.MethodGet, "/without-header", nil, nil) assert.NoError(t, err) _, err = client.Do(req, nil) assert.NoError(t, err) // ensure that X-CUSTOM-HEADER is set for only one request req, err = client.NewRequest( http.MethodGet, "/with-header", nil, []RequestOptionFunc{WithHeader("X-CUSTOM-HEADER", "randomtokenstring")}, ) assert.NoError(t, err) _, err = client.Do(req, nil) assert.NoError(t, err) req, err = client.NewRequest(http.MethodGet, "/without-header", nil, nil) assert.NoError(t, err) _, err = client.Do(req, nil) assert.NoError(t, err) // ensure that X-CUSTOM-HEADER is set for all client requests addr := client.BaseURL().String() client, err = NewClient("", // same base options as setup WithBaseURL(addr), // Disable backoff to speed up tests that expect errors. WithCustomBackoff(func(_, _ time.Duration, _ int, _ *http.Response) time.Duration { return 0 }), // add client headers WithRequestOptions(WithHeader("X-CUSTOM-HEADER", "randomtokenstring"))) assert.NoError(t, err) assert.NotNil(t, client) req, err = client.NewRequest(http.MethodGet, "/with-header", nil, nil) assert.NoError(t, err) assert.Equal(t, "randomtokenstring", req.Header.Get("X-CUSTOM-HEADER")) _, err = client.Do(req, nil) assert.NoError(t, err) req, err = client.NewRequest(http.MethodGet, "/with-header", nil, nil) assert.NoError(t, err) assert.Equal(t, "randomtokenstring", req.Header.Get("X-CUSTOM-HEADER")) _, err = client.Do(req, nil) assert.NoError(t, err) } func TestWithHeaders(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/without-headers", func(w http.ResponseWriter, r *http.Request) { assert.Empty(t, r.Header.Get("X-CUSTOM-HEADER-1")) assert.Empty(t, r.Header.Get("X-CUSTOM-HEADER-2")) w.WriteHeader(http.StatusOK) }) mux.HandleFunc("/api/v4/with-headers", func(w http.ResponseWriter, r *http.Request) { assert.Equal(t, "randomtokenstring", r.Header.Get("X-CUSTOM-HEADER-1")) assert.Equal(t, "randomtokenstring2", r.Header.Get("X-CUSTOM-HEADER-2")) w.WriteHeader(http.StatusOK) }) headers := map[string]string{ "X-CUSTOM-HEADER-1": "randomtokenstring", "X-CUSTOM-HEADER-2": "randomtokenstring2", } // ensure that X-CUSTOM-HEADER hasn't been set at all req, err := client.NewRequest(http.MethodGet, "/without-headers", nil, nil) assert.NoError(t, err) _, err = client.Do(req, nil) assert.NoError(t, err) // ensure that X-CUSTOM-HEADER is set for only one request req, err = client.NewRequest( http.MethodGet, "/with-headers", nil, []RequestOptionFunc{WithHeaders(headers)}, ) assert.NoError(t, err) _, err = client.Do(req, nil) assert.NoError(t, err) req, err = client.NewRequest(http.MethodGet, "/without-headers", nil, nil) assert.NoError(t, err) _, err = client.Do(req, nil) assert.NoError(t, err) // ensure that X-CUSTOM-HEADER is set for all client requests addr := client.BaseURL().String() client, err = NewClient("", // same base options as setup WithBaseURL(addr), // Disable backoff to speed up tests that expect errors. WithCustomBackoff(func(_, _ time.Duration, _ int, _ *http.Response) time.Duration { return 0 }), // add client headers WithRequestOptions(WithHeaders(headers)), ) assert.NoError(t, err) assert.NotNil(t, client) req, err = client.NewRequest(http.MethodGet, "/with-headers", nil, nil) assert.NoError(t, err) assert.Equal(t, "randomtokenstring", req.Header.Get("X-CUSTOM-HEADER-1")) _, err = client.Do(req, nil) assert.NoError(t, err) req, err = client.NewRequest(http.MethodGet, "/with-headers", nil, nil) assert.NoError(t, err) assert.Equal(t, "randomtokenstring", req.Header.Get("X-CUSTOM-HEADER-1")) _, err = client.Do(req, nil) assert.NoError(t, err) } func TestWithKeysetPaginationParameters(t *testing.T) { req, err := retryablehttp.NewRequest("GET", "https://gitlab.example.com/api/v4/groups?pagination=keyset&per_page=50&order_by=name&sort=asc", nil) assert.NoError(t, err) linkNext := "https://gitlab.example.com/api/v4/groups?pagination=keyset&per_page=50&order_by=name&sort=asc&cursor=eyJuYW1lIjoiRmxpZ2h0anMiLCJpZCI6IjI2IiwiX2tkIjoibiJ9" err = WithKeysetPaginationParameters(linkNext)(req) assert.NoError(t, err) values := req.URL.Query() // Ensure all original parameters remain assert.Equal(t, "keyset", values.Get("pagination")) assert.Equal(t, "50", values.Get("per_page")) assert.Equal(t, "name", values.Get("order_by")) assert.Equal(t, "asc", values.Get("sort")) // Ensure cursor gets properly pulled from "next link" header assert.Equal(t, "eyJuYW1lIjoiRmxpZ2h0anMiLCJpZCI6IjI2IiwiX2tkIjoibiJ9", values.Get("cursor")) } golang-gitlab-gitlab-org-api-client-go-0.123.0/resource_group.go000066400000000000000000000115521475761473200244550ustar00rootroot00000000000000// // Copyright 2021, Sander van Harmelen // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "fmt" "net/http" "time" ) // ResourceGroupService handles communication with the resource // group related methods of the GitLab API. // // GitLab API docs: // https://docs.gitlab.com/ee/api/resource_groups.html type ResourceGroupService struct { client *Client } // ResourceGrouop represents a GitLab Project Resource Group. // // GitLab API docs: // https://docs.gitlab.com/ee/api/resource_groups.html type ResourceGroup struct { ID int `json:"id"` Key string `json:"key"` ProcessMode string `json:"process_mode"` CreatedAt *time.Time `json:"created_at"` UpdatedAt *time.Time `json:"updated_at"` } // Gets a string representation of a ResourceGroup // // GitLab API docs: // https://docs.gitlab.com/ee/api/resource_groups.html func (rg ResourceGroup) String() string { return Stringify(rg) } // GetAllResourceGroupsForAProject allows you to get all resource // groups associated with a given project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/resource_groups.html#get-all-resource-groups-for-a-project func (s *ResourceGroupService) GetAllResourceGroupsForAProject(pid interface{}, options ...RequestOptionFunc) ([]*ResourceGroup, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/resource_groups", PathEscape(project)) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } var rgs []*ResourceGroup resp, err := s.client.Do(req, &rgs) if err != nil { return nil, resp, err } return rgs, resp, nil } // GetASpecificResourceGroup allows you to get a specific // resource group for a given project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/resource_groups.html#get-a-specific-resource-group func (s *ResourceGroupService) GetASpecificResourceGroup(pid interface{}, key string, options ...RequestOptionFunc) (*ResourceGroup, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/resource_groups/%s", PathEscape(project), key) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } rg := new(ResourceGroup) resp, err := s.client.Do(req, rg) if err != nil { return nil, resp, err } return rg, resp, nil } // ListUpcomingJobsForASpecificResourceGroup allows you to get all // upcoming jobs for a specific resource group for a given project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/resource_groups.html#list-upcoming-jobs-for-a-specific-resource-group func (s *ResourceGroupService) ListUpcomingJobsForASpecificResourceGroup(pid interface{}, key string, options ...RequestOptionFunc) ([]*Job, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/resource_groups/%s/upcoming_jobs", PathEscape(project), key) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } var js []*Job resp, err := s.client.Do(req, &js) if err != nil { return nil, resp, err } return js, resp, nil } // EditAnExistingResourceGroupOptions represents the available // EditAnExistingResourceGroup options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/resource_groups.html#edit-an-existing-resource-group type EditAnExistingResourceGroupOptions struct { ProcessMode *ResourceGroupProcessMode `url:"process_mode,omitempty" json:"process_mode,omitempty"` } // EditAnExistingResourceGroup allows you to edit a specific // resource group for a given project // // GitLab API docs: // https://docs.gitlab.com/ee/api/resource_groups.html#edit-an-existing-resource-group func (s *ResourceGroupService) EditAnExistingResourceGroup(pid interface{}, key string, opts *EditAnExistingResourceGroupOptions, options ...RequestOptionFunc) (*ResourceGroup, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/resource_groups/%s", PathEscape(project), key) req, err := s.client.NewRequest(http.MethodPut, u, opts, options) if err != nil { return nil, nil, err } rg := new(ResourceGroup) resp, err := s.client.Do(req, rg) if err != nil { return nil, resp, err } return rg, resp, nil } golang-gitlab-gitlab-org-api-client-go-0.123.0/resource_group_test.go000066400000000000000000000130121475761473200255050ustar00rootroot00000000000000package gitlab import ( "fmt" "net/http" "testing" "time" "github.com/stretchr/testify/require" ) func TestResourceGroups_GetAllResourceGroupsForAProject(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/resource_groups", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, `[ { "id": 3, "key": "production", "process_mode": "unordered", "created_at": "2021-09-01T08:04:59.650Z", "updated_at": "2021-09-01T08:04:59.650Z" } ]`) }) date, _ := time.Parse(timeLayout, "2021-09-01T08:04:59.650Z") want := []*ResourceGroup{{ ID: 3, Key: "production", ProcessMode: "unordered", CreatedAt: &date, UpdatedAt: &date, }} rgs, resp, err := client.ResourceGroup.GetAllResourceGroupsForAProject(1) require.NoError(t, err) require.NotNil(t, resp) require.ElementsMatch(t, want, rgs) rgs, resp, err = client.ResourceGroup.GetAllResourceGroupsForAProject(1.01) require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string") require.Nil(t, resp) require.Nil(t, rgs) rgs, resp, err = client.ResourceGroup.GetAllResourceGroupsForAProject(1, errorOption) require.EqualError(t, err, "RequestOptionFunc returns an error") require.Nil(t, resp) require.Nil(t, rgs) rgs, resp, err = client.ResourceGroup.GetAllResourceGroupsForAProject(2) require.Error(t, err) require.Equal(t, http.StatusNotFound, resp.StatusCode) require.Nil(t, rgs) } func TestResourceGroups_GetASpecificResourceGroup(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/resource_groups/production", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, ` { "id": 3, "key": "production", "process_mode": "unordered", "created_at": "2021-09-01T08:04:59.650Z", "updated_at": "2021-09-01T08:04:59.650Z" } `) }) date, _ := time.Parse(timeLayout, "2021-09-01T08:04:59.650Z") want := &ResourceGroup{ ID: 3, Key: "production", ProcessMode: "unordered", CreatedAt: &date, UpdatedAt: &date, } rg, resp, err := client.ResourceGroup.GetASpecificResourceGroup(1, "production") require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, want, rg) rg, resp, err = client.ResourceGroup.GetASpecificResourceGroup(1.01, "production") require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string") require.Nil(t, resp) require.Nil(t, rg) rg, resp, err = client.ResourceGroup.GetASpecificResourceGroup(1, "production", errorOption) require.EqualError(t, err, "RequestOptionFunc returns an error") require.Nil(t, resp) require.Nil(t, rg) rg, resp, err = client.ResourceGroup.GetASpecificResourceGroup(2, "production") require.Error(t, err) require.Equal(t, http.StatusNotFound, resp.StatusCode) require.Nil(t, rg) } func TestResourceGroups_ListUpcomingJobsForASpecificResourceGroup(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/resource_groups/production/upcoming_jobs", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, `[ { "id": 6 } ]`) }) want := []*Job{{ ID: 6, }} jobs, resp, err := client.ResourceGroup.ListUpcomingJobsForASpecificResourceGroup(1, "production") require.NoError(t, err) require.NotNil(t, resp) require.ElementsMatch(t, want, jobs) jobs, resp, err = client.ResourceGroup.ListUpcomingJobsForASpecificResourceGroup(1.01, "production") require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string") require.Nil(t, resp) require.Nil(t, jobs) jobs, resp, err = client.ResourceGroup.ListUpcomingJobsForASpecificResourceGroup(1, "production", errorOption) require.EqualError(t, err, "RequestOptionFunc returns an error") require.Nil(t, resp) require.Nil(t, jobs) jobs, resp, err = client.ResourceGroup.ListUpcomingJobsForASpecificResourceGroup(2, "production") require.Error(t, err) require.Equal(t, http.StatusNotFound, resp.StatusCode) require.Nil(t, jobs) } func TestResourceGroup_EditAnExistingResourceGroup(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/resource_groups/production", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPut) fmt.Fprint(w, ` { "id": 3, "key": "production", "process_mode": "unordered", "created_at": "2021-09-01T08:04:59.650Z", "updated_at": "2021-09-01T08:04:59.650Z" } `) }) date, _ := time.Parse(timeLayout, "2021-09-01T08:04:59.650Z") want := &ResourceGroup{ ID: 3, Key: "production", ProcessMode: "unordered", CreatedAt: &date, UpdatedAt: &date, } opts := &EditAnExistingResourceGroupOptions{ProcessMode: Ptr(OldestFirst)} rg, resp, err := client.ResourceGroup.EditAnExistingResourceGroup(1, "production", opts) require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, want, rg) rg, resp, err = client.ResourceGroup.EditAnExistingResourceGroup(1.01, "production", opts) require.EqualError(t, err, "invalid ID type 1.01, the ID must be an int or a string") require.Nil(t, resp) require.Nil(t, rg) rg, resp, err = client.ResourceGroup.EditAnExistingResourceGroup(1, "production", opts, errorOption) require.EqualError(t, err, "RequestOptionFunc returns an error") require.Nil(t, resp) require.Nil(t, rg) rg, resp, err = client.ResourceGroup.EditAnExistingResourceGroup(2, "production", opts) require.Error(t, err) require.Equal(t, http.StatusNotFound, resp.StatusCode) require.Nil(t, rg) } golang-gitlab-gitlab-org-api-client-go-0.123.0/resource_iteration_events.go000066400000000000000000000075611475761473200267100ustar00rootroot00000000000000// // Copyright 2023, Hakki Ceylan, Yavuz Turk // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "fmt" "net/http" "time" ) // ResourceIterationEventsService handles communication with the event related // methods of the GitLab API. // // GitLab API docs: https://docs.gitlab.com/ee/api/resource_iteration_events.html type ResourceIterationEventsService struct { client *Client } // IterationEvent represents a resource iteration event. // // GitLab API docs: https://docs.gitlab.com/ee/api/resource_iteration_events.html type IterationEvent struct { ID int `json:"id"` User *BasicUser `json:"user"` CreatedAt *time.Time `json:"created_at"` ResourceType string `json:"resource_type"` ResourceID int `json:"resource_id"` Iteration *Iteration `json:"iteration"` Action string `json:"action"` } // Iteration represents a project issue iteration. // // GitLab API docs: https://docs.gitlab.com/ee/api/resource_iteration_events.html type Iteration struct { ID int `json:"id"` IID int `json:"iid"` Sequence int `json:"sequence"` GroupID int `json:"group_id"` Title string `json:"title"` Description string `json:"description"` State int `json:"state"` CreatedAt *time.Time `json:"created_at"` UpdatedAt *time.Time `json:"updated_at"` DueDate *ISOTime `json:"due_date"` StartDate *ISOTime `json:"start_date"` WebURL string `json:"web_url"` } // ListIterationEventsOptions represents the options for all resource state // events list methods. // // GitLab API docs: // https://docs.gitlab.com/ee/api/resource_iteration_events.html#list-project-issue-iteration-events type ListIterationEventsOptions struct { ListOptions } // ListIssueIterationEvents retrieves resource iteration events for the // specified project and issue. // // GitLab API docs: // https://docs.gitlab.com/ee/api/resource_iteration_events.html#list-project-issue-iteration-events func (s *ResourceIterationEventsService) ListIssueIterationEvents(pid interface{}, issue int, opt *ListIterationEventsOptions, options ...RequestOptionFunc) ([]*IterationEvent, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/issues/%d/resource_iteration_events", PathEscape(project), issue) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } var ies []*IterationEvent resp, err := s.client.Do(req, &ies) if err != nil { return nil, resp, err } return ies, resp, nil } // GetIssueIterationEvent gets a single issue iteration event. // // GitLab API docs: // https://docs.gitlab.com/ee/api/resource_iteration_events.html#get-single-issue-iteration-event func (s *ResourceIterationEventsService) GetIssueIterationEvent(pid interface{}, issue int, event int, options ...RequestOptionFunc) (*IterationEvent, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/issues/%d/resource_iteration_events/%d", PathEscape(project), issue, event) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } ie := new(IterationEvent) resp, err := s.client.Do(req, ie) if err != nil { return nil, resp, err } return ie, resp, nil } golang-gitlab-gitlab-org-api-client-go-0.123.0/resource_iteration_events_test.go000066400000000000000000000136121475761473200277410ustar00rootroot00000000000000// Copyright 2023, Hakki Ceylan, Yavuz Turk // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package gitlab import ( "fmt" "net/http" "testing" "time" "github.com/stretchr/testify/require" ) func TestListIssueIterationEventsService_ListIssueIterationEvents(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/5/issues/11/resource_iteration_events", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprintf(w, `[ { "id": 142, "user": { "id": 1, "username": "root", "name": "Administrator", "state": "active", "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon", "web_url": "https://gitlab.example.com/root" }, "created_at": "2023-09-22T06:51:04.801Z", "resource_type": "Issue", "resource_id": 11, "iteration": { "id": 133, "iid": 1, "sequence": 1, "group_id": 153, "title": "Iteration 1", "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", "state": 1, "created_at": "2023-07-15T00:05:06.509Z", "updated_at": "2023-09-24T00:05:10.476Z", "start_date": "2023-09-17", "due_date": "2023-09-23", "web_url": "https://gitlab.example.com/groups/project/-/iterations/1" }, "action": "add" } ]`) }) opt := &ListIterationEventsOptions{ListOptions{Page: 1, PerPage: 10}} mes, _, err := client.ResourceIterationEvents.ListIssueIterationEvents(5, 11, opt) require.NoError(t, err) startDateISOTime, err := ParseISOTime("2023-09-17") require.NoError(t, err) dueDateISOTime, err := ParseISOTime("2023-09-23") require.NoError(t, err) want := []*IterationEvent{{ ID: 142, User: &BasicUser{ ID: 1, Username: "root", Name: "Administrator", State: "active", AvatarURL: "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon", WebURL: "https://gitlab.example.com/root", }, ResourceType: "Issue", ResourceID: 11, CreatedAt: Time(time.Date(2023, time.September, 22, 0o6, 51, 0o4, 801000000, time.UTC)), Iteration: &Iteration{ ID: 133, IID: 1, Sequence: 1, GroupID: 153, Title: "Iteration 1", Description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", State: 1, CreatedAt: Time(time.Date(2023, time.July, 15, 0o0, 0o5, 0o6, 509000000, time.UTC)), UpdatedAt: Time(time.Date(2023, time.September, 24, 0o0, 0o5, 10, 476000000, time.UTC)), StartDate: &startDateISOTime, DueDate: &dueDateISOTime, WebURL: "https://gitlab.example.com/groups/project/-/iterations/1", }, Action: "add", }} require.Equal(t, want, mes) } func TestListIssueIterationEventsService_GetIssueIterationEvent(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/5/issues/11/resource_iteration_events/143", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprintf(w, ` { "id": 143, "user": { "id": 1, "username": "root", "name": "Administrator", "state": "active", "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon", "web_url": "https://gitlab.example.com/root" }, "created_at": "2023-09-22T06:51:04.801Z", "resource_type": "Issue", "resource_id": 11, "iteration": { "id": 133, "iid": 1, "sequence": 1, "group_id": 153, "title": "Iteration 1", "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", "state": 1, "created_at": "2023-07-15T00:05:06.509Z", "updated_at": "2023-09-24T00:05:10.476Z", "start_date": "2023-09-17", "due_date": "2023-09-23", "web_url": "https://gitlab.example.com/groups/project/-/iterations/2" }, "action": "add" }`, ) }) me, _, err := client.ResourceIterationEvents.GetIssueIterationEvent(5, 11, 143) require.NoError(t, err) startDateISOTime, err := ParseISOTime("2023-09-17") require.NoError(t, err) dueDateISOTime, err := ParseISOTime("2023-09-23") require.NoError(t, err) want := &IterationEvent{ ID: 143, User: &BasicUser{ ID: 1, Username: "root", Name: "Administrator", State: "active", AvatarURL: "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon", WebURL: "https://gitlab.example.com/root", }, ResourceType: "Issue", ResourceID: 11, CreatedAt: Time(time.Date(2023, time.September, 22, 0o6, 51, 0o4, 801000000, time.UTC)), Iteration: &Iteration{ ID: 133, IID: 1, Sequence: 1, GroupID: 153, Title: "Iteration 1", Description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", State: 1, CreatedAt: Time(time.Date(2023, time.July, 15, 0o0, 0o5, 0o6, 509000000, time.UTC)), UpdatedAt: Time(time.Date(2023, time.September, 24, 0o0, 0o5, 10, 476000000, time.UTC)), StartDate: &startDateISOTime, DueDate: &dueDateISOTime, WebURL: "https://gitlab.example.com/groups/project/-/iterations/2", }, Action: "add", } require.Equal(t, want, me) } golang-gitlab-gitlab-org-api-client-go-0.123.0/resource_label_events.go000066400000000000000000000151241475761473200257630ustar00rootroot00000000000000// // Copyright 2021, Sander van Harmelen // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "fmt" "net/http" "time" ) // ResourceLabelEventsService handles communication with the event related // methods of the GitLab API. // // GitLab API docs: https://docs.gitlab.com/ee/api/resource_label_events.html type ResourceLabelEventsService struct { client *Client } // LabelEvent represents a resource label event. // // GitLab API docs: // https://docs.gitlab.com/ee/api/resource_label_events.html#get-single-issue-label-event type LabelEvent struct { ID int `json:"id"` Action string `json:"action"` CreatedAt *time.Time `json:"created_at"` ResourceType string `json:"resource_type"` ResourceID int `json:"resource_id"` User struct { ID int `json:"id"` Name string `json:"name"` Username string `json:"username"` State string `json:"state"` AvatarURL string `json:"avatar_url"` WebURL string `json:"web_url"` } `json:"user"` Label struct { ID int `json:"id"` Name string `json:"name"` Color string `json:"color"` TextColor string `json:"text_color"` Description string `json:"description"` } `json:"label"` } // ListLabelEventsOptions represents the options for all resource label events // list methods. // // GitLab API docs: // https://docs.gitlab.com/ee/api/resource_label_events.html#list-project-issue-label-events type ListLabelEventsOptions struct { ListOptions } // ListIssueLabelEvents retrieves resource label events for the // specified project and issue. // // GitLab API docs: // https://docs.gitlab.com/ee/api/resource_label_events.html#list-project-issue-label-events func (s *ResourceLabelEventsService) ListIssueLabelEvents(pid interface{}, issue int, opt *ListLabelEventsOptions, options ...RequestOptionFunc) ([]*LabelEvent, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/issues/%d/resource_label_events", PathEscape(project), issue) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } var ls []*LabelEvent resp, err := s.client.Do(req, &ls) if err != nil { return nil, resp, err } return ls, resp, nil } // GetIssueLabelEvent gets a single issue-label-event. // // GitLab API docs: // https://docs.gitlab.com/ee/api/resource_label_events.html#get-single-issue-label-event func (s *ResourceLabelEventsService) GetIssueLabelEvent(pid interface{}, issue int, event int, options ...RequestOptionFunc) (*LabelEvent, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/issues/%d/resource_label_events/%d", PathEscape(project), issue, event) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } l := new(LabelEvent) resp, err := s.client.Do(req, l) if err != nil { return nil, resp, err } return l, resp, nil } // ListGroupEpicLabelEvents retrieves resource label events for the specified // group and epic. // // GitLab API docs: // https://docs.gitlab.com/ee/api/resource_label_events.html#list-group-epic-label-events func (s *ResourceLabelEventsService) ListGroupEpicLabelEvents(gid interface{}, epic int, opt *ListLabelEventsOptions, options ...RequestOptionFunc) ([]*LabelEvent, *Response, error) { group, err := parseID(gid) if err != nil { return nil, nil, err } u := fmt.Sprintf("groups/%s/epics/%d/resource_label_events", PathEscape(group), epic) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } var ls []*LabelEvent resp, err := s.client.Do(req, &ls) if err != nil { return nil, resp, err } return ls, resp, nil } // GetGroupEpicLabelEvent gets a single group epic label event. // // GitLab API docs: // https://docs.gitlab.com/ee/api/resource_label_events.html#get-single-epic-label-event func (s *ResourceLabelEventsService) GetGroupEpicLabelEvent(gid interface{}, epic int, event int, options ...RequestOptionFunc) (*LabelEvent, *Response, error) { group, err := parseID(gid) if err != nil { return nil, nil, err } u := fmt.Sprintf("groups/%s/epics/%d/resource_label_events/%d", PathEscape(group), epic, event) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } l := new(LabelEvent) resp, err := s.client.Do(req, l) if err != nil { return nil, resp, err } return l, resp, nil } // ListMergeRequestsLabelEvents retrieves resource label events for the specified // project and merge request. // // GitLab API docs: // https://docs.gitlab.com/ee/api/resource_label_events.html#list-project-merge-request-label-events func (s *ResourceLabelEventsService) ListMergeRequestsLabelEvents(pid interface{}, request int, opt *ListLabelEventsOptions, options ...RequestOptionFunc) ([]*LabelEvent, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/merge_requests/%d/resource_label_events", PathEscape(project), request) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } var ls []*LabelEvent resp, err := s.client.Do(req, &ls) if err != nil { return nil, resp, err } return ls, resp, nil } // GetMergeRequestLabelEvent gets a single merge request label event. // // GitLab API docs: // https://docs.gitlab.com/ee/api/resource_label_events.html#get-single-merge-request-label-event func (s *ResourceLabelEventsService) GetMergeRequestLabelEvent(pid interface{}, request int, event int, options ...RequestOptionFunc) (*LabelEvent, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/merge_requests/%d/resource_label_events/%d", PathEscape(project), request, event) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } l := new(LabelEvent) resp, err := s.client.Do(req, l) if err != nil { return nil, resp, err } return l, resp, nil } golang-gitlab-gitlab-org-api-client-go-0.123.0/resource_label_events_test.go000066400000000000000000000347771475761473200270410ustar00rootroot00000000000000package gitlab import ( "fmt" "net/http" "testing" "github.com/stretchr/testify/require" ) func TestResourceLabelEventsService_ListIssueLabelEvents(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/5/issues/11/resource_label_events", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprintf(w, ` [ { "id": 142, "user": { "id": 1, "name": "Administrator", "username": "root", "state": "active", "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon", "web_url": "http://gitlab.example.com/root" }, "resource_type": "Issue", "resource_id": 253, "label": { "id": 73, "name": "a1", "color": "#34495E", "description": "" }, "action": "add" } ] `) }) want := []*LabelEvent{{ ID: 142, Action: "add", ResourceType: "Issue", ResourceID: 253, User: struct { ID int `json:"id"` Name string `json:"name"` Username string `json:"username"` State string `json:"state"` AvatarURL string `json:"avatar_url"` WebURL string `json:"web_url"` }{ ID: 1, Name: "Administrator", Username: "root", State: "active", AvatarURL: "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon", WebURL: "http://gitlab.example.com/root", }, Label: struct { ID int `json:"id"` Name string `json:"name"` Color string `json:"color"` TextColor string `json:"text_color"` Description string `json:"description"` }{ ID: 73, Name: "a1", Color: "#34495E", TextColor: "", Description: "", }, }} les, resp, err := client.ResourceLabelEvents.ListIssueLabelEvents(5, 11, nil) require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, want, les) les, resp, err = client.ResourceLabelEvents.ListIssueLabelEvents(1.5, 11, nil) require.EqualError(t, err, "invalid ID type 1.5, the ID must be an int or a string") require.Nil(t, resp) require.Nil(t, les) les, resp, err = client.ResourceLabelEvents.ListIssueLabelEvents(5, 11, nil, errorOption) require.EqualError(t, err, "RequestOptionFunc returns an error") require.Nil(t, resp) require.Nil(t, les) les, resp, err = client.ResourceLabelEvents.ListIssueLabelEvents(6, 11, nil) require.Error(t, err) require.Nil(t, les) require.Equal(t, http.StatusNotFound, resp.StatusCode) } func TestResourceLabelEventsService_GetIssueLabelEvent(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/5/issues/11/resource_label_events/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprintf(w, ` { "id": 1, "user": { "id": 1, "name": "Administrator", "username": "root", "state": "active", "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon", "web_url": "http://gitlab.example.com/root" }, "resource_type": "Issue", "resource_id": 253, "label": { "id": 73, "name": "a1", "color": "#34495E", "description": "" }, "action": "add" }`, ) }) want := &LabelEvent{ ID: 1, Action: "add", ResourceType: "Issue", ResourceID: 253, User: struct { ID int `json:"id"` Name string `json:"name"` Username string `json:"username"` State string `json:"state"` AvatarURL string `json:"avatar_url"` WebURL string `json:"web_url"` }{ ID: 1, Name: "Administrator", Username: "root", State: "active", AvatarURL: "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon", WebURL: "http://gitlab.example.com/root", }, Label: struct { ID int `json:"id"` Name string `json:"name"` Color string `json:"color"` TextColor string `json:"text_color"` Description string `json:"description"` }{ ID: 73, Name: "a1", Color: "#34495E", TextColor: "", Description: "", }, } le, resp, err := client.ResourceLabelEvents.GetIssueLabelEvent(5, 11, 1) require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, want, le) le, resp, err = client.ResourceLabelEvents.GetIssueLabelEvent(1.5, 11, 1) require.EqualError(t, err, "invalid ID type 1.5, the ID must be an int or a string") require.Nil(t, resp) require.Nil(t, le) le, resp, err = client.ResourceLabelEvents.GetIssueLabelEvent(5, 11, 1, errorOption) require.EqualError(t, err, "RequestOptionFunc returns an error") require.Nil(t, resp) require.Nil(t, le) le, resp, err = client.ResourceLabelEvents.GetIssueLabelEvent(6, 11, 1) require.Error(t, err) require.Nil(t, le) require.Equal(t, http.StatusNotFound, resp.StatusCode) } func TestResourceLabelEventsService_ListGroupEpicLabelEvents(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/groups/1/epics/11/resource_label_events", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprintf(w, ` [ { "id": 106, "user": { "id": 1, "name": "Administrator", "username": "root", "state": "active", "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon", "web_url": "http://gitlab.example.com/root" }, "resource_type": "Epic", "resource_id": 33, "label": { "id": 73, "name": "a1", "color": "#34495E", "description": "" }, "action": "add" } ] `) }) want := []*LabelEvent{{ ID: 106, Action: "add", ResourceType: "Epic", ResourceID: 33, User: struct { ID int `json:"id"` Name string `json:"name"` Username string `json:"username"` State string `json:"state"` AvatarURL string `json:"avatar_url"` WebURL string `json:"web_url"` }{ ID: 1, Name: "Administrator", Username: "root", State: "active", AvatarURL: "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon", WebURL: "http://gitlab.example.com/root", }, Label: struct { ID int `json:"id"` Name string `json:"name"` Color string `json:"color"` TextColor string `json:"text_color"` Description string `json:"description"` }{ ID: 73, Name: "a1", Color: "#34495E", TextColor: "", Description: "", }, }} les, resp, err := client.ResourceLabelEvents.ListGroupEpicLabelEvents(1, 11, nil) require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, want, les) les, resp, err = client.ResourceLabelEvents.ListGroupEpicLabelEvents(1.5, 11, nil) require.EqualError(t, err, "invalid ID type 1.5, the ID must be an int or a string") require.Nil(t, resp) require.Nil(t, les) les, resp, err = client.ResourceLabelEvents.ListGroupEpicLabelEvents(1, 11, nil, errorOption) require.EqualError(t, err, "RequestOptionFunc returns an error") require.Nil(t, resp) require.Nil(t, les) les, resp, err = client.ResourceLabelEvents.ListGroupEpicLabelEvents(6, 11, nil) require.Error(t, err) require.Nil(t, les) require.Equal(t, http.StatusNotFound, resp.StatusCode) } func TestResourceLabelEventsService_GetGroupEpicLabelEvent(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/groups/1/epics/11/resource_label_events/107", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprintf(w, ` { "id": 107, "user": { "id": 1, "name": "Administrator", "username": "root", "state": "active", "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon", "web_url": "http://gitlab.example.com/root" }, "resource_type": "Epic", "resource_id": 33, "label": { "id": 73, "name": "a1", "color": "#34495E", "description": "" }, "action": "add" } `) }) want := &LabelEvent{ ID: 107, Action: "add", ResourceType: "Epic", ResourceID: 33, User: struct { ID int `json:"id"` Name string `json:"name"` Username string `json:"username"` State string `json:"state"` AvatarURL string `json:"avatar_url"` WebURL string `json:"web_url"` }{ ID: 1, Name: "Administrator", Username: "root", State: "active", AvatarURL: "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon", WebURL: "http://gitlab.example.com/root", }, Label: struct { ID int `json:"id"` Name string `json:"name"` Color string `json:"color"` TextColor string `json:"text_color"` Description string `json:"description"` }{ ID: 73, Name: "a1", Color: "#34495E", TextColor: "", Description: "", }, } le, resp, err := client.ResourceLabelEvents.GetGroupEpicLabelEvent(1, 11, 107) require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, want, le) le, resp, err = client.ResourceLabelEvents.GetGroupEpicLabelEvent(1.5, 11, 107) require.EqualError(t, err, "invalid ID type 1.5, the ID must be an int or a string") require.Nil(t, resp) require.Nil(t, le) le, resp, err = client.ResourceLabelEvents.GetGroupEpicLabelEvent(1, 11, 107, errorOption) require.EqualError(t, err, "RequestOptionFunc returns an error") require.Nil(t, resp) require.Nil(t, le) le, resp, err = client.ResourceLabelEvents.GetGroupEpicLabelEvent(6, 11, 107) require.Error(t, err) require.Nil(t, le) require.Equal(t, http.StatusNotFound, resp.StatusCode) } func TestResourceLabelEventsService_ListMergeRequestsLabelEvents(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/5/merge_requests/11/resource_label_events", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprintf(w, ` [ { "id": 119, "user": { "id": 1, "name": "Administrator", "username": "root", "state": "active", "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon", "web_url": "http://gitlab.example.com/root" }, "resource_type": "MergeRequest", "resource_id": 28, "label": { "id": 74, "name": "p1", "color": "#0033CC", "description": "" }, "action": "add" } ] `) }) want := []*LabelEvent{{ ID: 119, Action: "add", ResourceType: "MergeRequest", ResourceID: 28, User: struct { ID int `json:"id"` Name string `json:"name"` Username string `json:"username"` State string `json:"state"` AvatarURL string `json:"avatar_url"` WebURL string `json:"web_url"` }{ ID: 1, Name: "Administrator", Username: "root", State: "active", AvatarURL: "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon", WebURL: "http://gitlab.example.com/root", }, Label: struct { ID int `json:"id"` Name string `json:"name"` Color string `json:"color"` TextColor string `json:"text_color"` Description string `json:"description"` }{ ID: 74, Name: "p1", Color: "#0033CC", TextColor: "", Description: "", }, }} les, resp, err := client.ResourceLabelEvents.ListMergeRequestsLabelEvents(5, 11, nil) require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, want, les) les, resp, err = client.ResourceLabelEvents.ListMergeRequestsLabelEvents(1.5, 11, nil) require.EqualError(t, err, "invalid ID type 1.5, the ID must be an int or a string") require.Nil(t, resp) require.Nil(t, les) les, resp, err = client.ResourceLabelEvents.ListMergeRequestsLabelEvents(5, 11, nil, errorOption) require.EqualError(t, err, "RequestOptionFunc returns an error") require.Nil(t, resp) require.Nil(t, les) les, resp, err = client.ResourceLabelEvents.ListMergeRequestsLabelEvents(6, 11, nil) require.Error(t, err) require.Nil(t, les) require.Equal(t, http.StatusNotFound, resp.StatusCode) } func TestResourceLabelEventsService_GetMergeRequestLabelEvent(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/5/merge_requests/11/resource_label_events/120", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprintf(w, ` { "id": 120, "user": { "id": 1, "name": "Administrator", "username": "root", "state": "active", "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon", "web_url": "http://gitlab.example.com/root" }, "resource_type": "MergeRequest", "resource_id": 28, "label": { "id": 74, "name": "p1", "color": "#0033CC", "description": "" }, "action": "add" } `) }) want := &LabelEvent{ ID: 120, Action: "add", ResourceType: "MergeRequest", ResourceID: 28, User: struct { ID int `json:"id"` Name string `json:"name"` Username string `json:"username"` State string `json:"state"` AvatarURL string `json:"avatar_url"` WebURL string `json:"web_url"` }{ ID: 1, Name: "Administrator", Username: "root", State: "active", AvatarURL: "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon", WebURL: "http://gitlab.example.com/root", }, Label: struct { ID int `json:"id"` Name string `json:"name"` Color string `json:"color"` TextColor string `json:"text_color"` Description string `json:"description"` }{ ID: 74, Name: "p1", Color: "#0033CC", TextColor: "", Description: "", }, } le, resp, err := client.ResourceLabelEvents.GetMergeRequestLabelEvent(5, 11, 120) require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, want, le) le, resp, err = client.ResourceLabelEvents.GetMergeRequestLabelEvent(1.5, 11, 120) require.EqualError(t, err, "invalid ID type 1.5, the ID must be an int or a string") require.Nil(t, resp) require.Nil(t, le) le, resp, err = client.ResourceLabelEvents.GetMergeRequestLabelEvent(5, 11, 120, errorOption) require.EqualError(t, err, "RequestOptionFunc returns an error") require.Nil(t, resp) require.Nil(t, le) le, resp, err = client.ResourceLabelEvents.GetMergeRequestLabelEvent(6, 11, 120) require.Error(t, err) require.Nil(t, le) require.Equal(t, http.StatusNotFound, resp.StatusCode) } golang-gitlab-gitlab-org-api-client-go-0.123.0/resource_milestone_events.go000066400000000000000000000115521475761473200267040ustar00rootroot00000000000000// // Copyright 2022, Mai Lapyst // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "fmt" "net/http" "time" ) // ResourceMilestoneEventsService handles communication with the event related // methods of the GitLab API. // // GitLab API docs: https://docs.gitlab.com/ee/api/resource_milestone_events.html type ResourceMilestoneEventsService struct { client *Client } // MilestoneEvent represents a resource milestone event. // // GitLab API docs: https://docs.gitlab.com/ee/api/resource_milestone_events.html type MilestoneEvent struct { ID int `json:"id"` User *BasicUser `json:"user"` CreatedAt *time.Time `json:"created_at"` ResourceType string `json:"resource_type"` ResourceID int `json:"resource_id"` Milestone *Milestone `json:"milestone"` Action string `json:"action"` } // ListMilestoneEventsOptions represents the options for all resource state events // list methods. // // GitLab API docs: // https://docs.gitlab.com/ee/api/resource_milestone_events.html#list-project-issue-milestone-events type ListMilestoneEventsOptions struct { ListOptions } // ListIssueMilestoneEvents retrieves resource milestone events for the specified // project and issue. // // GitLab API docs: // https://docs.gitlab.com/ee/api/resource_milestone_events.html#list-project-issue-milestone-events func (s *ResourceMilestoneEventsService) ListIssueMilestoneEvents(pid interface{}, issue int, opt *ListMilestoneEventsOptions, options ...RequestOptionFunc) ([]*MilestoneEvent, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/issues/%d/resource_milestone_events", PathEscape(project), issue) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } var mes []*MilestoneEvent resp, err := s.client.Do(req, &mes) if err != nil { return nil, resp, err } return mes, resp, nil } // GetIssueMilestoneEvent gets a single issue milestone event. // // GitLab API docs: // https://docs.gitlab.com/ee/api/resource_milestone_events.html#get-single-issue-milestone-event func (s *ResourceMilestoneEventsService) GetIssueMilestoneEvent(pid interface{}, issue int, event int, options ...RequestOptionFunc) (*MilestoneEvent, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/issues/%d/resource_milestone_events/%d", PathEscape(project), issue, event) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } me := new(MilestoneEvent) resp, err := s.client.Do(req, me) if err != nil { return nil, resp, err } return me, resp, nil } // ListMergeMilestoneEvents retrieves resource milestone events for the specified // project and merge request. // // GitLab API docs: // https://docs.gitlab.com/ee/api/resource_milestone_events.html#list-project-merge-request-milestone-events func (s *ResourceMilestoneEventsService) ListMergeMilestoneEvents(pid interface{}, request int, opt *ListMilestoneEventsOptions, options ...RequestOptionFunc) ([]*MilestoneEvent, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/merge_requests/%d/resource_milestone_events", PathEscape(project), request) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } var mes []*MilestoneEvent resp, err := s.client.Do(req, &mes) if err != nil { return nil, resp, err } return mes, resp, nil } // GetMergeRequestMilestoneEvent gets a single merge request milestone event. // // GitLab API docs: // https://docs.gitlab.com/ee/api/resource_milestone_events.html#get-single-merge-request-milestone-event func (s *ResourceMilestoneEventsService) GetMergeRequestMilestoneEvent(pid interface{}, request int, event int, options ...RequestOptionFunc) (*MilestoneEvent, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/merge_requests/%d/resource_milestone_events/%d", PathEscape(project), request, event) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } me := new(MilestoneEvent) resp, err := s.client.Do(req, me) if err != nil { return nil, resp, err } return me, resp, nil } golang-gitlab-gitlab-org-api-client-go-0.123.0/resource_milestone_events_test.go000066400000000000000000000152701475761473200277440ustar00rootroot00000000000000package gitlab import ( "fmt" "net/http" "testing" "github.com/stretchr/testify/require" ) func ListIssueMilestoneEvents(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/5/issues/11/resource_milestone_events", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprintf(w, `[ { "id": 142, "user": { "id": 1, "name": "Administrator", "username": "root", "state": "active", "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon", "web_url": "http://gitlab.example.com/root" }, "resource_type": "Issue", "resource_id": 11, "milestone": { "id": 61, "iid": 9, "project_id": 7, "title": "v1.2", "description": "Ipsum Lorem", "state": "active", "web_url": "http://gitlab.example.com:3000/group/project/-/milestones/9" }, "action": "add" } ]`) }) opt := &ListMilestoneEventsOptions{ListOptions{Page: 1, PerPage: 10}} mes, _, err := client.ResourceMilestoneEvents.ListIssueMilestoneEvents(5, 11, opt) require.NoError(t, err) want := []*MilestoneEvent{{ ID: 142, User: &BasicUser{ ID: 1, Username: "root", Name: "Administrator", State: "active", AvatarURL: "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon", WebURL: "http://gitlab.example.com/root", }, ResourceType: "Issue", ResourceID: 11, Milestone: &Milestone{ ID: 61, IID: 9, ProjectID: 7, Title: "v1.2", Description: "Ipsum Lorem", State: "active", WebURL: "http://gitlab.example.com:3000/group/project/-/milestones/9", }, Action: "add", }} require.Equal(t, want, mes) } func GetIssueMilestoneEvent(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/5/issues/11/resource_milestone_events/143", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprintf(w, ` { "id": 143, "user": { "id": 1, "name": "Administrator", "username": "root", "state": "active", "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon", "web_url": "http://gitlab.example.com/root" }, "resource_type": "Issue", "resource_id": 11, "milestone": { "id": 61, "iid": 9, "project_id": 7, "title": "v1.2", "description": "Ipsum Lorem", "state": "active", "web_url": "http://gitlab.example.com:3000/group/project/-/milestones/9" }, "action": "remove" }`, ) }) me, _, err := client.ResourceMilestoneEvents.GetIssueMilestoneEvent(5, 11, 143) require.NoError(t, err) want := &MilestoneEvent{ ID: 143, User: &BasicUser{ ID: 1, Username: "root", Name: "Administrator", State: "active", AvatarURL: "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon", WebURL: "http://gitlab.example.com/root", }, ResourceType: "Issue", ResourceID: 11, Milestone: &Milestone{ ID: 61, IID: 9, ProjectID: 7, Title: "v1.2", Description: "Ipsum Lorem", State: "active", WebURL: "http://gitlab.example.com:3000/group/project/-/milestones/9", }, Action: "remove", } require.Equal(t, want, me) } func ListMergeMilestoneEvents(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/5/merge_requests/11/resource_milestone_events", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprintf(w, `[ { "id": 142, "user": { "id": 1, "name": "Administrator", "username": "root", "state": "active", "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon", "web_url": "http://gitlab.example.com/root" }, "resource_type": "MergeRequest", "resource_id": 11, "milestone": { "id": 61, "iid": 9, "project_id": 7, "title": "v1.2", "description": "Ipsum Lorem", "state": "active", "web_url": "http://gitlab.example.com:3000/group/project/-/milestones/9" }, "action": "add" }]`, ) }) opt := &ListMilestoneEventsOptions{ListOptions{Page: 1, PerPage: 10}} ses, _, err := client.ResourceMilestoneEvents.ListMergeMilestoneEvents(5, 11, opt) require.NoError(t, err) want := []*MilestoneEvent{{ ID: 142, User: &BasicUser{ ID: 1, Username: "root", Name: "Administrator", State: "active", AvatarURL: "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon", WebURL: "http://gitlab.example.com/root", }, ResourceType: "MergeRequest", ResourceID: 11, Milestone: &Milestone{ ID: 61, IID: 9, ProjectID: 7, Title: "v1.2", Description: "Ipsum Lorem", State: "active", WebURL: "http://gitlab.example.com:3000/group/project/-/milestones/9", }, Action: "add", }} require.Equal(t, want, ses) } func GetMergeRequestMilestoneEvent(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/5/merge_requests/11/resource_milestone_events/120", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprintf(w, `{ "id": 120, "user": { "id": 1, "name": "Administrator", "username": "root", "state": "active", "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon", "web_url": "http://gitlab.example.com/root" }, "resource_type": "MergeRequest", "resource_id": 11, "milestone": { "id": 61, "iid": 9, "project_id": 7, "title": "v1.2", "description": "Ipsum Lorem", "state": "active", "web_url": "http://gitlab.example.com:3000/group/project/-/milestones/9" }, "action": "remove" }`) }) me, _, err := client.ResourceMilestoneEvents.GetMergeRequestMilestoneEvent(5, 11, 120) require.NoError(t, err) want := &MilestoneEvent{ ID: 120, User: &BasicUser{ ID: 1, Username: "root", Name: "Administrator", State: "active", AvatarURL: "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon", WebURL: "http://gitlab.example.com/root", }, ResourceType: "MergeRequest", ResourceID: 11, Milestone: &Milestone{ ID: 61, IID: 9, ProjectID: 7, Title: "v1.2", Description: "Ipsum Lorem", State: "active", WebURL: "http://gitlab.example.com:3000/group/project/-/milestones/9", }, Action: "remove", } require.Equal(t, want, me) } golang-gitlab-gitlab-org-api-client-go-0.123.0/resource_state_events.go000066400000000000000000000112251475761473200260220ustar00rootroot00000000000000// // Copyright 2021, Matthias Simon // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "fmt" "net/http" "time" ) // ResourceStateEventsService handles communication with the event related // methods of the GitLab API. // // GitLab API docs: https://docs.gitlab.com/ee/api/resource_state_events.html type ResourceStateEventsService struct { client *Client } // StateEvent represents a resource state event. // // GitLab API docs: https://docs.gitlab.com/ee/api/resource_state_events.html type StateEvent struct { ID int `json:"id"` User *BasicUser `json:"user"` CreatedAt *time.Time `json:"created_at"` ResourceType string `json:"resource_type"` ResourceID int `json:"resource_id"` State EventTypeValue `json:"state"` } // ListStateEventsOptions represents the options for all resource state events // list methods. // // GitLab API docs: // https://docs.gitlab.com/ee/api/resource_state_events.html#list-project-issue-state-events type ListStateEventsOptions struct { ListOptions } // ListIssueStateEvents retrieves resource state events for the specified // project and issue. // // GitLab API docs: // https://docs.gitlab.com/ee/api/resource_state_events.html#list-project-issue-state-events func (s *ResourceStateEventsService) ListIssueStateEvents(pid interface{}, issue int, opt *ListStateEventsOptions, options ...RequestOptionFunc) ([]*StateEvent, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/issues/%d/resource_state_events", PathEscape(project), issue) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } var ses []*StateEvent resp, err := s.client.Do(req, &ses) if err != nil { return nil, resp, err } return ses, resp, nil } // GetIssueStateEvent gets a single issue-state-event. // // GitLab API docs: // https://docs.gitlab.com/ee/api/resource_state_events.html#get-single-issue-state-event func (s *ResourceStateEventsService) GetIssueStateEvent(pid interface{}, issue int, event int, options ...RequestOptionFunc) (*StateEvent, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/issues/%d/resource_state_events/%d", PathEscape(project), issue, event) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } se := new(StateEvent) resp, err := s.client.Do(req, se) if err != nil { return nil, resp, err } return se, resp, nil } // ListMergeStateEvents retrieves resource state events for the specified // project and merge request. // // GitLab API docs: // https://docs.gitlab.com/ee/api/resource_state_events.html#list-project-merge-request-state-events func (s *ResourceStateEventsService) ListMergeStateEvents(pid interface{}, request int, opt *ListStateEventsOptions, options ...RequestOptionFunc) ([]*StateEvent, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/merge_requests/%d/resource_state_events", PathEscape(project), request) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } var ses []*StateEvent resp, err := s.client.Do(req, &ses) if err != nil { return nil, resp, err } return ses, resp, nil } // GetMergeRequestStateEvent gets a single merge request state event. // // GitLab API docs: // https://docs.gitlab.com/ee/api/resource_state_events.html#get-single-merge-request-state-event func (s *ResourceStateEventsService) GetMergeRequestStateEvent(pid interface{}, request int, event int, options ...RequestOptionFunc) (*StateEvent, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/merge_requests/%d/resource_state_events/%d", PathEscape(project), request, event) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } se := new(StateEvent) resp, err := s.client.Do(req, se) if err != nil { return nil, resp, err } return se, resp, nil } golang-gitlab-gitlab-org-api-client-go-0.123.0/resource_state_events_test.go000066400000000000000000000114311475761473200270600ustar00rootroot00000000000000package gitlab import ( "fmt" "net/http" "testing" "github.com/stretchr/testify/require" ) func TestResourceStateEventsService_ListIssueStateEvents(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/5/issues/11/resource_state_events", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprintf(w, `[ { "id": 142, "user": { "id": 1, "name": "Administrator", "username": "root", "state": "active", "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon", "web_url": "http://gitlab.example.com/root" }, "resource_type": "Issue", "resource_id": 11, "state": "opened" } ]`) }) opt := &ListStateEventsOptions{ListOptions{Page: 1, PerPage: 10}} ses, _, err := client.ResourceStateEvents.ListIssueStateEvents(5, 11, opt) require.NoError(t, err) want := []*StateEvent{{ ID: 142, User: &BasicUser{ ID: 1, Username: "root", Name: "Administrator", State: "active", AvatarURL: "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon", WebURL: "http://gitlab.example.com/root", }, ResourceType: "Issue", ResourceID: 11, State: "opened", }} require.Equal(t, want, ses) } func TestResourceStateEventsService_GetIssueStateEvent(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/5/issues/11/resource_state_events/143", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprintf(w, ` { "id": 143, "user": { "id": 1, "name": "Administrator", "username": "root", "state": "active", "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon", "web_url": "http://gitlab.example.com/root" }, "resource_type": "Issue", "resource_id": 11, "state": "closed" }`, ) }) se, _, err := client.ResourceStateEvents.GetIssueStateEvent(5, 11, 143) require.NoError(t, err) want := &StateEvent{ ID: 143, User: &BasicUser{ ID: 1, Username: "root", Name: "Administrator", State: "active", AvatarURL: "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon", WebURL: "http://gitlab.example.com/root", }, ResourceType: "Issue", ResourceID: 11, State: "closed", } require.Equal(t, want, se) } func TestResourceStateEventsService_ListMergeStateEvents(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/5/merge_requests/11/resource_state_events", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprintf(w, `[ { "id": 142, "user": { "id": 1, "name": "Administrator", "username": "root", "state": "active", "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon", "web_url": "http://gitlab.example.com/root" }, "resource_type": "MergeRequest", "resource_id": 11, "state": "opened" }]`, ) }) opt := &ListStateEventsOptions{ListOptions{Page: 1, PerPage: 10}} ses, _, err := client.ResourceStateEvents.ListMergeStateEvents(5, 11, opt) require.NoError(t, err) want := []*StateEvent{{ ID: 142, User: &BasicUser{ ID: 1, Username: "root", Name: "Administrator", State: "active", AvatarURL: "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon", WebURL: "http://gitlab.example.com/root", }, ResourceType: "MergeRequest", ResourceID: 11, State: "opened", }} require.Equal(t, want, ses) } func TestResourceStateEventsService_GetMergeRequestStateEvent(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/5/merge_requests/11/resource_state_events/120", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprintf(w, `{ "id": 120, "user": { "id": 1, "name": "Administrator", "username": "root", "state": "active", "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon", "web_url": "http://gitlab.example.com/root" }, "resource_type": "MergeRequest", "resource_id": 11, "state": "closed" }`) }) se, _, err := client.ResourceStateEvents.GetMergeRequestStateEvent(5, 11, 120) require.NoError(t, err) want := &StateEvent{ ID: 120, User: &BasicUser{ ID: 1, Username: "root", Name: "Administrator", State: "active", AvatarURL: "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon", WebURL: "http://gitlab.example.com/root", }, ResourceType: "MergeRequest", ResourceID: 11, State: "closed", } require.Equal(t, want, se) } golang-gitlab-gitlab-org-api-client-go-0.123.0/resource_weight_events.go000066400000000000000000000047241475761473200261770ustar00rootroot00000000000000// // Copyright 2021, Matthias Simon // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "fmt" "net/http" "time" ) // ResourceWeightEventsService handles communication with the event related // methods of the GitLab API. // // GitLab API docs: https://docs.gitlab.com/ee/api/resource_weight_events.html type ResourceWeightEventsService struct { client *Client } // WeightEvent represents a resource weight event. // // GitLab API docs: https://docs.gitlab.com/ee/api/resource_weight_events.html type WeightEvent struct { ID int `json:"id"` User *BasicUser `json:"user"` CreatedAt *time.Time `json:"created_at"` ResourceType string `json:"resource_type"` ResourceID int `json:"resource_id"` State EventTypeValue `json:"state"` IssueID int `json:"issue_id"` Weight int `json:"weight"` } // ListWeightEventsOptions represents the options for all resource weight events // list methods. // // GitLab API docs: // https://docs.gitlab.com/ee/api/resource_weight_events.html#list-project-issue-weight-events type ListWeightEventsOptions struct { ListOptions } // ListIssueWeightEvents retrieves resource weight events for the specified // project and issue. // // GitLab API docs: // https://docs.gitlab.com/ee/api/resource_weight_events.html#list-project-issue-weight-events func (s *ResourceWeightEventsService) ListIssueWeightEvents(pid interface{}, issue int, opt *ListWeightEventsOptions, options ...RequestOptionFunc) ([]*WeightEvent, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/issues/%d/resource_weight_events", PathEscape(project), issue) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } var wes []*WeightEvent resp, err := s.client.Do(req, &wes) if err != nil { return nil, resp, err } return wes, resp, nil } golang-gitlab-gitlab-org-api-client-go-0.123.0/resource_weight_events_test.go000066400000000000000000000025621475761473200272340ustar00rootroot00000000000000package gitlab import ( "fmt" "net/http" "testing" "time" "github.com/stretchr/testify/require" ) func TestResourceWeightEventsService_ListIssueWightEvents(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/5/issues/11/resource_weight_events", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprintf(w, `[ { "id": 142, "user": { "id": 1, "name": "Administrator", "username": "root", "state": "active", "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon", "web_url": "http://gitlab.example.com/root" }, "created_at": "2018-08-20T13:38:20.077Z", "issue_id": 253, "weight": 3 } ]`) }) opt := &ListWeightEventsOptions{ListOptions{Page: 1, PerPage: 10}} wes, _, err := client.ResourceWeightEvents.ListIssueWeightEvents(5, 11, opt) require.NoError(t, err) want := []*WeightEvent{{ ID: 142, User: &BasicUser{ ID: 1, Username: "root", Name: "Administrator", State: "active", AvatarURL: "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon", WebURL: "http://gitlab.example.com/root", }, CreatedAt: Ptr(time.Date(2018, time.August, 20, 13, 38, 20, 77000000, time.UTC)), IssueID: 253, Weight: 3, }} require.Equal(t, want, wes) } golang-gitlab-gitlab-org-api-client-go-0.123.0/runners.go000066400000000000000000000471631475761473200231150ustar00rootroot00000000000000// // Copyright 2021, Sander van Harmelen // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "fmt" "net/http" "time" ) // RunnersService handles communication with the runner related methods of the // GitLab API. // // GitLab API docs: https://docs.gitlab.com/ee/api/runners.html type RunnersService struct { client *Client } // Runner represents a GitLab CI Runner. // // GitLab API docs: https://docs.gitlab.com/ee/api/runners.html type Runner struct { ID int `json:"id"` Description string `json:"description"` Active bool `json:"active"` Paused bool `json:"paused"` IsShared bool `json:"is_shared"` IPAddress string `json:"ip_address"` RunnerType string `json:"runner_type"` Name string `json:"name"` Online bool `json:"online"` Status string `json:"status"` Token string `json:"token"` TokenExpiresAt *time.Time `json:"token_expires_at"` } // RunnerDetails represents the GitLab CI runner details. // // GitLab API docs: https://docs.gitlab.com/ee/api/runners.html type RunnerDetails struct { Paused bool `json:"paused"` Architecture string `json:"architecture"` Description string `json:"description"` ID int `json:"id"` IPAddress string `json:"ip_address"` IsShared bool `json:"is_shared"` RunnerType string `json:"runner_type"` ContactedAt *time.Time `json:"contacted_at"` MaintenanceNote string `json:"maintenance_note"` Name string `json:"name"` Online bool `json:"online"` Status string `json:"status"` Platform string `json:"platform"` Projects []struct { ID int `json:"id"` Name string `json:"name"` NameWithNamespace string `json:"name_with_namespace"` Path string `json:"path"` PathWithNamespace string `json:"path_with_namespace"` } `json:"projects"` Token string `json:"token"` Revision string `json:"revision"` TagList []string `json:"tag_list"` RunUntagged bool `json:"run_untagged"` Version string `json:"version"` Locked bool `json:"locked"` AccessLevel string `json:"access_level"` MaximumTimeout int `json:"maximum_timeout"` Groups []struct { ID int `json:"id"` Name string `json:"name"` WebURL string `json:"web_url"` } `json:"groups"` // Deprecated: Use Paused instead. (Deprecated in GitLab 14.8) Active bool `json:"active"` } // ListRunnersOptions represents the available ListRunners() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/runners.html#list-owned-runners type ListRunnersOptions struct { ListOptions Type *string `url:"type,omitempty" json:"type,omitempty"` Status *string `url:"status,omitempty" json:"status,omitempty"` Paused *bool `url:"paused,omitempty" json:"paused,omitempty"` TagList *[]string `url:"tag_list,comma,omitempty" json:"tag_list,omitempty"` // Deprecated: Use Type or Status instead. Scope *string `url:"scope,omitempty" json:"scope,omitempty"` } // ListRunners gets a list of runners accessible by the authenticated user. // // GitLab API docs: // https://docs.gitlab.com/ee/api/runners.html#list-owned-runners func (s *RunnersService) ListRunners(opt *ListRunnersOptions, options ...RequestOptionFunc) ([]*Runner, *Response, error) { req, err := s.client.NewRequest(http.MethodGet, "runners", opt, options) if err != nil { return nil, nil, err } var rs []*Runner resp, err := s.client.Do(req, &rs) if err != nil { return nil, resp, err } return rs, resp, nil } // ListAllRunners gets a list of all runners in the GitLab instance. Access is // restricted to users with admin privileges. // // GitLab API docs: // https://docs.gitlab.com/ee/api/runners.html#list-all-runners func (s *RunnersService) ListAllRunners(opt *ListRunnersOptions, options ...RequestOptionFunc) ([]*Runner, *Response, error) { req, err := s.client.NewRequest(http.MethodGet, "runners/all", opt, options) if err != nil { return nil, nil, err } var rs []*Runner resp, err := s.client.Do(req, &rs) if err != nil { return nil, resp, err } return rs, resp, nil } // GetRunnerDetails returns details for given runner. // // GitLab API docs: // https://docs.gitlab.com/ee/api/runners.html#get-runners-details func (s *RunnersService) GetRunnerDetails(rid interface{}, options ...RequestOptionFunc) (*RunnerDetails, *Response, error) { runner, err := parseID(rid) if err != nil { return nil, nil, err } u := fmt.Sprintf("runners/%s", runner) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } rs := new(RunnerDetails) resp, err := s.client.Do(req, &rs) if err != nil { return nil, resp, err } return rs, resp, nil } // UpdateRunnerDetailsOptions represents the available UpdateRunnerDetails() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/runners.html#update-runners-details type UpdateRunnerDetailsOptions struct { Description *string `url:"description,omitempty" json:"description,omitempty"` Paused *bool `url:"paused,omitempty" json:"paused,omitempty"` TagList *[]string `url:"tag_list[],omitempty" json:"tag_list,omitempty"` RunUntagged *bool `url:"run_untagged,omitempty" json:"run_untagged,omitempty"` Locked *bool `url:"locked,omitempty" json:"locked,omitempty"` AccessLevel *string `url:"access_level,omitempty" json:"access_level,omitempty"` MaximumTimeout *int `url:"maximum_timeout,omitempty" json:"maximum_timeout,omitempty"` MaintenanceNote *string `url:"maintenance_note,omitempty" json:"maintenance_note,omitempty"` // Deprecated: Use Paused instead. (Deprecated in GitLab 14.8) Active *bool `url:"active,omitempty" json:"active,omitempty"` } // UpdateRunnerDetails updates details for a given runner. // // GitLab API docs: // https://docs.gitlab.com/ee/api/runners.html#update-runners-details func (s *RunnersService) UpdateRunnerDetails(rid interface{}, opt *UpdateRunnerDetailsOptions, options ...RequestOptionFunc) (*RunnerDetails, *Response, error) { runner, err := parseID(rid) if err != nil { return nil, nil, err } u := fmt.Sprintf("runners/%s", runner) req, err := s.client.NewRequest(http.MethodPut, u, opt, options) if err != nil { return nil, nil, err } rs := new(RunnerDetails) resp, err := s.client.Do(req, &rs) if err != nil { return nil, resp, err } return rs, resp, nil } // RemoveRunner removes a runner. // // GitLab API docs: // https://docs.gitlab.com/ee/api/runners.html#delete-a-runner func (s *RunnersService) RemoveRunner(rid interface{}, options ...RequestOptionFunc) (*Response, error) { runner, err := parseID(rid) if err != nil { return nil, err } u := fmt.Sprintf("runners/%s", runner) req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } // ListRunnerJobsOptions represents the available ListRunnerJobs() // options. Status can be one of: running, success, failed, canceled. // // GitLab API docs: // https://docs.gitlab.com/ee/api/runners.html#list-runners-jobs type ListRunnerJobsOptions struct { ListOptions Status *string `url:"status,omitempty" json:"status,omitempty"` OrderBy *string `url:"order_by,omitempty" json:"order_by,omitempty"` Sort *string `url:"sort,omitempty" json:"sort,omitempty"` } // ListRunnerJobs gets a list of jobs that are being processed or were processed by specified Runner. // // GitLab API docs: // https://docs.gitlab.com/ee/api/runners.html#list-runners-jobs func (s *RunnersService) ListRunnerJobs(rid interface{}, opt *ListRunnerJobsOptions, options ...RequestOptionFunc) ([]*Job, *Response, error) { runner, err := parseID(rid) if err != nil { return nil, nil, err } u := fmt.Sprintf("runners/%s/jobs", runner) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } var rs []*Job resp, err := s.client.Do(req, &rs) if err != nil { return nil, resp, err } return rs, resp, nil } // ListProjectRunnersOptions represents the available ListProjectRunners() // options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/runners.html#list-projects-runners type ListProjectRunnersOptions ListRunnersOptions // ListProjectRunners gets a list of runners accessible by the authenticated user. // // GitLab API docs: // https://docs.gitlab.com/ee/api/runners.html#list-projects-runners func (s *RunnersService) ListProjectRunners(pid interface{}, opt *ListProjectRunnersOptions, options ...RequestOptionFunc) ([]*Runner, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/runners", PathEscape(project)) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } var rs []*Runner resp, err := s.client.Do(req, &rs) if err != nil { return nil, resp, err } return rs, resp, nil } // EnableProjectRunnerOptions represents the available EnableProjectRunner() // options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/runners.html#enable-a-runner-in-project type EnableProjectRunnerOptions struct { RunnerID int `json:"runner_id"` } // EnableProjectRunner enables an available specific runner in the project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/runners.html#enable-a-runner-in-project func (s *RunnersService) EnableProjectRunner(pid interface{}, opt *EnableProjectRunnerOptions, options ...RequestOptionFunc) (*Runner, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/runners", PathEscape(project)) req, err := s.client.NewRequest(http.MethodPost, u, opt, options) if err != nil { return nil, nil, err } r := new(Runner) resp, err := s.client.Do(req, &r) if err != nil { return nil, resp, err } return r, resp, nil } // DisableProjectRunner disables a specific runner from project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/runners.html#disable-a-runner-from-project func (s *RunnersService) DisableProjectRunner(pid interface{}, runner int, options ...RequestOptionFunc) (*Response, error) { project, err := parseID(pid) if err != nil { return nil, err } u := fmt.Sprintf("projects/%s/runners/%d", PathEscape(project), runner) req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } // ListGroupsRunnersOptions represents the available ListGroupsRunners() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/runners.html#list-groups-runners type ListGroupsRunnersOptions struct { ListOptions Type *string `url:"type,omitempty" json:"type,omitempty"` Status *string `url:"status,omitempty" json:"status,omitempty"` TagList *[]string `url:"tag_list,comma,omitempty" json:"tag_list,omitempty"` } // ListGroupsRunners lists all runners (specific and shared) available in the // group as well it’s ancestor groups. Shared runners are listed if at least one // shared runner is defined. // // GitLab API docs: // https://docs.gitlab.com/ee/api/runners.html#list-groups-runners func (s *RunnersService) ListGroupsRunners(gid interface{}, opt *ListGroupsRunnersOptions, options ...RequestOptionFunc) ([]*Runner, *Response, error) { group, err := parseID(gid) if err != nil { return nil, nil, err } u := fmt.Sprintf("groups/%s/runners", PathEscape(group)) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } var rs []*Runner resp, err := s.client.Do(req, &rs) if err != nil { return nil, resp, err } return rs, resp, nil } // RegisterNewRunnerOptions represents the available RegisterNewRunner() // options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/runners.html#register-a-new-runner type RegisterNewRunnerOptions struct { Token *string `url:"token" json:"token"` Description *string `url:"description,omitempty" json:"description,omitempty"` Info *RegisterNewRunnerInfoOptions `url:"info,omitempty" json:"info,omitempty"` Active *bool `url:"active,omitempty" json:"active,omitempty"` Paused *bool `url:"paused,omitempty" json:"paused,omitempty"` Locked *bool `url:"locked,omitempty" json:"locked,omitempty"` RunUntagged *bool `url:"run_untagged,omitempty" json:"run_untagged,omitempty"` TagList *[]string `url:"tag_list[],omitempty" json:"tag_list,omitempty"` AccessLevel *string `url:"access_level,omitempty" json:"access_level,omitempty"` MaximumTimeout *int `url:"maximum_timeout,omitempty" json:"maximum_timeout,omitempty"` MaintenanceNote *string `url:"maintenance_note,omitempty" json:"maintenance_note,omitempty"` } // RegisterNewRunnerInfoOptions represents the info hashmap parameter in // RegisterNewRunnerOptions. // // GitLab API docs: // https://docs.gitlab.com/ee/api/runners.html#register-a-new-runner type RegisterNewRunnerInfoOptions struct { Name *string `url:"name,omitempty" json:"name,omitempty"` Version *string `url:"version,omitempty" json:"version,omitempty"` Revision *string `url:"revision,omitempty" json:"revision,omitempty"` Platform *string `url:"platform,omitempty" json:"platform,omitempty"` Architecture *string `url:"architecture,omitempty" json:"architecture,omitempty"` } // RegisterNewRunner registers a new Runner for the instance. // // GitLab API docs: // https://docs.gitlab.com/ee/api/runners.html#register-a-new-runner func (s *RunnersService) RegisterNewRunner(opt *RegisterNewRunnerOptions, options ...RequestOptionFunc) (*Runner, *Response, error) { req, err := s.client.NewRequest(http.MethodPost, "runners", opt, options) if err != nil { return nil, nil, err } r := new(Runner) resp, err := s.client.Do(req, &r) if err != nil { return nil, resp, err } return r, resp, nil } // DeleteRegisteredRunnerOptions represents the available // DeleteRegisteredRunner() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/runners.html#delete-a-runner-by-authentication-token type DeleteRegisteredRunnerOptions struct { Token *string `url:"token" json:"token"` } // DeleteRegisteredRunner deletes a Runner by Token. // // GitLab API docs: // https://docs.gitlab.com/ee/api/runners.html#delete-a-runner-by-authentication-token func (s *RunnersService) DeleteRegisteredRunner(opt *DeleteRegisteredRunnerOptions, options ...RequestOptionFunc) (*Response, error) { req, err := s.client.NewRequest(http.MethodDelete, "runners", opt, options) if err != nil { return nil, err } return s.client.Do(req, nil) } // DeleteRegisteredRunnerByID deletes a runner by ID. // // GitLab API docs: // https://docs.gitlab.com/ee/api/runners.html#delete-a-runner-by-id func (s *RunnersService) DeleteRegisteredRunnerByID(rid int, options ...RequestOptionFunc) (*Response, error) { req, err := s.client.NewRequest(http.MethodDelete, fmt.Sprintf("runners/%d", rid), nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } // VerifyRegisteredRunnerOptions represents the available // VerifyRegisteredRunner() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/runners.html#verify-authentication-for-a-registered-runner type VerifyRegisteredRunnerOptions struct { Token *string `url:"token" json:"token"` } // VerifyRegisteredRunner registers a new runner for the instance. // // GitLab API docs: // https://docs.gitlab.com/ee/api/runners.html#verify-authentication-for-a-registered-runner func (s *RunnersService) VerifyRegisteredRunner(opt *VerifyRegisteredRunnerOptions, options ...RequestOptionFunc) (*Response, error) { req, err := s.client.NewRequest(http.MethodPost, "runners/verify", opt, options) if err != nil { return nil, err } return s.client.Do(req, nil) } type RunnerRegistrationToken struct { Token *string `url:"token" json:"token"` TokenExpiresAt *time.Time `url:"token_expires_at" json:"token_expires_at"` } // ResetInstanceRunnerRegistrationToken resets the instance runner registration // token. // // GitLab API docs: // https://docs.gitlab.com/ee/api/runners.html#reset-instances-runner-registration-token func (s *RunnersService) ResetInstanceRunnerRegistrationToken(options ...RequestOptionFunc) (*RunnerRegistrationToken, *Response, error) { req, err := s.client.NewRequest(http.MethodPost, "runners/reset_registration_token", nil, options) if err != nil { return nil, nil, err } r := new(RunnerRegistrationToken) resp, err := s.client.Do(req, &r) if err != nil { return nil, resp, err } return r, resp, nil } // ResetGroupRunnerRegistrationToken resets a group's runner registration token. // // GitLab API docs: // https://docs.gitlab.com/ee/api/runners.html#reset-groups-runner-registration-token func (s *RunnersService) ResetGroupRunnerRegistrationToken(gid interface{}, options ...RequestOptionFunc) (*RunnerRegistrationToken, *Response, error) { group, err := parseID(gid) if err != nil { return nil, nil, err } u := fmt.Sprintf("groups/%s/runners/reset_registration_token", PathEscape(group)) req, err := s.client.NewRequest(http.MethodPost, u, nil, options) if err != nil { return nil, nil, err } r := new(RunnerRegistrationToken) resp, err := s.client.Do(req, &r) if err != nil { return nil, resp, err } return r, resp, nil } // ResetGroupRunnerRegistrationToken resets a projects's runner registration token. // // GitLab API docs: // https://docs.gitlab.com/ee/api/runners.html#reset-projects-runner-registration-token func (s *RunnersService) ResetProjectRunnerRegistrationToken(pid interface{}, options ...RequestOptionFunc) (*RunnerRegistrationToken, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/runners/reset_registration_token", PathEscape(project)) req, err := s.client.NewRequest(http.MethodPost, u, nil, options) if err != nil { return nil, nil, err } r := new(RunnerRegistrationToken) resp, err := s.client.Do(req, &r) if err != nil { return nil, resp, err } return r, resp, nil } type RunnerAuthenticationToken struct { Token *string `url:"token" json:"token"` TokenExpiresAt *time.Time `url:"token_expires_at" json:"token_expires_at"` } // ResetRunnerAuthenticationToken resets a runner's authentication token. // // GitLab API docs: // https://docs.gitlab.com/ee/api/runners.html#reset-runners-authentication-token-by-using-the-runner-id func (s *RunnersService) ResetRunnerAuthenticationToken(rid int, options ...RequestOptionFunc) (*RunnerAuthenticationToken, *Response, error) { u := fmt.Sprintf("runners/%d/reset_authentication_token", rid) req, err := s.client.NewRequest(http.MethodPost, u, nil, options) if err != nil { return nil, nil, err } r := new(RunnerAuthenticationToken) resp, err := s.client.Do(req, &r) if err != nil { return nil, resp, err } return r, resp, nil } golang-gitlab-gitlab-org-api-client-go-0.123.0/runners_test.go000066400000000000000000000402501475761473200241420ustar00rootroot00000000000000// // Copyright 2021, Sander van Harmelen // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "fmt" "net/http" "reflect" "testing" "time" ) func TestDisableRunner(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/runners/2", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodDelete) w.WriteHeader(http.StatusNoContent) }) _, err := client.Runners.DisableProjectRunner(1, 2, nil) if err != nil { t.Fatalf("Runners.DisableProjectRunner returns an error: %v", err) } } func TestListRunnersJobs(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/runners/1/jobs", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, exampleListRunnerJobs) }) opt := &ListRunnerJobsOptions{} jobs, _, err := client.Runners.ListRunnerJobs(1, opt) if err != nil { t.Fatalf("Runners.ListRunnersJobs returns an error: %v", err) } pipeline := struct { ID int `json:"id"` ProjectID int `json:"project_id"` Ref string `json:"ref"` Sha string `json:"sha"` Status string `json:"status"` }{ ID: 8777, ProjectID: 3252, Ref: "master", Sha: "6c016b801a88f4bd31f927fc045b5c746a6f823e", Status: "failed", } want := []*Job{ { ID: 1, Status: "failed", Stage: "test", Name: "run_tests", Ref: "master", CreatedAt: Ptr(time.Date(2021, time.October, 22, 11, 59, 25, 201000000, time.UTC)), StartedAt: Ptr(time.Date(2021, time.October, 22, 11, 59, 33, 660000000, time.UTC)), FinishedAt: Ptr(time.Date(2021, time.October, 22, 15, 59, 25, 201000000, time.UTC)), Duration: 171.540594, QueuedDuration: 2.535766, User: &User{ ID: 368, Name: "John SMITH", Username: "john.smith", AvatarURL: "https://gitlab.example.com/uploads/-/system/user/avatar/368/avatar.png", State: "blocked", WebURL: "https://gitlab.example.com/john.smith", PublicEmail: "john.smith@example.com", }, Commit: &Commit{ ID: "6c016b801a88f4bd31f927fc045b5c746a6f823e", ShortID: "6c016b80", CreatedAt: Ptr(time.Date(2018, time.March, 21, 14, 41, 0, 0, time.UTC)), ParentIDs: []string{"6008b4902d40799ab11688e502d9f1f27f6d2e18"}, Title: "Update env for specific runner", Message: "Update env for specific runner\n", AuthorName: "John SMITH", AuthorEmail: "john.smith@example.com", AuthoredDate: Ptr(time.Date(2018, time.March, 21, 14, 41, 0, 0, time.UTC)), CommitterName: "John SMITH", CommitterEmail: "john.smith@example.com", CommittedDate: Ptr(time.Date(2018, time.March, 21, 14, 41, 0, 0, time.UTC)), WebURL: "https://gitlab.example.com/awesome/packages/common/-/commit/6c016b801a88f4bd31f927fc045b5c746a6f823e", }, Pipeline: pipeline, WebURL: "https://gitlab.example.com/awesome/packages/common/-/jobs/14606", Project: &Project{ ID: 3252, Description: "Common nodejs paquet for producer", Name: "common", NameWithNamespace: "awesome", Path: "common", PathWithNamespace: "awesome", CreatedAt: Ptr(time.Date(2018, time.February, 13, 9, 21, 48, 107000000, time.UTC)), }, }, } if !reflect.DeepEqual(want[0], jobs[0]) { t.Errorf("Runners.ListRunnersJobs returned %+v, want %+v", jobs[0], want[0]) } } func TestRemoveRunner(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/runners/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodDelete) w.WriteHeader(http.StatusNoContent) }) _, err := client.Runners.RemoveRunner(1, nil) if err != nil { t.Fatalf("Runners.RemoveARunner returns an error: %v", err) } } func TestUpdateRunnersDetails(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/runners/6", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPut) fmt.Fprint(w, exampleDetailResponse) }) opt := &UpdateRunnerDetailsOptions{} details, _, err := client.Runners.UpdateRunnerDetails(6, opt, nil) if err != nil { t.Fatalf("Runners.UpdateRunnersDetails returns an error: %v", err) } projects := []struct { ID int `json:"id"` Name string `json:"name"` NameWithNamespace string `json:"name_with_namespace"` Path string `json:"path"` PathWithNamespace string `json:"path_with_namespace"` }{{ ID: 1, Name: "GitLab Community Edition", NameWithNamespace: "GitLab.org / GitLab Community Edition", Path: "gitlab-ce", PathWithNamespace: "gitlab-org/gitlab-ce", }} want := &RunnerDetails{ Active: true, Description: "test-1-20150125-test", ID: 6, IsShared: false, RunnerType: "project_type", ContactedAt: Ptr(time.Date(2016, time.January, 25, 16, 39, 48, 166000000, time.UTC)), Online: true, Status: "online", Token: "205086a8e3b9a2b818ffac9b89d102", TagList: []string{"ruby", "mysql"}, RunUntagged: true, AccessLevel: "ref_protected", Projects: projects, MaximumTimeout: 3600, Locked: false, } if !reflect.DeepEqual(want, details) { t.Errorf("Runners.UpdateRunnersDetails returned %+v, want %+v", details, want) } } func TestGetRunnerDetails(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/runners/6", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, exampleDetailResponse) }) details, _, err := client.Runners.GetRunnerDetails(6, nil) if err != nil { t.Fatalf("Runners.GetRunnerDetails returns an error: %v", err) } projects := []struct { ID int `json:"id"` Name string `json:"name"` NameWithNamespace string `json:"name_with_namespace"` Path string `json:"path"` PathWithNamespace string `json:"path_with_namespace"` }{{ ID: 1, Name: "GitLab Community Edition", NameWithNamespace: "GitLab.org / GitLab Community Edition", Path: "gitlab-ce", PathWithNamespace: "gitlab-org/gitlab-ce", }} want := &RunnerDetails{ Active: true, Description: "test-1-20150125-test", ID: 6, IsShared: false, RunnerType: "project_type", ContactedAt: Ptr(time.Date(2016, time.January, 25, 16, 39, 48, 166000000, time.UTC)), Online: true, Status: "online", Token: "205086a8e3b9a2b818ffac9b89d102", TagList: []string{"ruby", "mysql"}, RunUntagged: true, AccessLevel: "ref_protected", Projects: projects, MaximumTimeout: 3600, Locked: false, } if !reflect.DeepEqual(want, details) { t.Errorf("Runners.UpdateRunnersDetails returned %+v, want %+v", details, want) } } func TestRegisterNewRunner(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/runners", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) w.WriteHeader(http.StatusCreated) fmt.Fprint(w, exampleRegisterNewRunner) }) opt := &RegisterNewRunnerOptions{} runner, resp, err := client.Runners.RegisterNewRunner(opt, nil) if err != nil { t.Fatalf("Runners.RegisterNewRunner returns an error: %v", err) } want := &Runner{ ID: 12345, Token: "6337ff461c94fd3fa32ba3b1ff4125", TokenExpiresAt: Ptr(time.Date(2016, time.January, 25, 16, 39, 48, 166000000, time.UTC)), } if !reflect.DeepEqual(want, runner) { t.Errorf("Runners.RegisterNewRunner returned %+v, want %+v", runner, want) } wantCode := 201 if !reflect.DeepEqual(wantCode, resp.StatusCode) { t.Errorf("Runners.DeleteRegisteredRunner returned status code %+v, want %+v", resp.StatusCode, wantCode) } } // Similar to TestRegisterNewRunner but sends info struct and some extra other // fields too. func TestRegisterNewRunnerInfo(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/runners", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) w.WriteHeader(http.StatusCreated) fmt.Fprint(w, `{ "id": 53, "description": "some description", "active": true, "ip_address": "1.2.3.4", "name": "some name", "online": true, "status": "online", "token": "1111122222333333444444", "token_expires_at": "2016-01-25T16:39:48.166Z" }`) }) opt := &RegisterNewRunnerOptions{ Token: Ptr("6337ff461c94fd3fa32ba3b1ff4125"), Description: Ptr("some description"), Info: &RegisterNewRunnerInfoOptions{ Ptr("some name"), Ptr("13.7.0"), Ptr("943fc252"), Ptr("linux"), Ptr("amd64"), }, Active: Ptr(true), Locked: Ptr(true), RunUntagged: Ptr(false), MaximumTimeout: Ptr(45), } runner, resp, err := client.Runners.RegisterNewRunner(opt, nil) if err != nil { t.Fatalf("Runners.RegisterNewRunner returns an error: %v", err) } want := &Runner{ ID: 53, Description: "some description", Active: true, IPAddress: "1.2.3.4", Name: "some name", Online: true, Status: "online", Token: "1111122222333333444444", TokenExpiresAt: Ptr(time.Date(2016, time.January, 25, 16, 39, 48, 166000000, time.UTC)), } if !reflect.DeepEqual(want, runner) { t.Errorf("Runners.RegisterNewRunner returned %+v, want %+v", runner, want) } wantCode := 201 if !reflect.DeepEqual(wantCode, resp.StatusCode) { t.Errorf("Runners.DeleteRegisteredRunner returned status code %+v, want %+v", resp.StatusCode, wantCode) } } func TestDeleteRegisteredRunner(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/runners", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodDelete) w.WriteHeader(http.StatusNoContent) }) opt := &DeleteRegisteredRunnerOptions{} resp, err := client.Runners.DeleteRegisteredRunner(opt, nil) if err != nil { t.Fatalf("Runners.DeleteRegisteredRunner returns an error: %v", err) } want := 204 if !reflect.DeepEqual(want, resp.StatusCode) { t.Errorf("Runners.DeleteRegisteredRunner returned returned status code %+v, want %+v", resp.StatusCode, want) } } func TestDeleteRegisteredRunnerByID(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/runners/11111", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodDelete) w.WriteHeader(http.StatusNoContent) }) rid := 11111 resp, err := client.Runners.DeleteRegisteredRunnerByID(rid, nil) if err != nil { t.Fatalf("Runners.DeleteRegisteredRunnerByID returns an error: %v", err) } want := 204 if !reflect.DeepEqual(want, resp.StatusCode) { t.Errorf("Runners.DeleteRegisteredRunnerByID returned returned status code %+v, want %+v", resp.StatusCode, want) } } func TestVerifyRegisteredRunner(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/runners/verify", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) w.WriteHeader(http.StatusOK) }) opt := &VerifyRegisteredRunnerOptions{} resp, err := client.Runners.VerifyRegisteredRunner(opt, nil) if err != nil { t.Fatalf("Runners.VerifyRegisteredRunner returns an error: %v", err) } want := 200 if !reflect.DeepEqual(want, resp.StatusCode) { t.Errorf("Runners.VerifyRegisteredRunner returned returned status code %+v, want %+v", resp.StatusCode, want) } } func TestResetInstanceRunnerRegistrationToken(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/runners/reset_registration_token", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) w.WriteHeader(http.StatusCreated) fmt.Fprint(w, `{ "token": "6337ff461c94fd3fa32ba3b1ff4125", "token_expires_at": "2016-01-25T16:39:48.166Z" }`) }) token, resp, err := client.Runners.ResetInstanceRunnerRegistrationToken(nil) if err != nil { t.Fatalf("Runners.ResetInstanceRunnerRegistrationToken returns an error: %v", err) } want := &RunnerRegistrationToken{ Token: Ptr("6337ff461c94fd3fa32ba3b1ff4125"), TokenExpiresAt: Ptr(time.Date(2016, time.January, 25, 16, 39, 48, 166000000, time.UTC)), } if !reflect.DeepEqual(want, token) { t.Errorf("Runners.ResetInstanceRunnerRegistrationToken returned %+v, want %+v", token, want) } wantCode := 201 if !reflect.DeepEqual(wantCode, resp.StatusCode) { t.Errorf("Runners.ResetInstanceRunnerRegistrationToken returned returned status code %+v, want %+v", resp.StatusCode, wantCode) } } func TestResetGroupRunnerRegistrationToken(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/groups/foobar/runners/reset_registration_token", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) w.WriteHeader(http.StatusCreated) fmt.Fprint(w, `{ "token": "6337ff461c94fd3fa32ba3b1ff4125", "token_expires_at": "2016-01-25T16:39:48.166Z" }`) }) token, resp, err := client.Runners.ResetGroupRunnerRegistrationToken("foobar", nil) if err != nil { t.Fatalf("Runners.ResetGroupRunnerRegistrationToken returns an error: %v", err) } want := &RunnerRegistrationToken{ Token: Ptr("6337ff461c94fd3fa32ba3b1ff4125"), TokenExpiresAt: Ptr(time.Date(2016, time.January, 25, 16, 39, 48, 166000000, time.UTC)), } if !reflect.DeepEqual(want, token) { t.Errorf("Runners.ResetGroupRunnerRegistrationToken returned %+v, want %+v", token, want) } wantCode := 201 if !reflect.DeepEqual(wantCode, resp.StatusCode) { t.Errorf("Runners.ResetGroupRunnerRegistrationToken returned returned status code %+v, want %+v", resp.StatusCode, wantCode) } } func TestResetProjectRunnerRegistrationToken(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/9/runners/reset_registration_token", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) w.WriteHeader(http.StatusCreated) fmt.Fprint(w, `{ "token": "6337ff461c94fd3fa32ba3b1ff4125", "token_expires_at": "2016-01-25T16:39:48.166Z" }`) }) token, resp, err := client.Runners.ResetProjectRunnerRegistrationToken("9", nil) if err != nil { t.Fatalf("Runners.ResetProjectRunnerRegistrationToken returns an error: %v", err) } want := &RunnerRegistrationToken{ Token: Ptr("6337ff461c94fd3fa32ba3b1ff4125"), TokenExpiresAt: Ptr(time.Date(2016, time.January, 25, 16, 39, 48, 166000000, time.UTC)), } if !reflect.DeepEqual(want, token) { t.Errorf("Runners.ResetProjectRunnerRegistrationToken returned %+v, want %+v", token, want) } wantCode := 201 if !reflect.DeepEqual(wantCode, resp.StatusCode) { t.Errorf("Runners.ResetProjectRunnerRegistrationToken returned returned status code %+v, want %+v", resp.StatusCode, wantCode) } } func TestResetRunnerAuthenticationToken(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/runners/42/reset_authentication_token", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) w.WriteHeader(http.StatusCreated) fmt.Fprint(w, `{ "token": "6337ff461c94fd3fa32ba3b1ff4125", "token_expires_at": "2016-01-25T16:39:48.166Z" }`) }) token, resp, err := client.Runners.ResetRunnerAuthenticationToken(42, nil) if err != nil { t.Fatalf("Runners.ResetRunnerAuthenticationToken returns an error: %v", err) } want := &RunnerAuthenticationToken{ Token: Ptr("6337ff461c94fd3fa32ba3b1ff4125"), TokenExpiresAt: Ptr(time.Date(2016, time.January, 25, 16, 39, 48, 166000000, time.UTC)), } if !reflect.DeepEqual(want, token) { t.Errorf("Runners.ResetRunnerAuthenticationToken returned %+v, want %+v", token, want) } wantCode := 201 if !reflect.DeepEqual(wantCode, resp.StatusCode) { t.Errorf("Runners.ResetRunnerAuthenticationToken returned returned status code %+v, want %+v", resp.StatusCode, wantCode) } } golang-gitlab-gitlab-org-api-client-go-0.123.0/search.go000066400000000000000000000331261475761473200226600ustar00rootroot00000000000000// // Copyright 2021, Sander van Harmelen // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "fmt" "net/http" ) // SearchService handles communication with the search related methods of the // GitLab API. // // GitLab API docs: https://docs.gitlab.com/ee/api/search.html type SearchService struct { client *Client } // SearchOptions represents the available options for all search methods. // // GitLab API docs: https://docs.gitlab.com/ee/api/search.html type SearchOptions struct { ListOptions Ref *string `url:"ref,omitempty" json:"ref,omitempty"` } type searchOptions struct { SearchOptions Scope string `url:"scope" json:"scope"` Search string `url:"search" json:"search"` } // Projects searches the expression within projects // // GitLab API docs: https://docs.gitlab.com/ee/api/search.html#scope-projects func (s *SearchService) Projects(query string, opt *SearchOptions, options ...RequestOptionFunc) ([]*Project, *Response, error) { var ps []*Project resp, err := s.search("projects", query, &ps, opt, options...) return ps, resp, err } // ProjectsByGroup searches the expression within projects for // the specified group // // GitLab API docs: https://docs.gitlab.com/ee/api/search.html#group-search-api func (s *SearchService) ProjectsByGroup(gid interface{}, query string, opt *SearchOptions, options ...RequestOptionFunc) ([]*Project, *Response, error) { var ps []*Project resp, err := s.searchByGroup(gid, "projects", query, &ps, opt, options...) return ps, resp, err } // Issues searches the expression within issues // // GitLab API docs: https://docs.gitlab.com/ee/api/search.html#scope-issues func (s *SearchService) Issues(query string, opt *SearchOptions, options ...RequestOptionFunc) ([]*Issue, *Response, error) { var is []*Issue resp, err := s.search("issues", query, &is, opt, options...) return is, resp, err } // IssuesByGroup searches the expression within issues for // the specified group // // GitLab API docs: https://docs.gitlab.com/ee/api/search.html#scope-issues-1 func (s *SearchService) IssuesByGroup(gid interface{}, query string, opt *SearchOptions, options ...RequestOptionFunc) ([]*Issue, *Response, error) { var is []*Issue resp, err := s.searchByGroup(gid, "issues", query, &is, opt, options...) return is, resp, err } // IssuesByProject searches the expression within issues for // the specified project // // GitLab API docs: https://docs.gitlab.com/ee/api/search.html#scope-issues-2 func (s *SearchService) IssuesByProject(pid interface{}, query string, opt *SearchOptions, options ...RequestOptionFunc) ([]*Issue, *Response, error) { var is []*Issue resp, err := s.searchByProject(pid, "issues", query, &is, opt, options...) return is, resp, err } // MergeRequests searches the expression within merge requests // // GitLab API docs: // https://docs.gitlab.com/ee/api/search.html#scope-merge_requests func (s *SearchService) MergeRequests(query string, opt *SearchOptions, options ...RequestOptionFunc) ([]*MergeRequest, *Response, error) { var ms []*MergeRequest resp, err := s.search("merge_requests", query, &ms, opt, options...) return ms, resp, err } // MergeRequestsByGroup searches the expression within merge requests for // the specified group // // GitLab API docs: // https://docs.gitlab.com/ee/api/search.html#scope-merge_requests-1 func (s *SearchService) MergeRequestsByGroup(gid interface{}, query string, opt *SearchOptions, options ...RequestOptionFunc) ([]*MergeRequest, *Response, error) { var ms []*MergeRequest resp, err := s.searchByGroup(gid, "merge_requests", query, &ms, opt, options...) return ms, resp, err } // MergeRequestsByProject searches the expression within merge requests for // the specified project // // GitLab API docs: // https://docs.gitlab.com/ee/api/search.html#scope-merge_requests-2 func (s *SearchService) MergeRequestsByProject(pid interface{}, query string, opt *SearchOptions, options ...RequestOptionFunc) ([]*MergeRequest, *Response, error) { var ms []*MergeRequest resp, err := s.searchByProject(pid, "merge_requests", query, &ms, opt, options...) return ms, resp, err } // Milestones searches the expression within milestones // // GitLab API docs: https://docs.gitlab.com/ee/api/search.html#scope-milestones func (s *SearchService) Milestones(query string, opt *SearchOptions, options ...RequestOptionFunc) ([]*Milestone, *Response, error) { var ms []*Milestone resp, err := s.search("milestones", query, &ms, opt, options...) return ms, resp, err } // MilestonesByGroup searches the expression within milestones for // the specified group // // GitLab API docs: https://docs.gitlab.com/ee/api/search.html#scope-milestones-1 func (s *SearchService) MilestonesByGroup(gid interface{}, query string, opt *SearchOptions, options ...RequestOptionFunc) ([]*Milestone, *Response, error) { var ms []*Milestone resp, err := s.searchByGroup(gid, "milestones", query, &ms, opt, options...) return ms, resp, err } // MilestonesByProject searches the expression within milestones for // the specified project // // GitLab API docs: https://docs.gitlab.com/ee/api/search.html#scope-milestones-2 func (s *SearchService) MilestonesByProject(pid interface{}, query string, opt *SearchOptions, options ...RequestOptionFunc) ([]*Milestone, *Response, error) { var ms []*Milestone resp, err := s.searchByProject(pid, "milestones", query, &ms, opt, options...) return ms, resp, err } // SnippetTitles searches the expression within snippet titles // // GitLab API docs: // https://docs.gitlab.com/ee/api/search.html#scope-snippet_titles func (s *SearchService) SnippetTitles(query string, opt *SearchOptions, options ...RequestOptionFunc) ([]*Snippet, *Response, error) { var ss []*Snippet resp, err := s.search("snippet_titles", query, &ss, opt, options...) return ss, resp, err } // SnippetBlobs searches the expression within snippet blobs // // GitLab API docs: // https://docs.gitlab.com/ee/api/search.html#scope-snippet_blobs func (s *SearchService) SnippetBlobs(query string, opt *SearchOptions, options ...RequestOptionFunc) ([]*Snippet, *Response, error) { var ss []*Snippet resp, err := s.search("snippet_blobs", query, &ss, opt, options...) return ss, resp, err } // NotesByProject searches the expression within notes for the specified // project // // GitLab API docs: // https://docs.gitlab.com/ee/api/search.html#scope-notes func (s *SearchService) NotesByProject(pid interface{}, query string, opt *SearchOptions, options ...RequestOptionFunc) ([]*Note, *Response, error) { var ns []*Note resp, err := s.searchByProject(pid, "notes", query, &ns, opt, options...) return ns, resp, err } // WikiBlobs searches the expression within all wiki blobs // // GitLab API docs: // https://docs.gitlab.com/ee/api/search.html#scope-wiki_blobs func (s *SearchService) WikiBlobs(query string, opt *SearchOptions, options ...RequestOptionFunc) ([]*Wiki, *Response, error) { var ws []*Wiki resp, err := s.search("wiki_blobs", query, &ws, opt, options...) return ws, resp, err } // WikiBlobsByGroup searches the expression within wiki blobs for // specified group // // GitLab API docs: // https://docs.gitlab.com/ee/api/search.html#scope-wiki_blobs-premium-1 func (s *SearchService) WikiBlobsByGroup(gid interface{}, query string, opt *SearchOptions, options ...RequestOptionFunc) ([]*Wiki, *Response, error) { var ws []*Wiki resp, err := s.searchByGroup(gid, "wiki_blobs", query, &ws, opt, options...) return ws, resp, err } // WikiBlobsByProject searches the expression within wiki blobs for // the specified project // // GitLab API docs: // https://docs.gitlab.com/ee/api/search.html#scope-wiki_blobs-premium-2 func (s *SearchService) WikiBlobsByProject(pid interface{}, query string, opt *SearchOptions, options ...RequestOptionFunc) ([]*Wiki, *Response, error) { var ws []*Wiki resp, err := s.searchByProject(pid, "wiki_blobs", query, &ws, opt, options...) return ws, resp, err } // Commits searches the expression within all commits // // GitLab API docs: https://docs.gitlab.com/ee/api/search.html#scope-commits func (s *SearchService) Commits(query string, opt *SearchOptions, options ...RequestOptionFunc) ([]*Commit, *Response, error) { var cs []*Commit resp, err := s.search("commits", query, &cs, opt, options...) return cs, resp, err } // CommitsByGroup searches the expression within commits for the specified // group // // GitLab API docs: https://docs.gitlab.com/ee/api/search.html#scope-commits-premium-1 func (s *SearchService) CommitsByGroup(gid interface{}, query string, opt *SearchOptions, options ...RequestOptionFunc) ([]*Commit, *Response, error) { var cs []*Commit resp, err := s.searchByGroup(gid, "commits", query, &cs, opt, options...) return cs, resp, err } // CommitsByProject searches the expression within commits for the // specified project // // GitLab API docs: https://docs.gitlab.com/ee/api/search.html#scope-commits-premium-2 func (s *SearchService) CommitsByProject(pid interface{}, query string, opt *SearchOptions, options ...RequestOptionFunc) ([]*Commit, *Response, error) { var cs []*Commit resp, err := s.searchByProject(pid, "commits", query, &cs, opt, options...) return cs, resp, err } // Blob represents a single blob. type Blob struct { Basename string `json:"basename"` Data string `json:"data"` Path string `json:"path"` Filename string `json:"filename"` ID string `json:"id"` Ref string `json:"ref"` Startline int `json:"startline"` ProjectID int `json:"project_id"` } // Blobs searches the expression within all blobs // // GitLab API docs: https://docs.gitlab.com/ee/api/search.html#scope-blobs func (s *SearchService) Blobs(query string, opt *SearchOptions, options ...RequestOptionFunc) ([]*Blob, *Response, error) { var bs []*Blob resp, err := s.search("blobs", query, &bs, opt, options...) return bs, resp, err } // BlobsByGroup searches the expression within blobs for the specified // group // // GitLab API docs: https://docs.gitlab.com/ee/api/search.html#scope-blobs-premium-1 func (s *SearchService) BlobsByGroup(gid interface{}, query string, opt *SearchOptions, options ...RequestOptionFunc) ([]*Blob, *Response, error) { var bs []*Blob resp, err := s.searchByGroup(gid, "blobs", query, &bs, opt, options...) return bs, resp, err } // BlobsByProject searches the expression within blobs for the specified // project // // GitLab API docs: https://docs.gitlab.com/ee/api/search.html#scope-blobs-premium-2 func (s *SearchService) BlobsByProject(pid interface{}, query string, opt *SearchOptions, options ...RequestOptionFunc) ([]*Blob, *Response, error) { var bs []*Blob resp, err := s.searchByProject(pid, "blobs", query, &bs, opt, options...) return bs, resp, err } // Users searches the expression within all users // // GitLab API docs: https://docs.gitlab.com/ee/api/search.html#scope-users func (s *SearchService) Users(query string, opt *SearchOptions, options ...RequestOptionFunc) ([]*User, *Response, error) { var ret []*User resp, err := s.search("users", query, &ret, opt, options...) return ret, resp, err } // UsersByGroup searches the expression within users for the specified // group // // GitLab API docs: https://docs.gitlab.com/ee/api/search.html#scope-users-1 func (s *SearchService) UsersByGroup(gid interface{}, query string, opt *SearchOptions, options ...RequestOptionFunc) ([]*User, *Response, error) { var ret []*User resp, err := s.searchByGroup(gid, "users", query, &ret, opt, options...) return ret, resp, err } // UsersByProject searches the expression within users for the // specified project // // GitLab API docs: https://docs.gitlab.com/ee/api/search.html#scope-users-2 func (s *SearchService) UsersByProject(pid interface{}, query string, opt *SearchOptions, options ...RequestOptionFunc) ([]*User, *Response, error) { var ret []*User resp, err := s.searchByProject(pid, "users", query, &ret, opt, options...) return ret, resp, err } func (s *SearchService) search(scope, query string, result interface{}, opt *SearchOptions, options ...RequestOptionFunc) (*Response, error) { opts := &searchOptions{SearchOptions: *opt, Scope: scope, Search: query} req, err := s.client.NewRequest(http.MethodGet, "search", opts, options) if err != nil { return nil, err } return s.client.Do(req, result) } func (s *SearchService) searchByGroup(gid interface{}, scope, query string, result interface{}, opt *SearchOptions, options ...RequestOptionFunc) (*Response, error) { group, err := parseID(gid) if err != nil { return nil, err } u := fmt.Sprintf("groups/%s/-/search", PathEscape(group)) opts := &searchOptions{SearchOptions: *opt, Scope: scope, Search: query} req, err := s.client.NewRequest(http.MethodGet, u, opts, options) if err != nil { return nil, err } return s.client.Do(req, result) } func (s *SearchService) searchByProject(pid interface{}, scope, query string, result interface{}, opt *SearchOptions, options ...RequestOptionFunc) (*Response, error) { project, err := parseID(pid) if err != nil { return nil, err } u := fmt.Sprintf("projects/%s/-/search", PathEscape(project)) opts := &searchOptions{SearchOptions: *opt, Scope: scope, Search: query} req, err := s.client.NewRequest(http.MethodGet, u, opts, options) if err != nil { return nil, err } return s.client.Do(req, result) } golang-gitlab-gitlab-org-api-client-go-0.123.0/search_test.go000066400000000000000000000053301475761473200237130ustar00rootroot00000000000000// // Copyright 2021, Sander van Harmelen // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "net/http" "testing" "github.com/stretchr/testify/require" ) func TestSearchService_Users(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/search", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) mustWriteHTTPResponse(t, w, "testdata/search_users.json") }) opts := &SearchOptions{ListOptions: ListOptions{PerPage: 2}} users, _, err := client.Search.Users("doe", opts) require.NoError(t, err) want := []*User{{ ID: 1, Username: "user1", Name: "John Doe1", State: "active", AvatarURL: "http://www.gravatar.com/avatar/c922747a93b40d1ea88262bf1aebee62?s=80&d=identicon", WebURL: "http://localhost/user1", }} require.Equal(t, want, users) } func TestSearchService_UsersByGroup(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/groups/3/-/search", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) mustWriteHTTPResponse(t, w, "testdata/search_users.json") }) opts := &SearchOptions{ListOptions: ListOptions{PerPage: 2}} users, _, err := client.Search.UsersByGroup("3", "doe", opts) require.NoError(t, err) want := []*User{{ ID: 1, Username: "user1", Name: "John Doe1", State: "active", AvatarURL: "http://www.gravatar.com/avatar/c922747a93b40d1ea88262bf1aebee62?s=80&d=identicon", WebURL: "http://localhost/user1", }} require.Equal(t, want, users) } func TestSearchService_UsersByProject(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/6/-/search", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) mustWriteHTTPResponse(t, w, "testdata/search_users.json") }) opts := &SearchOptions{ListOptions: ListOptions{PerPage: 2}} users, _, err := client.Search.UsersByProject("6", "doe", opts) require.NoError(t, err) want := []*User{{ ID: 1, Username: "user1", Name: "John Doe1", State: "active", AvatarURL: "http://www.gravatar.com/avatar/c922747a93b40d1ea88262bf1aebee62?s=80&d=identicon", WebURL: "http://localhost/user1", }} require.Equal(t, want, users) } golang-gitlab-gitlab-org-api-client-go-0.123.0/services.go000066400000000000000000002712551475761473200232450ustar00rootroot00000000000000// // Copyright 2021, Sander van Harmelen // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "encoding/json" "fmt" "net/http" "strconv" "time" ) // ServicesService handles communication with the services related methods of // the GitLab API. // // GitLab API docs: https://docs.gitlab.com/ee/api/integrations.html type ServicesService struct { client *Client } // Service represents a GitLab service. // // GitLab API docs: https://docs.gitlab.com/ee/api/integrations.html type Service struct { ID int `json:"id"` Title string `json:"title"` Slug string `json:"slug"` CreatedAt *time.Time `json:"created_at"` UpdatedAt *time.Time `json:"updated_at"` Active bool `json:"active"` AlertEvents bool `json:"alert_events"` CommitEvents bool `json:"commit_events"` ConfidentialIssuesEvents bool `json:"confidential_issues_events"` ConfidentialNoteEvents bool `json:"confidential_note_events"` DeploymentEvents bool `json:"deployment_events"` GroupConfidentialMentionEvents bool `json:"group_confidential_mention_events"` GroupMentionEvents bool `json:"group_mention_events"` IncidentEvents bool `json:"incident_events"` IssuesEvents bool `json:"issues_events"` JobEvents bool `json:"job_events"` MergeRequestsEvents bool `json:"merge_requests_events"` NoteEvents bool `json:"note_events"` PipelineEvents bool `json:"pipeline_events"` PushEvents bool `json:"push_events"` TagPushEvents bool `json:"tag_push_events"` VulnerabilityEvents bool `json:"vulnerability_events"` WikiPageEvents bool `json:"wiki_page_events"` CommentOnEventEnabled bool `json:"comment_on_event_enabled"` Inherited bool `json:"inherited"` } // ListServices gets a list of all active services. // // GitLab API docs: https://docs.gitlab.com/ee/api/integrations.html#list-all-active-integrations func (s *ServicesService) ListServices(pid interface{}, options ...RequestOptionFunc) ([]*Service, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/services", PathEscape(project)) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } var svcs []*Service resp, err := s.client.Do(req, &svcs) if err != nil { return nil, resp, err } return svcs, resp, nil } // CustomIssueTrackerService represents Custom Issue Tracker service settings. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#custom-issue-tracker type CustomIssueTrackerService struct { Service Properties *CustomIssueTrackerServiceProperties `json:"properties"` } // CustomIssueTrackerServiceProperties represents Custom Issue Tracker specific properties. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#custom-issue-tracker type CustomIssueTrackerServiceProperties struct { ProjectURL string `json:"project_url,omitempty"` IssuesURL string `json:"issues_url,omitempty"` NewIssueURL string `json:"new_issue_url,omitempty"` } // GetCustomIssueTrackerService gets Custom Issue Tracker service settings for a project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#get-custom-issue-tracker-settings func (s *ServicesService) GetCustomIssueTrackerService(pid interface{}, options ...RequestOptionFunc) (*CustomIssueTrackerService, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/services/custom-issue-tracker", PathEscape(project)) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } svc := new(CustomIssueTrackerService) resp, err := s.client.Do(req, svc) if err != nil { return nil, resp, err } return svc, resp, nil } // SetCustomIssueTrackerServiceOptions represents the available SetCustomIssueTrackerService() // options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#set-up-a-custom-issue-tracker type SetCustomIssueTrackerServiceOptions struct { NewIssueURL *string `url:"new_issue_url,omitempty" json:"new_issue_url,omitempty"` IssuesURL *string `url:"issues_url,omitempty" json:"issues_url,omitempty"` ProjectURL *string `url:"project_url,omitempty" json:"project_url,omitempty"` } // SetCustomIssueTrackerService sets Custom Issue Tracker service for a project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#set-up-a-custom-issue-tracker func (s *ServicesService) SetCustomIssueTrackerService(pid interface{}, opt *SetCustomIssueTrackerServiceOptions, options ...RequestOptionFunc) (*CustomIssueTrackerService, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/services/custom-issue-tracker", PathEscape(project)) req, err := s.client.NewRequest(http.MethodPut, u, opt, options) if err != nil { return nil, nil, err } svc := new(CustomIssueTrackerService) resp, err := s.client.Do(req, svc) if err != nil { return nil, nil, err } return svc, resp, nil } // DeleteCustomIssueTrackerService deletes Custom Issue Tracker service settings for a project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#disable-a-custom-issue-tracker func (s *ServicesService) DeleteCustomIssueTrackerService(pid interface{}, options ...RequestOptionFunc) (*Response, error) { project, err := parseID(pid) if err != nil { return nil, err } u := fmt.Sprintf("projects/%s/services/custom-issue-tracker", PathEscape(project)) req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } // DataDogService represents DataDog service settings. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#datadog type DataDogService struct { Service Properties *DataDogServiceProperties `json:"properties"` } // DataDogServiceProperties represents DataDog specific properties. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#datadog type DataDogServiceProperties struct { APIURL string `url:"api_url,omitempty" json:"api_url,omitempty"` DataDogEnv string `url:"datadog_env,omitempty" json:"datadog_env,omitempty"` DataDogService string `url:"datadog_service,omitempty" json:"datadog_service,omitempty"` DataDogSite string `url:"datadog_site,omitempty" json:"datadog_site,omitempty"` DataDogTags string `url:"datadog_tags,omitempty" json:"datadog_tags,omitempty"` ArchiveTraceEvents bool `url:"archive_trace_events,omitempty" json:"archive_trace_events,omitempty"` } // GetDataDogService gets DataDog service settings for a project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#get-datadog-settings func (s *ServicesService) GetDataDogService(pid interface{}, options ...RequestOptionFunc) (*DataDogService, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/services/datadog", PathEscape(project)) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } svc := new(DataDogService) resp, err := s.client.Do(req, svc) if err != nil { return nil, resp, err } return svc, resp, nil } // SetDataDogServiceOptions represents the available SetDataDogService() // options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#set-up-datadog type SetDataDogServiceOptions struct { APIKey *string `url:"api_key,omitempty" json:"api_key,omitempty"` APIURL *string `url:"api_url,omitempty" json:"api_url,omitempty"` DataDogEnv *string `url:"datadog_env,omitempty" json:"datadog_env,omitempty"` DataDogService *string `url:"datadog_service,omitempty" json:"datadog_service,omitempty"` DataDogSite *string `url:"datadog_site,omitempty" json:"datadog_site,omitempty"` DataDogTags *string `url:"datadog_tags,omitempty" json:"datadog_tags,omitempty"` ArchiveTraceEvents *bool `url:"archive_trace_events,omitempty" json:"archive_trace_events,omitempty"` } // SetDataDogService sets DataDog service settings for a project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#set-up-datadog func (s *ServicesService) SetDataDogService(pid interface{}, opt *SetDataDogServiceOptions, options ...RequestOptionFunc) (*DataDogService, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/services/datadog", PathEscape(project)) req, err := s.client.NewRequest(http.MethodPut, u, opt, options) if err != nil { return nil, nil, err } svc := new(DataDogService) resp, err := s.client.Do(req, svc) if err != nil { return nil, nil, err } return svc, resp, nil } // DeleteDataDogService deletes the DataDog service settings for a project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#disable-datadog func (s *ServicesService) DeleteDataDogService(pid interface{}, options ...RequestOptionFunc) (*Response, error) { project, err := parseID(pid) if err != nil { return nil, err } u := fmt.Sprintf("projects/%s/services/datadog", PathEscape(project)) req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } // DiscordService represents Discord service settings. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#discord-notifications type DiscordService struct { Service Properties *DiscordServiceProperties `json:"properties"` } // DiscordServiceProperties represents Discord specific properties. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#discord-notifications type DiscordServiceProperties struct { BranchesToBeNotified string `url:"branches_to_be_notified,omitempty" json:"branches_to_be_notified,omitempty"` NotifyOnlyBrokenPipelines bool `url:"notify_only_broken_pipelines,omitempty" json:"notify_only_broken_pipelines,omitempty"` } // GetDiscordService gets Discord service settings for a project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#get-discord-notifications-settings func (s *ServicesService) GetDiscordService(pid interface{}, options ...RequestOptionFunc) (*DiscordService, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/services/discord", PathEscape(project)) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } svc := new(DiscordService) resp, err := s.client.Do(req, svc) if err != nil { return nil, resp, err } return svc, resp, nil } // SetDiscordServiceOptions represents the available SetDiscordService() // options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#set-up-discord-notifications type SetDiscordServiceOptions struct { WebHook *string `url:"webhook,omitempty" json:"webhook,omitempty"` BranchesToBeNotified *string `url:"branches_to_be_notified,omitempty" json:"branches_to_be_notified,omitempty"` ConfidentialIssuesEvents *bool `url:"confidential_issues_events,omitempty" json:"confidential_issues_events,omitempty"` ConfidentialIssuesChannel *string `url:"confidential_issue_channel,omitempty" json:"confidential_issue_channel,omitempty"` ConfidentialNoteEvents *bool `url:"confidential_note_events,omitempty" json:"confidential_note_events,omitempty"` ConfidentialNoteChannel *string `url:"confidential_note_channel,omitempty" json:"confidential_note_channel,omitempty"` DeploymentEvents *bool `url:"deployment_events,omitempty" json:"deployment_events,omitempty"` DeploymentChannel *string `url:"deployment_channel,omitempty" json:"deployment_channel,omitempty"` GroupConfidentialMentionsEvents *bool `url:"group_confidential_mentions_events,omitempty" json:"group_confidential_mentions_events,omitempty"` GroupConfidentialMentionsChannel *string `url:"group_confidential_mentions_channel,omitempty" json:"group_confidential_mentions_channel,omitempty"` GroupMentionsEvents *bool `url:"group_mentions_events,omitempty" json:"group_mentions_events,omitempty"` GroupMentionsChannel *string `url:"group_mentions_channel,omitempty" json:"group_mentions_channel,omitempty"` IssuesEvents *bool `url:"issues_events,omitempty" json:"issues_events,omitempty"` IssueChannel *string `url:"issue_channel,omitempty" json:"issue_channel,omitempty"` MergeRequestsEvents *bool `url:"merge_requests_events,omitempty" json:"merge_requests_events,omitempty"` MergeRequestChannel *string `url:"merge_request_channel,omitempty" json:"merge_request_channel,omitempty"` NoteEvents *bool `url:"note_events,omitempty" json:"note_events,omitempty"` NoteChannel *string `url:"note_channel,omitempty" json:"note_channel,omitempty"` NotifyOnlyBrokenPipelines *bool `url:"notify_only_broken_pipelines,omitempty" json:"notify_only_broken_pipelines,omitempty"` PipelineEvents *bool `url:"pipeline_events,omitempty" json:"pipeline_events,omitempty"` PipelineChannel *string `url:"pipeline_channel,omitempty" json:"pipeline_channel,omitempty"` PushEvents *bool `url:"push_events,omitempty" json:"push_events,omitempty"` PushChannel *string `url:"push_channel,omitempty" json:"push_channel,omitempty"` TagPushEvents *bool `url:"tag_push_events,omitempty" json:"tag_push_events,omitempty"` TagPushChannel *string `url:"tag_push_channel,omitempty" json:"tag_push_channel,omitempty"` WikiPageEvents *bool `url:"wiki_page_events,omitempty" json:"wiki_page_events,omitempty"` WikiPageChannel *string `url:"wiki_page_channel,omitempty" json:"wiki_page_channel,omitempty"` } // SetDiscordService sets Discord service for a project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#set-up-discord-notifications func (s *ServicesService) SetDiscordService(pid interface{}, opt *SetDiscordServiceOptions, options ...RequestOptionFunc) (*DiscordService, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/services/discord", PathEscape(project)) req, err := s.client.NewRequest(http.MethodPut, u, opt, options) if err != nil { return nil, nil, err } svc := new(DiscordService) resp, err := s.client.Do(req, svc) if err != nil { return nil, resp, err } return svc, resp, nil } // DeleteDiscordService deletes Discord service settings for a project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#disable-discord-notifications func (s *ServicesService) DeleteDiscordService(pid interface{}, options ...RequestOptionFunc) (*Response, error) { project, err := parseID(pid) if err != nil { return nil, err } u := fmt.Sprintf("projects/%s/services/discord", PathEscape(project)) req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } // DroneCIService represents Drone CI service settings. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#drone type DroneCIService struct { Service Properties *DroneCIServiceProperties `json:"properties"` } // DroneCIServiceProperties represents Drone CI specific properties. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#drone type DroneCIServiceProperties struct { DroneURL string `json:"drone_url"` EnableSSLVerification bool `json:"enable_ssl_verification"` } // GetDroneCIService gets Drone CI service settings for a project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#get-drone-settings func (s *ServicesService) GetDroneCIService(pid interface{}, options ...RequestOptionFunc) (*DroneCIService, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/services/drone-ci", PathEscape(project)) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } svc := new(DroneCIService) resp, err := s.client.Do(req, svc) if err != nil { return nil, resp, err } return svc, resp, nil } // SetDroneCIServiceOptions represents the available SetDroneCIService() // options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#set-up-drone type SetDroneCIServiceOptions struct { Token *string `url:"token,omitempty" json:"token,omitempty"` DroneURL *string `url:"drone_url,omitempty" json:"drone_url,omitempty"` EnableSSLVerification *bool `url:"enable_ssl_verification,omitempty" json:"enable_ssl_verification,omitempty"` PushEvents *bool `url:"push_events,omitempty" json:"push_events,omitempty"` MergeRequestsEvents *bool `url:"merge_requests_events,omitempty" json:"merge_requests_events,omitempty"` TagPushEvents *bool `url:"tag_push_events,omitempty" json:"tag_push_events,omitempty"` } // SetDroneCIService sets Drone CI service for a project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#set-up-drone func (s *ServicesService) SetDroneCIService(pid interface{}, opt *SetDroneCIServiceOptions, options ...RequestOptionFunc) (*DroneCIService, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/services/drone-ci", PathEscape(project)) req, err := s.client.NewRequest(http.MethodPut, u, opt, options) if err != nil { return nil, nil, err } svc := new(DroneCIService) resp, err := s.client.Do(req, svc) if err != nil { return nil, nil, err } return svc, resp, nil } // DeleteDroneCIService deletes Drone CI service settings for a project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#disable-drone func (s *ServicesService) DeleteDroneCIService(pid interface{}, options ...RequestOptionFunc) (*Response, error) { project, err := parseID(pid) if err != nil { return nil, err } u := fmt.Sprintf("projects/%s/services/drone-ci", PathEscape(project)) req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } // EmailsOnPushService represents Emails on Push service settings. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#emails-on-push type EmailsOnPushService struct { Service Properties *EmailsOnPushServiceProperties `json:"properties"` } // EmailsOnPushServiceProperties represents Emails on Push specific properties. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#emails-on-push type EmailsOnPushServiceProperties struct { Recipients string `json:"recipients"` DisableDiffs bool `json:"disable_diffs"` SendFromCommitterEmail bool `json:"send_from_committer_email"` PushEvents bool `json:"push_events"` TagPushEvents bool `json:"tag_push_events"` BranchesToBeNotified string `json:"branches_to_be_notified"` } // GetEmailsOnPushService gets Emails on Push service settings for a project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#get-emails-on-push-integration-settings func (s *ServicesService) GetEmailsOnPushService(pid interface{}, options ...RequestOptionFunc) (*EmailsOnPushService, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/integrations/emails-on-push", PathEscape(project)) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } svc := new(EmailsOnPushService) resp, err := s.client.Do(req, svc) if err != nil { return nil, resp, err } return svc, resp, nil } // SetEmailsOnPushServiceOptions represents the available SetEmailsOnPushService() // options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#set-up-emails-on-push type SetEmailsOnPushServiceOptions struct { Recipients *string `url:"recipients,omitempty" json:"recipients,omitempty"` DisableDiffs *bool `url:"disable_diffs,omitempty" json:"disable_diffs,omitempty"` SendFromCommitterEmail *bool `url:"send_from_committer_email,omitempty" json:"send_from_committer_email,omitempty"` PushEvents *bool `url:"push_events,omitempty" json:"push_events,omitempty"` TagPushEvents *bool `url:"tag_push_events,omitempty" json:"tag_push_events,omitempty"` BranchesToBeNotified *string `url:"branches_to_be_notified,omitempty" json:"branches_to_be_notified,omitempty"` } // SetEmailsOnPushService sets Emails on Push service for a project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#set-up-emails-on-push func (s *ServicesService) SetEmailsOnPushService(pid interface{}, opt *SetEmailsOnPushServiceOptions, options ...RequestOptionFunc) (*EmailsOnPushService, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/integrations/emails-on-push", PathEscape(project)) req, err := s.client.NewRequest(http.MethodPut, u, opt, options) if err != nil { return nil, nil, err } svc := new(EmailsOnPushService) resp, err := s.client.Do(req, svc) if err != nil { return nil, nil, err } return svc, resp, nil } // DeleteEmailsOnPushService deletes Emails on Push service settings for a project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#disable-emails-on-push func (s *ServicesService) DeleteEmailsOnPushService(pid interface{}, options ...RequestOptionFunc) (*Response, error) { project, err := parseID(pid) if err != nil { return nil, err } u := fmt.Sprintf("projects/%s/integrations/emails-on-push", PathEscape(project)) req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } // ExternalWikiService represents External Wiki service settings. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#external-wiki type ExternalWikiService struct { Service Properties *ExternalWikiServiceProperties `json:"properties"` } // ExternalWikiServiceProperties represents External Wiki specific properties. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#external-wiki type ExternalWikiServiceProperties struct { ExternalWikiURL string `json:"external_wiki_url"` } // GetExternalWikiService gets External Wiki service settings for a project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#get-external-wiki-settings func (s *ServicesService) GetExternalWikiService(pid interface{}, options ...RequestOptionFunc) (*ExternalWikiService, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/services/external-wiki", PathEscape(project)) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } svc := new(ExternalWikiService) resp, err := s.client.Do(req, svc) if err != nil { return nil, resp, err } return svc, resp, nil } // SetExternalWikiServiceOptions represents the available SetExternalWikiService() // options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#set-up-an-external-wiki type SetExternalWikiServiceOptions struct { ExternalWikiURL *string `url:"external_wiki_url,omitempty" json:"external_wiki_url,omitempty"` } // SetExternalWikiService sets External Wiki service for a project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#set-up-an-external-wiki func (s *ServicesService) SetExternalWikiService(pid interface{}, opt *SetExternalWikiServiceOptions, options ...RequestOptionFunc) (*ExternalWikiService, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/services/external-wiki", PathEscape(project)) req, err := s.client.NewRequest(http.MethodPut, u, opt, options) if err != nil { return nil, nil, err } svc := new(ExternalWikiService) resp, err := s.client.Do(req, svc) if err != nil { return nil, nil, err } return svc, resp, nil } // DeleteExternalWikiService deletes External Wiki service for project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#disable-an-external-wiki func (s *ServicesService) DeleteExternalWikiService(pid interface{}, options ...RequestOptionFunc) (*Response, error) { project, err := parseID(pid) if err != nil { return nil, err } u := fmt.Sprintf("projects/%s/services/external-wiki", PathEscape(project)) req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } // GithubService represents Github service settings. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#github type GithubService struct { Service Properties *GithubServiceProperties `json:"properties"` } // GithubServiceProperties represents Github specific properties. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#github type GithubServiceProperties struct { RepositoryURL string `json:"repository_url"` StaticContext bool `json:"static_context"` } // GetGithubService gets Github service settings for a project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#get-github-settings func (s *ServicesService) GetGithubService(pid interface{}, options ...RequestOptionFunc) (*GithubService, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/services/github", PathEscape(project)) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } svc := new(GithubService) resp, err := s.client.Do(req, svc) if err != nil { return nil, resp, err } return svc, resp, nil } // SetGithubServiceOptions represents the available SetGithubService() // options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#set-up-github type SetGithubServiceOptions struct { Token *string `url:"token,omitempty" json:"token,omitempty"` RepositoryURL *string `url:"repository_url,omitempty" json:"repository_url,omitempty"` StaticContext *bool `url:"static_context,omitempty" json:"static_context,omitempty"` } // SetGithubService sets Github service for a project // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#set-up-github func (s *ServicesService) SetGithubService(pid interface{}, opt *SetGithubServiceOptions, options ...RequestOptionFunc) (*GithubService, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/services/github", PathEscape(project)) req, err := s.client.NewRequest(http.MethodPut, u, opt, options) if err != nil { return nil, nil, err } svc := new(GithubService) resp, err := s.client.Do(req, svc) if err != nil { return nil, nil, err } return svc, resp, nil } // DeleteGithubService deletes Github service for a project // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#disable-github func (s *ServicesService) DeleteGithubService(pid interface{}, options ...RequestOptionFunc) (*Response, error) { project, err := parseID(pid) if err != nil { return nil, err } u := fmt.Sprintf("projects/%s/services/github", PathEscape(project)) req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } // HarborService represents the Harbor service settings. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#harbor type HarborService struct { Service Properties *HarborServiceProperties `json:"properties"` } // HarborServiceProperties represents Harbor specific properties. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#harbor type HarborServiceProperties struct { URL string `json:"url"` ProjectName string `json:"project_name"` Username string `json:"username"` Password string `json:"password"` UseInheritedSettings bool `json:"use_inherited_settings"` } // GetHarborService gets Harbor service settings for a project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#get-harbor-settings func (s *ServicesService) GetHarborService(pid interface{}, options ...RequestOptionFunc) (*HarborService, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/integrations/harbor", PathEscape(project)) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } svc := new(HarborService) resp, err := s.client.Do(req, svc) if err != nil { return nil, resp, err } return svc, resp, nil } // SetHarborServiceOptions represents the available SetHarborService() // options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#set-up-harbor type SetHarborServiceOptions struct { URL *string `url:"url,omitempty" json:"url,omitempty"` ProjectName *string `url:"project_name,omitempty" json:"project_name,omitempty"` Username *string `url:"username,omitempty" json:"username,omitempty"` Password *string `url:"password,omitempty" json:"password,omitempty"` UseInheritedSettings *bool `url:"use_inherited_settings,omitempty" json:"use_inherited_settings,omitempty"` } // SetHarborService sets Harbor service for a project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#set-up-harbor func (s *ServicesService) SetHarborService(pid interface{}, opt *SetHarborServiceOptions, options ...RequestOptionFunc) (*HarborService, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/integrations/harbor", PathEscape(project)) req, err := s.client.NewRequest(http.MethodPut, u, opt, options) if err != nil { return nil, nil, err } svc := new(HarborService) resp, err := s.client.Do(req, svc) if err != nil { return nil, nil, err } return svc, resp, nil } // DeleteHarborService deletes Harbor service for a project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#disable-harbor func (s *ServicesService) DeleteHarborService(pid interface{}, options ...RequestOptionFunc) (*Response, error) { project, err := parseID(pid) if err != nil { return nil, err } u := fmt.Sprintf("projects/%s/integrations/harbor", PathEscape(project)) req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } // SlackApplication represents GitLab for slack application settings. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#gitlab-for-slack-app type SlackApplication struct { Service Properties *SlackApplicationProperties `json:"properties"` } // SlackApplicationProperties represents GitLab for slack application specific properties. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#gitlab-for-slack-app type SlackApplicationProperties struct { Channel string `json:"channel"` NotifyOnlyBrokenPipelines bool `json:"notify_only_broken_pipelines"` BranchesToBeNotified string `json:"branches_to_be_notified"` LabelsToBeNotified string `json:"labels_to_be_notified"` LabelsToBeNotifiedBehavior string `json:"labels_to_be_notified_behavior"` PushChannel string `json:"push_channel"` IssueChannel string `json:"issue_channel"` ConfidentialIssueChannel string `json:"confidential_issue_channel"` MergeRequestChannel string `json:"merge_request_channel"` NoteChannel string `json:"note_channel"` ConfidentialNoteChannel string `json:"confidential_note_channel"` TagPushChannel string `json:"tag_push_channel"` PipelineChannel string `json:"pipeline_channel"` WikiPageChannel string `json:"wiki_page_channel"` DeploymentChannel string `json:"deployment_channel"` IncidentChannel string `json:"incident_channel"` VulnerabilityChannel string `json:"vulnerability_channel"` AlertChannel string `json:"alert_channel"` // Deprecated: This parameter has been replaced with BranchesToBeNotified. NotifyOnlyDefaultBranch bool `json:"notify_only_default_branch"` } // GetSlackApplication gets the GitLab for Slack app integration settings for a // project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#get-gitlab-for-slack-app-settings func (s *ServicesService) GetSlackApplication(pid interface{}, options ...RequestOptionFunc) (*SlackApplication, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/integrations/gitlab-slack-application", PathEscape(project)) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } svc := new(SlackApplication) resp, err := s.client.Do(req, svc) if err != nil { return nil, resp, err } return svc, resp, nil } // SetSlackApplicationOptions represents the available SetSlackApplication() // options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#set-up-gitlab-for-slack-app type SetSlackApplicationOptions struct { Channel *string `url:"channel,omitempty" json:"channel,omitempty"` NotifyOnlyBrokenPipelines *bool `url:"notify_only_broken_pipelines,omitempty" json:"notify_only_broken_pipelines,omitempty"` BranchesToBeNotified *string `url:"branches_to_be_notified,omitempty" json:"branches_to_be_notified,omitempty"` AlertEvents *bool `url:"alert_events,omitempty" json:"alert_events,omitempty"` IssuesEvents *bool `url:"issues_events,omitempty" json:"issues_events,omitempty"` ConfidentialIssuesEvents *bool `url:"confidential_issues_events,omitempty" json:"confidential_issues_events,omitempty"` MergeRequestsEvents *bool `url:"merge_requests_events,omitempty" json:"merge_requests_events,omitempty"` NoteEvents *bool `url:"note_events,omitempty" json:"note_events,omitempty"` ConfidentialNoteEvents *bool `url:"confidential_note_events,omitempty" json:"confidential_note_events,omitempty"` DeploymentEvents *bool `url:"deployment_events,omitempty" json:"deployment_events,omitempty"` IncidentsEvents *bool `url:"incidents_events,omitempty" json:"incidents_events,omitempty"` PipelineEvents *bool `url:"pipeline_events,omitempty" json:"pipeline_events,omitempty"` PushEvents *bool `url:"push_events,omitempty" json:"push_events,omitempty"` TagPushEvents *bool `url:"tag_push_events,omitempty" json:"tag_push_events,omitempty"` VulnerabilityEvents *bool `url:"vulnerability_events,omitempty" json:"vulnerability_events,omitempty"` WikiPageEvents *bool `url:"wiki_page_events,omitempty" json:"wiki_page_events,omitempty"` LabelsToBeNotified *string `url:"labels_to_be_notified,omitempty" json:"labels_to_be_notified,omitempty"` LabelsToBeNotifiedBehavior *string `url:"labels_to_be_notified_behavior,omitempty" json:"labels_to_be_notified_behavior,omitempty"` PushChannel *string `url:"push_channel,omitempty" json:"push_channel,omitempty"` IssueChannel *string `url:"issue_channel,omitempty" json:"issue_channel,omitempty"` ConfidentialIssueChannel *string `url:"confidential_issue_channel,omitempty" json:"confidential_issue_channel,omitempty"` MergeRequestChannel *string `url:"merge_request_channel,omitempty" json:"merge_request_channel,omitempty"` NoteChannel *string `url:"note_channel,omitempty" json:"note_channel,omitempty"` ConfidentialNoteChannel *string `url:"confidential_note_channel,omitempty" json:"confidential_note_channel,omitempty"` TagPushChannel *string `url:"tag_push_channel,omitempty" json:"tag_push_channel,omitempty"` PipelineChannel *string `url:"pipeline_channel,omitempty" json:"pipeline_channel,omitempty"` WikiPageChannel *string `url:"wiki_page_channel,omitempty" json:"wiki_page_channel,omitempty"` DeploymentChannel *string `url:"deployment_channel,omitempty" json:"deployment_channel,omitempty"` IncidentChannel *string `url:"incident_channel,omitempty" json:"incident_channel,omitempty"` VulnerabilityChannel *string `url:"vulnerability_channel,omitempty" json:"vulnerability_channel,omitempty"` AlertChannel *string `url:"alert_channel,omitempty" json:"alert_channel,omitempty"` UseInheritedSettings *bool `url:"use_inherited_settings,omitempty" json:"use_inherited_settings,omitempty"` // Deprecated: This parameter has been replaced with BranchesToBeNotified. NotifyOnlyDefaultBranch *bool `url:"notify_only_default_branch,omitempty" json:"notify_only_default_branch,omitempty"` } // SetSlackApplication update the GitLab for Slack app integration for a project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#set-up-gitlab-for-slack-app func (s *ServicesService) SetSlackApplication(pid interface{}, opt *SetSlackApplicationOptions, options ...RequestOptionFunc) (*SlackApplication, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/integrations/gitlab-slack-application", PathEscape(project)) req, err := s.client.NewRequest(http.MethodPut, u, opt, options) if err != nil { return nil, nil, err } svc := new(SlackApplication) resp, err := s.client.Do(req, svc) if err != nil { return nil, nil, err } return svc, resp, nil } // DisableSlackApplication disable the GitLab for Slack app integration for a project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#disable-gitlab-for-slack-app func (s *ServicesService) DisableSlackApplication(pid interface{}, options ...RequestOptionFunc) (*Response, error) { project, err := parseID(pid) if err != nil { return nil, err } u := fmt.Sprintf("projects/%s/integrations/gitlab-slack-application", PathEscape(project)) req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } // SetGitLabCIServiceOptions represents the available SetGitLabCIService() // options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#edit-gitlab-ci-service type SetGitLabCIServiceOptions struct { Token *string `url:"token,omitempty" json:"token,omitempty"` ProjectURL *string `url:"project_url,omitempty" json:"project_url,omitempty"` } // SetGitLabCIService sets GitLab CI service for a project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#edit-gitlab-ci-service func (s *ServicesService) SetGitLabCIService(pid interface{}, opt *SetGitLabCIServiceOptions, options ...RequestOptionFunc) (*Response, error) { project, err := parseID(pid) if err != nil { return nil, err } u := fmt.Sprintf("projects/%s/services/gitlab-ci", PathEscape(project)) req, err := s.client.NewRequest(http.MethodPut, u, opt, options) if err != nil { return nil, err } return s.client.Do(req, nil) } // DeleteGitLabCIService deletes GitLab CI service settings for a project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#delete-gitlab-ci-service func (s *ServicesService) DeleteGitLabCIService(pid interface{}, options ...RequestOptionFunc) (*Response, error) { project, err := parseID(pid) if err != nil { return nil, err } u := fmt.Sprintf("projects/%s/services/gitlab-ci", PathEscape(project)) req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } // SetHipChatServiceOptions represents the available SetHipChatService() // options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#edit-hipchat-service type SetHipChatServiceOptions struct { Token *string `url:"token,omitempty" json:"token,omitempty" ` Room *string `url:"room,omitempty" json:"room,omitempty"` } // SetHipChatService sets HipChat service for a project // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#edit-hipchat-service func (s *ServicesService) SetHipChatService(pid interface{}, opt *SetHipChatServiceOptions, options ...RequestOptionFunc) (*Response, error) { project, err := parseID(pid) if err != nil { return nil, err } u := fmt.Sprintf("projects/%s/services/hipchat", PathEscape(project)) req, err := s.client.NewRequest(http.MethodPut, u, opt, options) if err != nil { return nil, err } return s.client.Do(req, nil) } // DeleteHipChatService deletes HipChat service for project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#delete-hipchat-service func (s *ServicesService) DeleteHipChatService(pid interface{}, options ...RequestOptionFunc) (*Response, error) { project, err := parseID(pid) if err != nil { return nil, err } u := fmt.Sprintf("projects/%s/services/hipchat", PathEscape(project)) req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } // JenkinsCIService represents Jenkins CI service settings. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#jenkins type JenkinsCIService struct { Service Properties *JenkinsCIServiceProperties `json:"properties"` } // JenkinsCIServiceProperties represents Jenkins CI specific properties. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#jenkins type JenkinsCIServiceProperties struct { URL string `json:"jenkins_url"` EnableSSLVerification bool `json:"enable_ssl_verification"` ProjectName string `json:"project_name"` Username string `json:"username"` } // GetJenkinsCIService gets Jenkins CI service settings for a project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#get-jenkins-settings func (s *ServicesService) GetJenkinsCIService(pid interface{}, options ...RequestOptionFunc) (*JenkinsCIService, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/services/jenkins", PathEscape(project)) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } svc := new(JenkinsCIService) resp, err := s.client.Do(req, svc) if err != nil { return nil, resp, err } return svc, resp, nil } // SetJenkinsCIServiceOptions represents the available SetJenkinsCIService() // options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#jenkins type SetJenkinsCIServiceOptions struct { URL *string `url:"jenkins_url,omitempty" json:"jenkins_url,omitempty"` EnableSSLVerification *bool `url:"enable_ssl_verification,omitempty" json:"enable_ssl_verification,omitempty"` ProjectName *string `url:"project_name,omitempty" json:"project_name,omitempty"` Username *string `url:"username,omitempty" json:"username,omitempty"` Password *string `url:"password,omitempty" json:"password,omitempty"` PushEvents *bool `url:"push_events,omitempty" json:"push_events,omitempty"` MergeRequestsEvents *bool `url:"merge_requests_events,omitempty" json:"merge_requests_events,omitempty"` TagPushEvents *bool `url:"tag_push_events,omitempty" json:"tag_push_events,omitempty"` } // SetJenkinsCIService sets Jenkins service for a project // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#set-up-jenkins func (s *ServicesService) SetJenkinsCIService(pid interface{}, opt *SetJenkinsCIServiceOptions, options ...RequestOptionFunc) (*JenkinsCIService, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/services/jenkins", PathEscape(project)) req, err := s.client.NewRequest(http.MethodPut, u, opt, options) if err != nil { return nil, nil, err } svc := new(JenkinsCIService) resp, err := s.client.Do(req, svc) if err != nil { return nil, nil, err } return svc, resp, nil } // DeleteJenkinsCIService deletes Jenkins CI service for project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#disable-jenkins func (s *ServicesService) DeleteJenkinsCIService(pid interface{}, options ...RequestOptionFunc) (*Response, error) { project, err := parseID(pid) if err != nil { return nil, err } u := fmt.Sprintf("projects/%s/services/jenkins", PathEscape(project)) req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } // JiraService represents Jira service settings. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#jira type JiraService struct { Service Properties *JiraServiceProperties `json:"properties"` } // JiraServiceProperties represents Jira specific properties. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#jira type JiraServiceProperties struct { URL string `json:"url"` APIURL string `json:"api_url"` Username string `json:"username" ` Password string `json:"password" ` Active bool `json:"active"` JiraAuthType int `json:"jira_auth_type"` JiraIssuePrefix string `json:"jira_issue_prefix"` JiraIssueRegex string `json:"jira_issue_regex"` JiraIssueTransitionAutomatic bool `json:"jira_issue_transition_automatic"` JiraIssueTransitionID string `json:"jira_issue_transition_id"` CommitEvents bool `json:"commit_events"` MergeRequestsEvents bool `json:"merge_requests_events"` CommentOnEventEnabled bool `json:"comment_on_event_enabled"` IssuesEnabled bool `json:"issues_enabled"` ProjectKeys []string `json:"project_keys" ` UseInheritedSettings bool `json:"use_inherited_settings"` // Deprecated: This parameter was removed in GitLab 17.0 ProjectKey string `json:"project_key" ` } // UnmarshalJSON decodes the Jira Service Properties. // // This allows support of JiraIssueTransitionID for both type string (>11.9) and float64 (<11.9) func (p *JiraServiceProperties) UnmarshalJSON(b []byte) error { type Alias JiraServiceProperties raw := struct { *Alias JiraIssueTransitionID interface{} `json:"jira_issue_transition_id"` }{ Alias: (*Alias)(p), } if err := json.Unmarshal(b, &raw); err != nil { return err } switch id := raw.JiraIssueTransitionID.(type) { case nil: // No action needed. case string: p.JiraIssueTransitionID = id case float64: p.JiraIssueTransitionID = strconv.Itoa(int(id)) default: return fmt.Errorf("failed to unmarshal JiraTransitionID of type: %T", id) } return nil } // GetJiraService gets Jira service settings for a project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#get-jira-service-settings func (s *ServicesService) GetJiraService(pid interface{}, options ...RequestOptionFunc) (*JiraService, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/integrations/jira", PathEscape(project)) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } svc := new(JiraService) resp, err := s.client.Do(req, svc) if err != nil { return nil, resp, err } return svc, resp, nil } // SetJiraServiceOptions represents the available SetJiraService() // options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#edit-jira-service type SetJiraServiceOptions struct { URL *string `url:"url,omitempty" json:"url,omitempty"` APIURL *string `url:"api_url,omitempty" json:"api_url,omitempty"` Username *string `url:"username,omitempty" json:"username,omitempty" ` Password *string `url:"password,omitempty" json:"password,omitempty" ` Active *bool `url:"active,omitempty" json:"active,omitempty"` JiraAuthType *int `url:"jira_auth_type,omitempty" json:"jira_auth_type,omitempty"` JiraIssuePrefix *string `url:"jira_issue_prefix,omitempty" json:"jira_issue_prefix,omitempty"` JiraIssueRegex *string `url:"jira_issue_regex,omitempty" json:"jira_issue_regex,omitempty"` JiraIssueTransitionAutomatic *bool `url:"jira_issue_transition_automatic,omitempty" json:"jira_issue_transition_automatic,omitempty"` JiraIssueTransitionID *string `url:"jira_issue_transition_id,omitempty" json:"jira_issue_transition_id,omitempty"` CommitEvents *bool `url:"commit_events,omitempty" json:"commit_events,omitempty"` MergeRequestsEvents *bool `url:"merge_requests_events,omitempty" json:"merge_requests_events,omitempty"` CommentOnEventEnabled *bool `url:"comment_on_event_enabled,omitempty" json:"comment_on_event_enabled,omitempty"` IssuesEnabled *bool `url:"issues_enabled,omitempty" json:"issues_enabled,omitempty"` ProjectKeys *[]string `url:"project_keys,comma,omitempty" json:"project_keys,omitempty" ` UseInheritedSettings *bool `url:"use_inherited_settings,omitempty" json:"use_inherited_settings,omitempty"` // Deprecated: This parameter was removed in GitLab 17.0 ProjectKey *string `url:"project_key,omitempty" json:"project_key,omitempty" ` } // SetJiraService sets Jira service for a project // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#edit-jira-service func (s *ServicesService) SetJiraService(pid interface{}, opt *SetJiraServiceOptions, options ...RequestOptionFunc) (*JiraService, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/integrations/jira", PathEscape(project)) req, err := s.client.NewRequest(http.MethodPut, u, opt, options) if err != nil { return nil, nil, err } svc := new(JiraService) resp, err := s.client.Do(req, svc) if err != nil { return nil, nil, err } return svc, resp, nil } // DeleteJiraService deletes Jira service for project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#delete-jira-service func (s *ServicesService) DeleteJiraService(pid interface{}, options ...RequestOptionFunc) (*Response, error) { project, err := parseID(pid) if err != nil { return nil, err } u := fmt.Sprintf("projects/%s/integrations/jira", PathEscape(project)) req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } // MattermostService represents Mattermost service settings. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#mattermost-notifications type MattermostService struct { Service Properties *MattermostServiceProperties `json:"properties"` } // MattermostServiceProperties represents Mattermost specific properties. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#mattermost-notifications type MattermostServiceProperties struct { WebHook string `json:"webhook"` Username string `json:"username"` Channel string `json:"channel"` NotifyOnlyBrokenPipelines BoolValue `json:"notify_only_broken_pipelines"` BranchesToBeNotified string `json:"branches_to_be_notified"` ConfidentialIssueChannel string `json:"confidential_issue_channel"` ConfidentialNoteChannel string `json:"confidential_note_channel"` IssueChannel string `json:"issue_channel"` MergeRequestChannel string `json:"merge_request_channel"` NoteChannel string `json:"note_channel"` TagPushChannel string `json:"tag_push_channel"` PipelineChannel string `json:"pipeline_channel"` PushChannel string `json:"push_channel"` VulnerabilityChannel string `json:"vulnerability_channel"` WikiPageChannel string `json:"wiki_page_channel"` } // GetMattermostService gets Mattermost service settings for a project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#get-slack-service-settings func (s *ServicesService) GetMattermostService(pid interface{}, options ...RequestOptionFunc) (*MattermostService, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/services/mattermost", PathEscape(project)) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } svc := new(MattermostService) resp, err := s.client.Do(req, svc) if err != nil { return nil, resp, err } return svc, resp, nil } // SetMattermostServiceOptions represents the available SetMattermostService() // options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#createedit-mattermost-notifications-service type SetMattermostServiceOptions struct { WebHook *string `url:"webhook,omitempty" json:"webhook,omitempty"` Username *string `url:"username,omitempty" json:"username,omitempty"` Channel *string `url:"channel,omitempty" json:"channel,omitempty"` NotifyOnlyBrokenPipelines *bool `url:"notify_only_broken_pipelines,omitempty" json:"notify_only_broken_pipelines,omitempty"` BranchesToBeNotified *string `url:"branches_to_be_notified,omitempty" json:"branches_to_be_notified,omitempty"` PushEvents *bool `url:"push_events,omitempty" json:"push_events,omitempty"` IssuesEvents *bool `url:"issues_events,omitempty" json:"issues_events,omitempty"` ConfidentialIssuesEvents *bool `url:"confidential_issues_events,omitempty" json:"confidential_issues_events,omitempty"` MergeRequestsEvents *bool `url:"merge_requests_events,omitempty" json:"merge_requests_events,omitempty"` TagPushEvents *bool `url:"tag_push_events,omitempty" json:"tag_push_events,omitempty"` NoteEvents *bool `url:"note_events,omitempty" json:"note_events,omitempty"` ConfidentialNoteChannel *string `url:"confidential_note_channel,omitempty" json:"confidential_note_channel,omitempty"` PipelineEvents *bool `url:"pipeline_events,omitempty" json:"pipeline_events,omitempty"` WikiPageEvents *bool `url:"wiki_page_events,omitempty" json:"wiki_page_events,omitempty"` PushChannel *string `url:"push_channel,omitempty" json:"push_channel,omitempty"` IssueChannel *string `url:"issue_channel,omitempty" json:"issue_channel,omitempty"` ConfidentialIssueChannel *string `url:"confidential_issue_channel,omitempty" json:"confidential_issue_channel,omitempty"` MergeRequestChannel *string `url:"merge_request_channel,omitempty" json:"merge_request_channel,omitempty"` NoteChannel *string `url:"note_channel,omitempty" json:"note_channel,omitempty"` ConfidentialNoteEvents *bool `url:"confidential_note_events,omitempty" json:"confidential_note_events,omitempty"` TagPushChannel *string `url:"tag_push_channel,omitempty" json:"tag_push_channel,omitempty"` PipelineChannel *string `url:"pipeline_channel,omitempty" json:"pipeline_channel,omitempty"` WikiPageChannel *string `url:"wiki_page_channel,omitempty" json:"wiki_page_channel,omitempty"` } // SetMattermostService sets Mattermost service for a project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#createedit-mattermost-notifications-service func (s *ServicesService) SetMattermostService(pid interface{}, opt *SetMattermostServiceOptions, options ...RequestOptionFunc) (*MattermostService, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/services/mattermost", PathEscape(project)) req, err := s.client.NewRequest(http.MethodPut, u, opt, options) if err != nil { return nil, nil, err } svc := new(MattermostService) resp, err := s.client.Do(req, svc) if err != nil { return nil, nil, err } return svc, resp, nil } // DeleteMattermostService deletes Mattermost service for project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#delete-mattermost-notifications-service func (s *ServicesService) DeleteMattermostService(pid interface{}, options ...RequestOptionFunc) (*Response, error) { project, err := parseID(pid) if err != nil { return nil, err } u := fmt.Sprintf("projects/%s/services/mattermost", PathEscape(project)) req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } // MattermostSlashCommandsService represents Mattermost slash commands settings. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#mattermost-slash-commands type MattermostSlashCommandsService struct { Service Properties *MattermostSlashCommandsProperties `json:"properties"` } // MattermostSlashCommandsProperties represents Mattermost slash commands specific properties. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#mattermost-slash-commands type MattermostSlashCommandsProperties struct { Token string `json:"token"` Username string `json:"username,omitempty"` } // GetMattermostSlashCommandsService gets Slack Mattermost commands service settings for a project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#get-mattermost-slash-command-integration-settings func (s *ServicesService) GetMattermostSlashCommandsService(pid interface{}, options ...RequestOptionFunc) (*MattermostSlashCommandsService, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/services/mattermost-slash-commands", PathEscape(project)) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } svc := new(MattermostSlashCommandsService) resp, err := s.client.Do(req, svc) if err != nil { return nil, resp, err } return svc, resp, nil } // SetMattermostSlashCommandsServiceOptions represents the available SetSlackSlashCommandsService() // options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#get-mattermost-slash-command-integration-settings type SetMattermostSlashCommandsServiceOptions struct { Token *string `url:"token,omitempty" json:"token,omitempty"` Username *string `url:"username,omitempty" json:"username,omitempty"` } // SetMattermostSlashCommandsService sets Mattermost slash commands service for a project // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#createedit-mattermost-slash-command-integration func (s *ServicesService) SetMattermostSlashCommandsService(pid interface{}, opt *SetMattermostSlashCommandsServiceOptions, options ...RequestOptionFunc) (*MattermostSlashCommandsService, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/services/mattermost-slash-commands", PathEscape(project)) req, err := s.client.NewRequest(http.MethodPut, u, opt, options) if err != nil { return nil, nil, err } svc := new(MattermostSlashCommandsService) resp, err := s.client.Do(req, svc) if err != nil { return nil, nil, err } return svc, resp, nil } // DeleteMattermostSlashCommandsService deletes Mattermost slash commands service for project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#disable-mattermost-slash-command-integration func (s *ServicesService) DeleteMattermostSlashCommandsService(pid interface{}, options ...RequestOptionFunc) (*Response, error) { project, err := parseID(pid) if err != nil { return nil, err } u := fmt.Sprintf("projects/%s/services/mattermost-slash-commands", PathEscape(project)) req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } // MicrosoftTeamsService represents Microsoft Teams service settings. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#microsoft-teams type MicrosoftTeamsService struct { Service Properties *MicrosoftTeamsServiceProperties `json:"properties"` } // MicrosoftTeamsServiceProperties represents Microsoft Teams specific properties. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#microsoft-teams type MicrosoftTeamsServiceProperties struct { WebHook string `json:"webhook"` NotifyOnlyBrokenPipelines BoolValue `json:"notify_only_broken_pipelines"` BranchesToBeNotified string `json:"branches_to_be_notified"` IssuesEvents BoolValue `json:"issues_events"` ConfidentialIssuesEvents BoolValue `json:"confidential_issues_events"` MergeRequestsEvents BoolValue `json:"merge_requests_events"` TagPushEvents BoolValue `json:"tag_push_events"` NoteEvents BoolValue `json:"note_events"` ConfidentialNoteEvents BoolValue `json:"confidential_note_events"` PipelineEvents BoolValue `json:"pipeline_events"` WikiPageEvents BoolValue `json:"wiki_page_events"` } // GetMicrosoftTeamsService gets MicrosoftTeams service settings for a project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#get-microsoft-teams-service-settings func (s *ServicesService) GetMicrosoftTeamsService(pid interface{}, options ...RequestOptionFunc) (*MicrosoftTeamsService, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/services/microsoft-teams", PathEscape(project)) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } svc := new(MicrosoftTeamsService) resp, err := s.client.Do(req, svc) if err != nil { return nil, resp, err } return svc, resp, nil } // SetMicrosoftTeamsServiceOptions represents the available SetMicrosoftTeamsService() // options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#create-edit-microsoft-teams-service type SetMicrosoftTeamsServiceOptions struct { WebHook *string `url:"webhook,omitempty" json:"webhook,omitempty"` NotifyOnlyBrokenPipelines *bool `url:"notify_only_broken_pipelines,omitempty" json:"notify_only_broken_pipelines,omitempty"` BranchesToBeNotified *string `url:"branches_to_be_notified,omitempty" json:"branches_to_be_notified,omitempty"` PushEvents *bool `url:"push_events,omitempty" json:"push_events,omitempty"` IssuesEvents *bool `url:"issues_events,omitempty" json:"issues_events,omitempty"` ConfidentialIssuesEvents *bool `url:"confidential_issues_events,omitempty" json:"confidential_issues_events,omitempty"` MergeRequestsEvents *bool `url:"merge_requests_events,omitempty" json:"merge_requests_events,omitempty"` TagPushEvents *bool `url:"tag_push_events,omitempty" json:"tag_push_events,omitempty"` NoteEvents *bool `url:"note_events,omitempty" json:"note_events,omitempty"` ConfidentialNoteEvents *bool `url:"confidential_note_events,omitempty" json:"confidential_note_events,omitempty"` PipelineEvents *bool `url:"pipeline_events,omitempty" json:"pipeline_events,omitempty"` WikiPageEvents *bool `url:"wiki_page_events,omitempty" json:"wiki_page_events,omitempty"` } // SetMicrosoftTeamsService sets Microsoft Teams service for a project // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#create-edit-microsoft-teams-service func (s *ServicesService) SetMicrosoftTeamsService(pid interface{}, opt *SetMicrosoftTeamsServiceOptions, options ...RequestOptionFunc) (*MicrosoftTeamsService, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/services/microsoft-teams", PathEscape(project)) req, err := s.client.NewRequest(http.MethodPut, u, opt, options) if err != nil { return nil, nil, err } svc := new(MicrosoftTeamsService) resp, err := s.client.Do(req, svc) if err != nil { return nil, nil, err } return svc, resp, nil } // DeleteMicrosoftTeamsService deletes Microsoft Teams service for project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#delete-microsoft-teams-service func (s *ServicesService) DeleteMicrosoftTeamsService(pid interface{}, options ...RequestOptionFunc) (*Response, error) { project, err := parseID(pid) if err != nil { return nil, err } u := fmt.Sprintf("projects/%s/services/microsoft-teams", PathEscape(project)) req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } // PipelinesEmailService represents Pipelines Email service settings. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#pipeline-emails type PipelinesEmailService struct { Service Properties *PipelinesEmailProperties `json:"properties"` } // PipelinesEmailProperties represents PipelinesEmail specific properties. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#pipeline-emails type PipelinesEmailProperties struct { Recipients string `json:"recipients"` NotifyOnlyBrokenPipelines BoolValue `json:"notify_only_broken_pipelines"` NotifyOnlyDefaultBranch BoolValue `json:"notify_only_default_branch"` BranchesToBeNotified string `json:"branches_to_be_notified"` } // GetPipelinesEmailService gets Pipelines Email service settings for a project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#get-pipeline-emails-service-settings func (s *ServicesService) GetPipelinesEmailService(pid interface{}, options ...RequestOptionFunc) (*PipelinesEmailService, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/services/pipelines-email", PathEscape(project)) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } svc := new(PipelinesEmailService) resp, err := s.client.Do(req, svc) if err != nil { return nil, resp, err } return svc, resp, nil } // SetPipelinesEmailServiceOptions represents the available // SetPipelinesEmailService() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#pipeline-emails type SetPipelinesEmailServiceOptions struct { Recipients *string `url:"recipients,omitempty" json:"recipients,omitempty"` NotifyOnlyBrokenPipelines *bool `url:"notify_only_broken_pipelines,omitempty" json:"notify_only_broken_pipelines,omitempty"` NotifyOnlyDefaultBranch *bool `url:"notify_only_default_branch,omitempty" json:"notify_only_default_branch,omitempty"` AddPusher *bool `url:"add_pusher,omitempty" json:"add_pusher,omitempty"` BranchesToBeNotified *string `url:"branches_to_be_notified,omitempty" json:"branches_to_be_notified,omitempty"` PipelineEvents *bool `url:"pipeline_events,omitempty" json:"pipeline_events,omitempty"` } // SetPipelinesEmailService sets Pipelines Email service for a project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#pipeline-emails func (s *ServicesService) SetPipelinesEmailService(pid interface{}, opt *SetPipelinesEmailServiceOptions, options ...RequestOptionFunc) (*PipelinesEmailService, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/services/pipelines-email", PathEscape(project)) req, err := s.client.NewRequest(http.MethodPut, u, opt, options) if err != nil { return nil, nil, err } svc := new(PipelinesEmailService) resp, err := s.client.Do(req, svc) if err != nil { return nil, nil, err } return svc, resp, nil } // DeletePipelinesEmailService deletes Pipelines Email service settings for a project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#delete-pipeline-emails-service func (s *ServicesService) DeletePipelinesEmailService(pid interface{}, options ...RequestOptionFunc) (*Response, error) { project, err := parseID(pid) if err != nil { return nil, err } u := fmt.Sprintf("projects/%s/services/pipelines-email", PathEscape(project)) req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } // PrometheusService represents Prometheus service settings. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#prometheus type PrometheusService struct { Service Properties *PrometheusServiceProperties `json:"properties"` } // PrometheusServiceProperties represents Prometheus specific properties. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#prometheus type PrometheusServiceProperties struct { APIURL string `json:"api_url"` GoogleIAPAudienceClientID string `json:"google_iap_audience_client_id"` GoogleIAPServiceAccountJSON string `json:"google_iap_service_account_json"` } // GetPrometheusService gets Prometheus service settings for a project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#get-prometheus-service-settings func (s *ServicesService) GetPrometheusService(pid interface{}, options ...RequestOptionFunc) (*PrometheusService, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/services/prometheus", PathEscape(project)) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } svc := new(PrometheusService) resp, err := s.client.Do(req, svc) if err != nil { return nil, resp, err } return svc, resp, nil } // SetPrometheusServiceOptions represents the available SetPrometheusService() // options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#createedit-prometheus-service type SetPrometheusServiceOptions struct { APIURL *string `url:"api_url,omitempty" json:"api_url,omitempty"` GoogleIAPAudienceClientID *string `url:"google_iap_audience_client_id,omitempty" json:"google_iap_audience_client_id,omitempty"` GoogleIAPServiceAccountJSON *string `url:"google_iap_service_account_json,omitempty" json:"google_iap_service_account_json,omitempty"` } // SetPrometheusService sets Prometheus service for a project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#createedit-prometheus-service func (s *ServicesService) SetPrometheusService(pid interface{}, opt *SetPrometheusServiceOptions, options ...RequestOptionFunc) (*PrometheusService, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/services/prometheus", PathEscape(project)) req, err := s.client.NewRequest(http.MethodPut, u, opt, options) if err != nil { return nil, nil, err } svc := new(PrometheusService) resp, err := s.client.Do(req, svc) if err != nil { return nil, nil, err } return svc, resp, nil } // DeletePrometheusService deletes Prometheus service settings for a project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#delete-prometheus-service func (s *ServicesService) DeletePrometheusService(pid interface{}, options ...RequestOptionFunc) (*Response, error) { project, err := parseID(pid) if err != nil { return nil, err } u := fmt.Sprintf("projects/%s/services/prometheus", PathEscape(project)) req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } // RedmineService represents the Redmine service settings. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#redmine type RedmineService struct { Service Properties *RedmineServiceProperties `json:"properties"` } // RedmineServiceProperties represents Redmine specific properties. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#redmine type RedmineServiceProperties struct { NewIssueURL string `json:"new_issue_url"` ProjectURL string `json:"project_url"` IssuesURL string `json:"issues_url"` UseInheritedSettings BoolValue `json:"use_inherited_settings"` } // GetRedmineService gets Redmine service settings for a project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#get-redmine-settings func (s *ServicesService) GetRedmineService(pid interface{}, options ...RequestOptionFunc) (*RedmineService, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/integrations/redmine", PathEscape(project)) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } svc := new(RedmineService) resp, err := s.client.Do(req, svc) if err != nil { return nil, resp, err } return svc, resp, nil } // SetRedmineServiceOptions represents the available SetRedmineService(). // options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#set-up-redmine type SetRedmineServiceOptions struct { NewIssueURL *string `url:"new_issue_url,omitempty" json:"new_issue_url,omitempty"` ProjectURL *string `url:"project_url,omitempty" json:"project_url,omitempty"` IssuesURL *string `url:"issues_url,omitempty" json:"issues_url,omitempty"` UseInheritedSettings *bool `url:"use_inherited_settings,omitempty" json:"use_inherited_settings,omitempty"` } // SetRedmineService sets Redmine service for a project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#set-up-redmine func (s *ServicesService) SetRedmineService(pid interface{}, opt *SetRedmineServiceOptions, options ...RequestOptionFunc) (*RedmineService, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/integrations/redmine", PathEscape(project)) req, err := s.client.NewRequest(http.MethodPut, u, opt, options) if err != nil { return nil, nil, err } svc := new(RedmineService) resp, err := s.client.Do(req, svc) if err != nil { return nil, nil, err } return svc, resp, nil } // DeleteRedmineService deletes Redmine service for project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#disable-redmine func (s *ServicesService) DeleteRedmineService(pid interface{}, options ...RequestOptionFunc) (*Response, error) { project, err := parseID(pid) if err != nil { return nil, err } u := fmt.Sprintf("projects/%s/integrations/redmine", PathEscape(project)) req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } // SlackService represents Slack service settings. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#slack type SlackService struct { Service Properties *SlackServiceProperties `json:"properties"` } // SlackServiceProperties represents Slack specific properties. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#slack type SlackServiceProperties struct { WebHook string `json:"webhook"` Username string `json:"username"` Channel string `json:"channel"` NotifyOnlyBrokenPipelines BoolValue `json:"notify_only_broken_pipelines"` NotifyOnlyDefaultBranch BoolValue `json:"notify_only_default_branch"` BranchesToBeNotified string `json:"branches_to_be_notified"` AlertChannel string `json:"alert_channel"` ConfidentialIssueChannel string `json:"confidential_issue_channel"` ConfidentialNoteChannel string `json:"confidential_note_channel"` DeploymentChannel string `json:"deployment_channel"` IssueChannel string `json:"issue_channel"` MergeRequestChannel string `json:"merge_request_channel"` NoteChannel string `json:"note_channel"` TagPushChannel string `json:"tag_push_channel"` PipelineChannel string `json:"pipeline_channel"` PushChannel string `json:"push_channel"` VulnerabilityChannel string `json:"vulnerability_channel"` WikiPageChannel string `json:"wiki_page_channel"` } // GetSlackService gets Slack service settings for a project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#get-slack-service-settings func (s *ServicesService) GetSlackService(pid interface{}, options ...RequestOptionFunc) (*SlackService, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/services/slack", PathEscape(project)) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } svc := new(SlackService) resp, err := s.client.Do(req, svc) if err != nil { return nil, resp, err } return svc, resp, nil } // SetSlackServiceOptions represents the available SetSlackService() // options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#edit-slack-service type SetSlackServiceOptions struct { WebHook *string `url:"webhook,omitempty" json:"webhook,omitempty"` Username *string `url:"username,omitempty" json:"username,omitempty"` Channel *string `url:"channel,omitempty" json:"channel,omitempty"` NotifyOnlyBrokenPipelines *bool `url:"notify_only_broken_pipelines,omitempty" json:"notify_only_broken_pipelines,omitempty"` NotifyOnlyDefaultBranch *bool `url:"notify_only_default_branch,omitempty" json:"notify_only_default_branch,omitempty"` BranchesToBeNotified *string `url:"branches_to_be_notified,omitempty" json:"branches_to_be_notified,omitempty"` AlertChannel *string `url:"alert_channel,omitempty" json:"alert_channel,omitempty"` AlertEvents *bool `url:"alert_events,omitempty" json:"alert_events,omitempty"` ConfidentialIssueChannel *string `url:"confidential_issue_channel,omitempty" json:"confidential_issue_channel,omitempty"` ConfidentialIssuesEvents *bool `url:"confidential_issues_events,omitempty" json:"confidential_issues_events,omitempty"` ConfidentialNoteChannel *string `url:"confidential_note_channel,omitempty" json:"confidential_note_channel,omitempty"` ConfidentialNoteEvents *bool `url:"confidential_note_events,omitempty" json:"confidential_note_events,omitempty"` DeploymentChannel *string `url:"deployment_channel,omitempty" json:"deployment_channel,omitempty"` DeploymentEvents *bool `url:"deployment_events,omitempty" json:"deployment_events,omitempty"` IssueChannel *string `url:"issue_channel,omitempty" json:"issue_channel,omitempty"` IssuesEvents *bool `url:"issues_events,omitempty" json:"issues_events,omitempty"` MergeRequestChannel *string `url:"merge_request_channel,omitempty" json:"merge_request_channel,omitempty"` MergeRequestsEvents *bool `url:"merge_requests_events,omitempty" json:"merge_requests_events,omitempty"` NoteChannel *string `url:"note_channel,omitempty" json:"note_channel,omitempty"` NoteEvents *bool `url:"note_events,omitempty" json:"note_events,omitempty"` PipelineChannel *string `url:"pipeline_channel,omitempty" json:"pipeline_channel,omitempty"` PipelineEvents *bool `url:"pipeline_events,omitempty" json:"pipeline_events,omitempty"` PushChannel *string `url:"push_channel,omitempty" json:"push_channel,omitempty"` PushEvents *bool `url:"push_events,omitempty" json:"push_events,omitempty"` TagPushChannel *string `url:"tag_push_channel,omitempty" json:"tag_push_channel,omitempty"` TagPushEvents *bool `url:"tag_push_events,omitempty" json:"tag_push_events,omitempty"` WikiPageChannel *string `url:"wiki_page_channel,omitempty" json:"wiki_page_channel,omitempty"` WikiPageEvents *bool `url:"wiki_page_events,omitempty" json:"wiki_page_events,omitempty"` } // SetSlackService sets Slack service for a project // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#edit-slack-service func (s *ServicesService) SetSlackService(pid interface{}, opt *SetSlackServiceOptions, options ...RequestOptionFunc) (*SlackService, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/services/slack", PathEscape(project)) req, err := s.client.NewRequest(http.MethodPut, u, opt, options) if err != nil { return nil, nil, err } svc := new(SlackService) resp, err := s.client.Do(req, svc) if err != nil { return nil, nil, err } return svc, resp, nil } // DeleteSlackService deletes Slack service for project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#delete-slack-service func (s *ServicesService) DeleteSlackService(pid interface{}, options ...RequestOptionFunc) (*Response, error) { project, err := parseID(pid) if err != nil { return nil, err } u := fmt.Sprintf("projects/%s/services/slack", PathEscape(project)) req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } // SlackSlashCommandsService represents Slack slash commands settings. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#slack-slash-commands type SlackSlashCommandsService struct { Service Properties *SlackSlashCommandsProperties `json:"properties"` } // SlackSlashCommandsProperties represents Slack slash commands specific properties. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#slack-slash-commands type SlackSlashCommandsProperties struct { Token string `json:"token"` } // GetSlackSlashCommandsService gets Slack slash commands service settings for a project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#get-slack-slash-command-integration-settings func (s *ServicesService) GetSlackSlashCommandsService(pid interface{}, options ...RequestOptionFunc) (*SlackSlashCommandsService, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/services/slack-slash-commands", PathEscape(project)) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } svc := new(SlackSlashCommandsService) resp, err := s.client.Do(req, svc) if err != nil { return nil, resp, err } return svc, resp, nil } // SetSlackSlashCommandsServiceOptions represents the available SetSlackSlashCommandsService() // options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#createedit-slack-slash-command-service type SetSlackSlashCommandsServiceOptions struct { Token *string `url:"token,omitempty" json:"token,omitempty"` } // SetSlackSlashCommandsService sets Slack slash commands service for a project // // GitLab API docs: // https://docs.gitlab.com/13.12/ee/api/integrations.html#createedit-slack-slash-command-service func (s *ServicesService) SetSlackSlashCommandsService(pid interface{}, opt *SetSlackSlashCommandsServiceOptions, options ...RequestOptionFunc) (*SlackSlashCommandsService, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/services/slack-slash-commands", PathEscape(project)) req, err := s.client.NewRequest(http.MethodPut, u, opt, options) if err != nil { return nil, nil, err } svc := new(SlackSlashCommandsService) resp, err := s.client.Do(req, svc) if err != nil { return nil, nil, err } return svc, resp, nil } // DeleteSlackSlashCommandsService deletes Slack slash commands service for project. // // GitLab API docs: // https://docs.gitlab.com/13.12/ee/api/integrations.html#delete-slack-slash-command-service func (s *ServicesService) DeleteSlackSlashCommandsService(pid interface{}, options ...RequestOptionFunc) (*Response, error) { project, err := parseID(pid) if err != nil { return nil, err } u := fmt.Sprintf("projects/%s/services/slack-slash-commands", PathEscape(project)) req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } // TelegramService represents Telegram service settings. // // Gitlab API docs: // https://docs.gitlab.com/ee/api/integrations.html#telegram type TelegramService struct { Service Properties *TelegramServiceProperties `json:"properties"` } // TelegramServiceProperties represents Telegram specific properties. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#set-up-telegram type TelegramServiceProperties struct { Room string `json:"room"` NotifyOnlyBrokenPipelines bool `json:"notify_only_broken_pipelines"` BranchesToBeNotified string `json:"branches_to_be_notified"` } // GetTelegramService gets MicrosoftTeams service settings for a project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#get-telegram-settings func (s *ServicesService) GetTelegramService(pid interface{}, options ...RequestOptionFunc) (*TelegramService, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/services/telegram", PathEscape(project)) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } svc := new(TelegramService) resp, err := s.client.Do(req, svc) if err != nil { return nil, resp, err } return svc, resp, nil } // SetTelegramServiceOptions represents the available SetTelegramService() // options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#set-up-telegram type SetTelegramServiceOptions struct { Token *string `url:"token,omitempty" json:"token,omitempty"` Room *string `url:"room,omitempty" json:"room,omitempty"` NotifyOnlyBrokenPipelines *bool `url:"notify_only_broken_pipelines,omitempty" json:"notify_only_broken_pipelines,omitempty"` BranchesToBeNotified *string `url:"branches_to_be_notified,omitempty" json:"branches_to_be_notified,omitempty"` PushEvents *bool `url:"push_events,omitempty" json:"push_events,omitempty"` IssuesEvents *bool `url:"issues_events,omitempty" json:"issues_events,omitempty"` ConfidentialIssuesEvents *bool `url:"confidential_issues_events,omitempty" json:"confidential_issues_events,omitempty"` MergeRequestsEvents *bool `url:"merge_requests_events,omitempty" json:"merge_requests_events,omitempty"` TagPushEvents *bool `url:"tag_push_events,omitempty" json:"tag_push_events,omitempty"` NoteEvents *bool `url:"note_events,omitempty" json:"note_events,omitempty"` ConfidentialNoteEvents *bool `url:"confidential_note_events,omitempty" json:"confidential_note_events,omitempty"` PipelineEvents *bool `url:"pipeline_events,omitempty" json:"pipeline_events,omitempty"` WikiPageEvents *bool `url:"wiki_page_events,omitempty" json:"wiki_page_events,omitempty"` } // SetTelegramService sets Telegram service for a project // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#set-up-telegram func (s *ServicesService) SetTelegramService(pid interface{}, opt *SetTelegramServiceOptions, options ...RequestOptionFunc) (*TelegramService, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/services/telegram", PathEscape(project)) req, err := s.client.NewRequest(http.MethodPut, u, opt, options) if err != nil { return nil, nil, err } svc := new(TelegramService) resp, err := s.client.Do(req, svc) if err != nil { return nil, nil, err } return svc, resp, nil } // DeleteTelegramService deletes Telegram service for project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#disable-telegram func (s *ServicesService) DeleteTelegramService(pid interface{}, options ...RequestOptionFunc) (*Response, error) { project, err := parseID(pid) if err != nil { return nil, err } u := fmt.Sprintf("projects/%s/services/telegram", PathEscape(project)) req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } // YouTrackService represents YouTrack service settings. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#youtrack type YouTrackService struct { Service Properties *YouTrackServiceProperties `json:"properties"` } // YouTrackServiceProperties represents YouTrack specific properties. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#youtrack type YouTrackServiceProperties struct { IssuesURL string `json:"issues_url"` ProjectURL string `json:"project_url"` Description string `json:"description"` PushEvents bool `json:"push_events"` } // GetYouTrackService gets YouTrack service settings for a project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#get-youtrack-service-settings func (s *ServicesService) GetYouTrackService(pid interface{}, options ...RequestOptionFunc) (*YouTrackService, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/services/youtrack", PathEscape(project)) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } svc := new(YouTrackService) resp, err := s.client.Do(req, svc) if err != nil { return nil, resp, err } return svc, resp, nil } // SetYouTrackServiceOptions represents the available SetYouTrackService() // options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#createedit-youtrack-service type SetYouTrackServiceOptions struct { IssuesURL *string `url:"issues_url,omitempty" json:"issues_url,omitempty"` ProjectURL *string `url:"project_url,omitempty" json:"project_url,omitempty"` Description *string `url:"description,omitempty" json:"description,omitempty"` PushEvents *bool `url:"push_events,omitempty" json:"push_events,omitempty"` } // SetYouTrackService sets YouTrack service for a project // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#createedit-youtrack-service func (s *ServicesService) SetYouTrackService(pid interface{}, opt *SetYouTrackServiceOptions, options ...RequestOptionFunc) (*YouTrackService, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/services/youtrack", PathEscape(project)) svc := new(YouTrackService) req, err := s.client.NewRequest(http.MethodPut, u, opt, options) if err != nil { return nil, nil, err } resp, err := s.client.Do(req, svc) if err != nil { return nil, nil, err } return svc, resp, nil } // DeleteYouTrackService deletes YouTrack service settings for a project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/integrations.html#delete-youtrack-service func (s *ServicesService) DeleteYouTrackService(pid interface{}, options ...RequestOptionFunc) (*Response, error) { project, err := parseID(pid) if err != nil { return nil, err } u := fmt.Sprintf("projects/%s/services/youtrack", PathEscape(project)) req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } golang-gitlab-gitlab-org-api-client-go-0.123.0/services_test.go000066400000000000000000001033651475761473200243000ustar00rootroot00000000000000// // Copyright 2021, Sander van Harmelen // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "fmt" "net/http" "reflect" "testing" "time" ) func TestListServices(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/services", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, `[{"id":1},{"id":2}]`) }) want := []*Service{{ID: 1}, {ID: 2}} services, _, err := client.Services.ListServices(1) if err != nil { t.Fatalf("Services.ListServices returns an error: %v", err) } if !reflect.DeepEqual(want, services) { t.Errorf("Services.ListServices returned %+v, want %+v", services, want) } } func TestCustomIssueTrackerService(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/services/custom-issue-tracker", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, `{ "id": 1, "title": "5", "push_events": true, "properties": { "new_issue_url": "1", "issues_url": "2", "project_url": "3" } }`) }) want := &CustomIssueTrackerService{ Service: Service{ ID: 1, Title: "5", PushEvents: true, }, Properties: &CustomIssueTrackerServiceProperties{ NewIssueURL: "1", IssuesURL: "2", ProjectURL: "3", }, } service, _, err := client.Services.GetCustomIssueTrackerService(1) if err != nil { t.Fatalf("Services.GetCustomIssueTrackerService returns an error: %v", err) } if !reflect.DeepEqual(want, service) { t.Errorf("Services.GetCustomIssueTrackerService returned %+v, want %+v", service, want) } } func TestSetCustomIssueTrackerService(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/services/custom-issue-tracker", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPut) fmt.Fprint(w, `{"id":1, "properties": {}}`) }) opt := &SetCustomIssueTrackerServiceOptions{ NewIssueURL: Ptr("1"), IssuesURL: Ptr("2"), ProjectURL: Ptr("3"), } _, _, err := client.Services.SetCustomIssueTrackerService(1, opt) if err != nil { t.Fatalf("Services.SetCustomIssueTrackerService returns an error: %v", err) } } func TestDeleteCustomIssueTrackerService(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/services/custom-issue-tracker", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodDelete) }) _, err := client.Services.DeleteCustomIssueTrackerService(1) if err != nil { t.Fatalf("Services.DeleteCustomIssueTrackerService returns an error: %v", err) } } func TestGetDataDogService(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/services/datadog", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, `{ "id": 1, "active": true, "properties": { "api_url": "", "datadog_env": "production", "datadog_service": "gitlab", "datadog_site": "datadoghq.com", "datadog_tags": "country=canada\nprovince=ontario", "archive_trace_events": true } }`) }) want := &DataDogService{ Service: Service{ID: 1, Active: true}, Properties: &DataDogServiceProperties{ APIURL: "", DataDogEnv: "production", DataDogService: "gitlab", DataDogSite: "datadoghq.com", DataDogTags: "country=canada\nprovince=ontario", ArchiveTraceEvents: true, }, } service, _, err := client.Services.GetDataDogService(1) if err != nil { t.Fatalf("Services.GetDataDogService returns an error: %v", err) } if !reflect.DeepEqual(want, service) { t.Errorf("Services.GetDataDogService returned %+v, want %+v", service, want) } } func TestSetDataDogService(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/services/datadog", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPut) fmt.Fprint(w, `{"id":1, "properties": {}}`) }) opt := &SetDataDogServiceOptions{ APIKey: String("secret"), APIURL: String("https://some-api.com"), DataDogEnv: String("sandbox"), DataDogService: String("source-code"), DataDogSite: String("datadoghq.eu"), DataDogTags: String("country=france"), ArchiveTraceEvents: Bool(false), } _, _, err := client.Services.SetDataDogService(1, opt) if err != nil { t.Fatalf("Services.SetDataDogService returns an error: %v", err) } } func TestDeleteDataDogService(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/services/datadog", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodDelete) }) _, err := client.Services.DeleteDataDogService(1) if err != nil { t.Fatalf("Services.DeleteDataDogService returns an error: %v", err) } } func TestGetDiscordService(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/services/discord", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, `{"id":1}`) }) want := &DiscordService{Service: Service{ID: 1}} service, _, err := client.Services.GetDiscordService(1) if err != nil { t.Fatalf("Services.GetDiscordService returns an error: %v", err) } if !reflect.DeepEqual(want, service) { t.Errorf("Services.GetDiscordService returned %+v, want %+v", service, want) } } func TestSetDiscordService(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/services/discord", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPut) fmt.Fprint(w, `{"id":1, "properties": {}}`) }) opt := &SetDiscordServiceOptions{ WebHook: Ptr("webhook_uri"), } _, _, err := client.Services.SetDiscordService(1, opt) if err != nil { t.Fatalf("Services.SetDiscordService returns an error: %v", err) } } func TestDeleteDiscordService(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/services/discord", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodDelete) }) _, err := client.Services.DeleteDiscordService(1) if err != nil { t.Fatalf("Services.DeleteDiscordService returns an error: %v", err) } } func TestGetDroneCIService(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/services/drone-ci", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, `{"id":1}`) }) want := &DroneCIService{Service: Service{ID: 1}} service, _, err := client.Services.GetDroneCIService(1) if err != nil { t.Fatalf("Services.GetDroneCIService returns an error: %v", err) } if !reflect.DeepEqual(want, service) { t.Errorf("Services.GetDroneCIService returned %+v, want %+v", service, want) } } func TestSetDroneCIService(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/services/drone-ci", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPut) fmt.Fprint(w, `{"id":1, "properties": {}}`) }) opt := &SetDroneCIServiceOptions{Ptr("token"), Ptr("drone-url"), Ptr(true), nil, nil, nil} _, _, err := client.Services.SetDroneCIService(1, opt) if err != nil { t.Fatalf("Services.SetDroneCIService returns an error: %v", err) } } func TestDeleteDroneCIService(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/services/drone-ci", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodDelete) }) _, err := client.Services.DeleteDroneCIService(1) if err != nil { t.Fatalf("Services.DeleteDroneCIService returns an error: %v", err) } } func TestGetEmailsOnPushService(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/integrations/emails-on-push", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, `{"id":1}`) }) want := &EmailsOnPushService{Service: Service{ID: 1}} service, _, err := client.Services.GetEmailsOnPushService(1) if err != nil { t.Fatalf("Services.GetEmailsOnPushService returns an error: %v", err) } if !reflect.DeepEqual(want, service) { t.Errorf("Services.GetEmailsOnPushService returned %+v, want %+v", service, want) } } func TestSetEmailsOnPushService(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/integrations/emails-on-push", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPut) fmt.Fprint(w, `{"id":1, "properties": {}}`) }) opt := &SetEmailsOnPushServiceOptions{Ptr("t"), Ptr(true), Ptr(true), Ptr(true), Ptr(true), Ptr("t")} _, _, err := client.Services.SetEmailsOnPushService(1, opt) if err != nil { t.Fatalf("Services.SetEmailsOnPushService returns an error: %v", err) } } func TestDeleteEmailsOnPushService(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/integrations/emails-on-push", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodDelete) }) _, err := client.Services.DeleteEmailsOnPushService(1) if err != nil { t.Fatalf("Services.DeleteEmailsOnPushService returns an error: %v", err) } } func TestGetHarborService(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/integrations/harbor", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, `{"id":1}`) }) want := &HarborService{Service: Service{ID: 1}} service, _, err := client.Services.GetHarborService(1) if err != nil { t.Fatalf("Services.GetHarborService returns an error: %v", err) } if !reflect.DeepEqual(want, service) { t.Errorf("Services.GetHarborService returned %+v, want %+v", service, want) } } func TestSetHarborService(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/integrations/harbor", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPut) fmt.Fprint(w, `{"id":1, "properties": {}}`) }) opt := &SetHarborServiceOptions{ URL: Ptr("url"), ProjectName: Ptr("project"), Username: Ptr("user"), Password: Ptr("pass"), UseInheritedSettings: Ptr(false), } _, _, err := client.Services.SetHarborService(1, opt) if err != nil { t.Fatalf("Services.SetHarborService returns an error: %v", err) } } func TestDeleteHarborService(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/integrations/harbor", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodDelete) }) _, err := client.Services.DeleteHarborService(1) if err != nil { t.Fatalf("Services.DeleteHarborService returns an error: %v", err) } } func TestGetSlackApplication(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/integrations/gitlab-slack-application", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, `{"id":1}`) }) want := &SlackApplication{Service: Service{ID: 1}} service, _, err := client.Services.GetSlackApplication(1) if err != nil { t.Fatalf("Services.GetSlackApplication returns an error: %v", err) } if !reflect.DeepEqual(want, service) { t.Errorf("Services.GetSlackApplication returned %+v, want %+v", service, want) } } func TestSetSlackApplication(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/integrations/gitlab-slack-application", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPut) fmt.Fprint(w, `{"id":1, "properties": {}}`) }) opt := &SetSlackApplicationOptions{Channel: Ptr("#channel1"), NoteEvents: Ptr(true), AlertEvents: Ptr(true)} _, _, err := client.Services.SetSlackApplication(1, opt) if err != nil { t.Fatalf("Services.SetSlackApplication returns an error: %v", err) } } func TestDisableSlackApplication(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/integrations/gitlab-slack-application", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodDelete) }) _, err := client.Services.DisableSlackApplication(1) if err != nil { t.Fatalf("Services.DisableSlackApplication returns an error: %v", err) } } func TestGetJiraService(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/0/integrations/jira", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, `{"id":1, "properties": {}}`) }) mux.HandleFunc("/api/v4/projects/1/integrations/jira", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, `{"id":1, "properties": {"jira_issue_transition_id": "2"}}`) }) mux.HandleFunc("/api/v4/projects/2/integrations/jira", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, `{"id":1, "properties": {"jira_issue_transition_id": 2}}`) }) mux.HandleFunc("/api/v4/projects/3/integrations/jira", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, `{"id":1, "properties": {"jira_issue_transition_id": "2,3"}}`) }) mux.HandleFunc("/api/v4/projects/4/integrations/jira", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, `{"id":1, "properties": {"jira_auth_type": 1}}`) }) want := []*JiraService{ { Service: Service{ID: 1}, Properties: &JiraServiceProperties{}, }, { Service: Service{ID: 1}, Properties: &JiraServiceProperties{ JiraIssueTransitionID: "2", }, }, { Service: Service{ID: 1}, Properties: &JiraServiceProperties{ JiraIssueTransitionID: "2", }, }, { Service: Service{ID: 1}, Properties: &JiraServiceProperties{ JiraIssueTransitionID: "2,3", }, }, { Service: Service{ID: 1}, Properties: &JiraServiceProperties{ JiraAuthType: 1, }, }, } for testcase := 0; testcase < len(want); testcase++ { service, _, err := client.Services.GetJiraService(testcase) if err != nil { t.Fatalf("Services.GetJiraService returns an error: %v", err) } if !reflect.DeepEqual(want[testcase], service) { t.Errorf("Services.GetJiraService returned %+v, want %+v", service, want[testcase]) } } } func TestSetJiraService(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/integrations/jira", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPut) fmt.Fprint(w, `{"id":1, "properties": {}}`) }) opt := &SetJiraServiceOptions{ URL: Ptr("asd"), APIURL: Ptr("asd"), ProjectKey: Ptr("as"), Username: Ptr("aas"), Password: Ptr("asd"), Active: Ptr(true), JiraIssuePrefix: Ptr("ASD"), JiraIssueRegex: Ptr("ASD"), JiraIssueTransitionAutomatic: Ptr(true), JiraIssueTransitionID: Ptr("2,3"), CommitEvents: Ptr(true), MergeRequestsEvents: Ptr(true), CommentOnEventEnabled: Ptr(true), IssuesEnabled: Ptr(true), UseInheritedSettings: Ptr(true), } _, _, err := client.Services.SetJiraService(1, opt) if err != nil { t.Fatalf("Services.SetJiraService returns an error: %v", err) } } func TestSetJiraServiceProjecKeys(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/integrations/jira", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPut) fmt.Fprint(w, `{"id":1, "properties": {}}`) }) opt := &SetJiraServiceOptions{ URL: Ptr("asd"), APIURL: Ptr("asd"), Username: Ptr("aas"), Password: Ptr("asd"), Active: Ptr(true), JiraIssuePrefix: Ptr("ASD"), JiraIssueRegex: Ptr("ASD"), JiraIssueTransitionAutomatic: Ptr(true), JiraIssueTransitionID: Ptr("2,3"), CommitEvents: Ptr(true), MergeRequestsEvents: Ptr(true), CommentOnEventEnabled: Ptr(true), IssuesEnabled: Ptr(true), ProjectKeys: Ptr([]string{"as"}), UseInheritedSettings: Ptr(true), } _, _, err := client.Services.SetJiraService(1, opt) if err != nil { t.Fatalf("Services.SetJiraService returns an error: %v", err) } } func TestSetJiraServiceAuthTypeBasicAuth(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/integrations/jira", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPut) fmt.Fprint(w, `{"id":1, "properties": {}}`) }) opt := &SetJiraServiceOptions{ URL: Ptr("asd"), Username: Ptr("aas"), Password: Ptr("asd"), JiraAuthType: Ptr(0), } _, _, err := client.Services.SetJiraService(1, opt) if err != nil { t.Fatalf("Services.SetJiraService returns an error: %v", err) } } func TestSetJiraServiceAuthTypeTokenAuth(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/integrations/jira", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPut) fmt.Fprint(w, `{"id":1, "properties": {}}`) }) opt := &SetJiraServiceOptions{ URL: Ptr("asd"), Password: Ptr("asd"), JiraAuthType: Ptr(1), } _, _, err := client.Services.SetJiraService(1, opt) if err != nil { t.Fatalf("Services.SetJiraService returns an error: %v", err) } } func TestDeleteJiraService(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/integrations/jira", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodDelete) }) _, err := client.Services.DeleteJiraService(1) if err != nil { t.Fatalf("Services.DeleteJiraService returns an error: %v", err) } } func TestGetMattermostService(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/services/mattermost", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, `{"id":1}`) }) want := &MattermostService{Service: Service{ID: 1}} service, _, err := client.Services.GetMattermostService(1) if err != nil { t.Fatalf("Services.GetMattermostService returns an error: %v", err) } if !reflect.DeepEqual(want, service) { t.Errorf("Services.GetMattermostService returned %+v, want %+v", service, want) } } func TestSetMattermostService(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/services/mattermost", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPut) fmt.Fprint(w, `{"id":1, "properties": {}}`) }) opt := &SetMattermostServiceOptions{ WebHook: Ptr("webhook_uri"), Username: Ptr("username"), Channel: Ptr("#development"), } _, _, err := client.Services.SetMattermostService(1, opt) if err != nil { t.Fatalf("Services.SetMasttermostService returns an error: %v", err) } } func TestDeleteMattermostService(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/services/mattermost", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodDelete) }) _, err := client.Services.DeleteMattermostService(1) if err != nil { t.Fatalf("Services.DeleteMattermostService returns an error: %v", err) } } func TestGetMattermostSlashCommandsService(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/services/mattermost-slash-commands", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, `{"id":1}`) }) want := &MattermostSlashCommandsService{Service: Service{ID: 1}} service, _, err := client.Services.GetMattermostSlashCommandsService(1) if err != nil { t.Fatalf("Services.mattermost-slash-commands returns an error: %v", err) } if !reflect.DeepEqual(want, service) { t.Errorf("Services.mattermost-slash-commands returned %+v, want %+v", service, want) } } func TestSetMattermostSlashCommandsService(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/services/mattermost-slash-commands", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPut) fmt.Fprint(w, `{"id":1, "properties": {}}`) }) opt := &SetMattermostSlashCommandsServiceOptions{ Token: Ptr("token"), Username: Ptr("username"), } _, _, err := client.Services.SetMattermostSlashCommandsService(1, opt) if err != nil { t.Fatalf("Services.SetMattermostSlashCommandsService returns an error: %v", err) } } func TestDeleteMattermostSlashCommandsService(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/services/mattermost-slash-commands", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodDelete) }) _, err := client.Services.DeleteMattermostSlashCommandsService(1) if err != nil { t.Fatalf("Services.DeleteMattermostSlashCommandsService returns an error: %v", err) } } func TestGetPipelinesEmailService(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/services/pipelines-email", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, `{"id":1}`) }) want := &PipelinesEmailService{Service: Service{ID: 1}} service, _, err := client.Services.GetPipelinesEmailService(1) if err != nil { t.Fatalf("Services.GetPipelinesEmailService returns an error: %v", err) } if !reflect.DeepEqual(want, service) { t.Errorf("Services.GetPipelinesEmailService returned %+v, want %+v", service, want) } } func TestSetPipelinesEmailService(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/services/pipelines-email", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPut) fmt.Fprint(w, `{"id":1, "properties": {}}`) }) opt := &SetPipelinesEmailServiceOptions{ Recipients: Ptr("test@email.com"), NotifyOnlyBrokenPipelines: Ptr(true), NotifyOnlyDefaultBranch: Ptr(false), AddPusher: nil, BranchesToBeNotified: nil, PipelineEvents: nil, } _, _, err := client.Services.SetPipelinesEmailService(1, opt) if err != nil { t.Fatalf("Services.SetPipelinesEmailService returns an error: %v", err) } } func TestDeletePipelinesEmailService(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/services/pipelines-email", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodDelete) }) _, err := client.Services.DeletePipelinesEmailService(1) if err != nil { t.Fatalf("Services.DeletePipelinesEmailService returns an error: %v", err) } } func TestGetPrometheusService(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/services/prometheus", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, `{"id":1}`) }) want := &PrometheusService{Service: Service{ID: 1}} service, _, err := client.Services.GetPrometheusService(1) if err != nil { t.Fatalf("Services.GetPrometheusService returns an error: %v", err) } if !reflect.DeepEqual(want, service) { t.Errorf("Services.GetPrometheusService returned %+v, want %+v", service, want) } } func TestSetPrometheusService(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/services/prometheus", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPut) fmt.Fprint(w, `{"id":1, "properties": {}}`) }) opt := &SetPrometheusServiceOptions{Ptr("t"), Ptr("u"), Ptr("a")} _, _, err := client.Services.SetPrometheusService(1, opt) if err != nil { t.Fatalf("Services.SetDroneCIService returns an error: %v", err) } } func TestDeletePrometheusService(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/services/prometheus", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodDelete) }) _, err := client.Services.DeletePrometheusService(1) if err != nil { t.Fatalf("Services.DeletePrometheusService returns an error: %v", err) } } func TestGetRedmineService(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/integrations/redmine", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, `{"id":1}`) }) want := &RedmineService{Service: Service{ID: 1}} service, _, err := client.Services.GetRedmineService(1) if err != nil { t.Fatalf("Services.GetRedmineService returns an error: %v", err) } if !reflect.DeepEqual(want, service) { t.Errorf("Services.GetRedmineService returned %+v, want %+v", service, want) } } func TestSetRedmineService(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/integrations/redmine", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPut) fmt.Fprint(w, `{"id":1, "properties": {}}`) }) opt := &SetRedmineServiceOptions{Ptr("t"), Ptr("u"), Ptr("a"), Ptr(false)} _, _, err := client.Services.SetRedmineService(1, opt) if err != nil { t.Fatalf("Services.SetRedmineService returns an error: %v", err) } } func TestDeleteRedmineService(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/integrations/redmine", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodDelete) }) _, err := client.Services.DeleteRedmineService(1) if err != nil { t.Fatalf("Services.DeleteRedmineService returns an error: %v", err) } } func TestGetSlackService(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/services/slack", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, `{"id":1}`) }) want := &SlackService{Service: Service{ID: 1}} service, _, err := client.Services.GetSlackService(1) if err != nil { t.Fatalf("Services.GetSlackService returns an error: %v", err) } if !reflect.DeepEqual(want, service) { t.Errorf("Services.GetSlackService returned %+v, want %+v", service, want) } } func TestSetSlackService(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/services/slack", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPut) fmt.Fprint(w, `{"id":1, "properties": {}}`) }) opt := &SetSlackServiceOptions{ WebHook: Ptr("webhook_uri"), Username: Ptr("username"), Channel: Ptr("#development"), } _, _, err := client.Services.SetSlackService(1, opt) if err != nil { t.Fatalf("Services.SetSlackService returns an error: %v", err) } } func TestDeleteSlackService(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/services/slack", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodDelete) }) _, err := client.Services.DeleteSlackService(1) if err != nil { t.Fatalf("Services.DeleteSlackService returns an error: %v", err) } } func TestGetSlackSlashCommandsService(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/services/slack-slash-commands", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, `{"id":1}`) }) want := &SlackSlashCommandsService{Service: Service{ID: 1}} service, _, err := client.Services.GetSlackSlashCommandsService(1) if err != nil { t.Fatalf("Services.GetSlackSlashCommandsService returns an error: %v", err) } if !reflect.DeepEqual(want, service) { t.Errorf("Services.GetSlackSlashCommandsService returned %+v, want %+v", service, want) } } func TestSetSlackSlashCommandsService(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/services/slack-slash-commands", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPut) fmt.Fprint(w, `{"id":1, "properties": {}}`) }) opt := &SetSlackSlashCommandsServiceOptions{ Token: Ptr("token"), } _, _, err := client.Services.SetSlackSlashCommandsService(1, opt) if err != nil { t.Fatalf("Services.SetSlackSlashCommandsService returns an error: %v", err) } } func TestDeleteSlackSlashCommandsService(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/services/slack-slash-commands", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodDelete) }) _, err := client.Services.DeleteSlackSlashCommandsService(1) if err != nil { t.Fatalf("Services.DeleteSlackSlashCommandsService returns an error: %v", err) } } func TestGetTelegramService(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/services/telegram", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, ` { "id": 1, "title": "Telegram", "slug": "telegram", "created_at": "2023-12-16T20:21:03.117Z", "updated_at": "2023-12-16T20:22:19.140Z", "active": true, "commit_events": true, "push_events": false, "issues_events": false, "incident_events": false, "alert_events": true, "confidential_issues_events": false, "merge_requests_events": false, "tag_push_events": false, "deployment_events": false, "note_events": false, "confidential_note_events": false, "pipeline_events": true, "wiki_page_events": false, "job_events": true, "comment_on_event_enabled": true, "vulnerability_events": false, "properties": { "room": "-1000000000000", "notify_only_broken_pipelines": false, "branches_to_be_notified": "all" } } `) }) wantCreatedAt, _ := time.Parse(time.RFC3339, "2023-12-16T20:21:03.117Z") wantUpdatedAt, _ := time.Parse(time.RFC3339, "2023-12-16T20:22:19.140Z") want := &TelegramService{ Service: Service{ ID: 1, Title: "Telegram", Slug: "telegram", CreatedAt: &wantCreatedAt, UpdatedAt: &wantUpdatedAt, Active: true, CommitEvents: true, PushEvents: false, IssuesEvents: false, AlertEvents: true, ConfidentialIssuesEvents: false, MergeRequestsEvents: false, TagPushEvents: false, DeploymentEvents: false, NoteEvents: false, ConfidentialNoteEvents: false, PipelineEvents: true, WikiPageEvents: false, JobEvents: true, CommentOnEventEnabled: true, VulnerabilityEvents: false, }, Properties: &TelegramServiceProperties{ Room: "-1000000000000", NotifyOnlyBrokenPipelines: false, BranchesToBeNotified: "all", }, } service, _, err := client.Services.GetTelegramService(1) if err != nil { t.Fatalf("Services.GetTelegramService returns an error: %v", err) } if !reflect.DeepEqual(want, service) { t.Errorf("Services.GetTelegramService returned %+v, want %+v", service, want) } } func TestSetTelegramService(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/services/telegram", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPut) fmt.Fprint(w, `{"id":1, "properties": {}}`) }) opt := &SetTelegramServiceOptions{ Token: Ptr("token"), Room: Ptr("-1000"), NotifyOnlyBrokenPipelines: Ptr(true), BranchesToBeNotified: Ptr("all"), PushEvents: Ptr(true), IssuesEvents: Ptr(true), ConfidentialIssuesEvents: Ptr(true), MergeRequestsEvents: Ptr(true), TagPushEvents: Ptr(true), NoteEvents: Ptr(true), ConfidentialNoteEvents: Ptr(true), PipelineEvents: Ptr(true), WikiPageEvents: Ptr(true), } _, _, err := client.Services.SetTelegramService(1, opt) if err != nil { t.Fatalf("Services.SetTelegramService returns an error: %v", err) } } func TestDeleteTelegramService(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/services/telegram", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodDelete) }) _, err := client.Services.DeleteTelegramService(1) if err != nil { t.Fatalf("Services.DeleteTelegramService returns an error: %v", err) } } func TestGetYouTrackService(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/services/youtrack", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, `{"id":1}`) }) want := &YouTrackService{Service: Service{ID: 1}} service, _, err := client.Services.GetYouTrackService(1) if err != nil { t.Fatalf("Services.GetYouTrackService returns an error: %v", err) } if !reflect.DeepEqual(want, service) { t.Errorf("Services.GetYouTrackService returned %+v, want %+v", service, want) } } func TestSetYouTrackService(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/services/youtrack", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPut) fmt.Fprint(w, `{"id":1, "properties": {}}`) }) opt := &SetYouTrackServiceOptions{ IssuesURL: Ptr("https://example.org/youtrack/issue/:id"), ProjectURL: Ptr("https://example.org/youtrack/projects/1"), Description: Ptr("description"), PushEvents: Ptr(true), } _, _, err := client.Services.SetYouTrackService(1, opt) if err != nil { t.Fatalf("Services.SetYouTrackService returns an error: %v", err) } } func TestDeleteYouTrackService(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/services/youtrack", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodDelete) }) _, err := client.Services.DeleteYouTrackService(1) if err != nil { t.Fatalf("Services.DeleteYouTrackService returns an error: %v", err) } } golang-gitlab-gitlab-org-api-client-go-0.123.0/settings.go000066400000000000000000003775641475761473200232740ustar00rootroot00000000000000// // Copyright 2021, Sander van Harmelen // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "encoding/json" "net/http" "time" ) // SettingsService handles communication with the application SettingsService // related methods of the GitLab API. // // GitLab API docs: https://docs.gitlab.com/ee/api/settings.html type SettingsService struct { client *Client } // Settings represents the GitLab application settings. // // GitLab API docs: https://docs.gitlab.com/ee/api/settings.html // // The available parameters have been modeled directly after the code, as the // documentation seems to be inaccurate. // // https://gitlab.com/gitlab-org/gitlab/-/blob/v14.9.3-ee/lib/api/settings.rb // https://gitlab.com/gitlab-org/gitlab/-/blob/v14.9.3-ee/lib/api/entities/application_setting.rb#L5 // https://gitlab.com/gitlab-org/gitlab/-/blob/v14.9.3-ee/app/helpers/application_settings_helper.rb#L192 // https://gitlab.com/gitlab-org/gitlab/-/blob/v14.9.3-ee/ee/lib/ee/api/helpers/settings_helpers.rb#L10 // https://gitlab.com/gitlab-org/gitlab/-/blob/v14.9.3-ee/ee/app/helpers/ee/application_settings_helper.rb#L20 type Settings struct { ID int `json:"id"` AbuseNotificationEmail string `json:"abuse_notification_email"` AdminMode bool `json:"admin_mode"` AfterSignOutPath string `json:"after_sign_out_path"` AfterSignUpText string `json:"after_sign_up_text"` AkismetAPIKey string `json:"akismet_api_key"` AkismetEnabled bool `json:"akismet_enabled"` AllowAccountDeletion bool `json:"allow_account_deletion"` AllowGroupOwnersToManageLDAP bool `json:"allow_group_owners_to_manage_ldap"` AllowLocalRequestsFromSystemHooks bool `json:"allow_local_requests_from_system_hooks"` AllowLocalRequestsFromWebHooksAndServices bool `json:"allow_local_requests_from_web_hooks_and_services"` AllowProjectCreationForGuestAndBelow bool `json:"allow_project_creation_for_guest_and_below"` AllowRunnerRegistrationToken bool `json:"allow_runner_registration_token"` ArchiveBuildsInHumanReadable string `json:"archive_builds_in_human_readable"` ASCIIDocMaxIncludes int `json:"asciidoc_max_includes"` AssetProxyAllowlist []string `json:"asset_proxy_allowlist"` AssetProxyEnabled bool `json:"asset_proxy_enabled"` AssetProxyURL string `json:"asset_proxy_url"` AssetProxySecretKey string `json:"asset_proxy_secret_key"` AuthorizedKeysEnabled bool `json:"authorized_keys_enabled"` AutoBanUserOnExcessiveProjectsDownload bool `json:"auto_ban_user_on_excessive_projects_download"` AutoDevOpsDomain string `json:"auto_devops_domain"` AutoDevOpsEnabled bool `json:"auto_devops_enabled"` AutomaticPurchasedStorageAllocation bool `json:"automatic_purchased_storage_allocation"` BulkImportConcurrentPipelineBatchLimit int `json:"bulk_import_concurrent_pipeline_batch_limit"` BulkImportEnabled bool `json:"bulk_import_enabled"` BulkImportMaxDownloadFileSize int `json:"bulk_import_max_download_file_size"` CanCreateGroup bool `json:"can_create_group"` CheckNamespacePlan bool `json:"check_namespace_plan"` CIMaxIncludes int `json:"ci_max_includes"` CIMaxTotalYAMLSizeBytes int `json:"ci_max_total_yaml_size_bytes"` CommitEmailHostname string `json:"commit_email_hostname"` ConcurrentBitbucketImportJobsLimit int `json:"concurrent_bitbucket_import_jobs_limit"` ConcurrentBitbucketServerImportJobsLimit int `json:"concurrent_bitbucket_server_import_jobs_limit"` ConcurrentGitHubImportJobsLimit int `json:"concurrent_github_import_jobs_limit"` ContainerExpirationPoliciesEnableHistoricEntries bool `json:"container_expiration_policies_enable_historic_entries"` ContainerRegistryCleanupTagsServiceMaxListSize int `json:"container_registry_cleanup_tags_service_max_list_size"` ContainerRegistryDeleteTagsServiceTimeout int `json:"container_registry_delete_tags_service_timeout"` ContainerRegistryExpirationPoliciesCaching bool `json:"container_registry_expiration_policies_caching"` ContainerRegistryExpirationPoliciesWorkerCapacity int `json:"container_registry_expiration_policies_worker_capacity"` ContainerRegistryImportCreatedBefore *time.Time `json:"container_registry_import_created_before"` ContainerRegistryImportMaxRetries int `json:"container_registry_import_max_retries"` ContainerRegistryImportMaxStepDuration int `json:"container_registry_import_max_step_duration"` ContainerRegistryImportMaxTagsCount int `json:"container_registry_import_max_tags_count"` ContainerRegistryImportStartMaxRetries int `json:"container_registry_import_start_max_retries"` ContainerRegistryImportTargetPlan string `json:"container_registry_import_target_plan"` ContainerRegistryTokenExpireDelay int `json:"container_registry_token_expire_delay"` CreatedAt *time.Time `json:"created_at"` CustomHTTPCloneURLRoot string `json:"custom_http_clone_url_root"` DNSRebindingProtectionEnabled bool `json:"dns_rebinding_protection_enabled"` DSAKeyRestriction int `json:"dsa_key_restriction"` DeactivateDormantUsers bool `json:"deactivate_dormant_users"` DeactivateDormantUsersPeriod int `json:"deactivate_dormant_users_period"` DecompressArchiveFileTimeout int `json:"decompress_archive_file_timeout"` DefaultArtifactsExpireIn string `json:"default_artifacts_expire_in"` DefaultBranchName string `json:"default_branch_name"` DefaultBranchProtection int `json:"default_branch_protection"` DefaultBranchProtectionDefaults *BranchProtectionDefaults `json:"default_branch_protection_defaults,omitempty"` DefaultCiConfigPath string `json:"default_ci_config_path"` DefaultGroupVisibility VisibilityValue `json:"default_group_visibility"` DefaultPreferredLanguage string `json:"default_preferred_language"` DefaultProjectCreation int `json:"default_project_creation"` DefaultProjectDeletionProtection bool `json:"default_project_deletion_protection"` DefaultProjectVisibility VisibilityValue `json:"default_project_visibility"` DefaultProjectsLimit int `json:"default_projects_limit"` DefaultSnippetVisibility VisibilityValue `json:"default_snippet_visibility"` DefaultSyntaxHighlightingTheme int `json:"default_syntax_highlighting_theme"` DelayedGroupDeletion bool `json:"delayed_group_deletion"` DelayedProjectDeletion bool `json:"delayed_project_deletion"` DeleteInactiveProjects bool `json:"delete_inactive_projects"` DeleteUnconfirmedUsers bool `json:"delete_unconfirmed_users"` DeletionAdjournedPeriod int `json:"deletion_adjourned_period"` DiagramsnetEnabled bool `json:"diagramsnet_enabled"` DiagramsnetURL string `json:"diagramsnet_url"` DiffMaxFiles int `json:"diff_max_files"` DiffMaxLines int `json:"diff_max_lines"` DiffMaxPatchBytes int `json:"diff_max_patch_bytes"` DisableAdminOAuthScopes bool `json:"disable_admin_oauth_scopes"` DisableFeedToken bool `json:"disable_feed_token"` DisableOverridingApproversPerMergeRequest bool `json:"disable_overriding_approvers_per_merge_request"` DisablePersonalAccessTokens bool `json:"disable_personal_access_tokens"` DisabledOauthSignInSources []string `json:"disabled_oauth_sign_in_sources"` DomainAllowlist []string `json:"domain_allowlist"` DomainDenylist []string `json:"domain_denylist"` DomainDenylistEnabled bool `json:"domain_denylist_enabled"` DownstreamPipelineTriggerLimitPerProjectUserSHA int `json:"downstream_pipeline_trigger_limit_per_project_user_sha"` DuoFeaturesEnabled bool `json:"duo_features_enabled"` ECDSAKeyRestriction int `json:"ecdsa_key_restriction"` ECDSASKKeyRestriction int `json:"ecdsa_sk_key_restriction"` EKSAccessKeyID string `json:"eks_access_key_id"` EKSAccountID string `json:"eks_account_id"` EKSIntegrationEnabled bool `json:"eks_integration_enabled"` EKSSecretAccessKey string `json:"eks_secret_access_key"` Ed25519KeyRestriction int `json:"ed25519_key_restriction"` Ed25519SKKeyRestriction int `json:"ed25519_sk_key_restriction"` ElasticsearchAWS bool `json:"elasticsearch_aws"` ElasticsearchAWSAccessKey string `json:"elasticsearch_aws_access_key"` ElasticsearchAWSRegion string `json:"elasticsearch_aws_region"` ElasticsearchAWSSecretAccessKey string `json:"elasticsearch_aws_secret_access_key"` ElasticsearchAnalyzersKuromojiEnabled bool `json:"elasticsearch_analyzers_kuromoji_enabled"` ElasticsearchAnalyzersKuromojiSearch bool `json:"elasticsearch_analyzers_kuromoji_search"` ElasticsearchAnalyzersSmartCNEnabled bool `json:"elasticsearch_analyzers_smartcn_enabled"` ElasticsearchAnalyzersSmartCNSearch bool `json:"elasticsearch_analyzers_smartcn_search"` ElasticsearchClientRequestTimeout int `json:"elasticsearch_client_request_timeout"` ElasticsearchIndexedFieldLengthLimit int `json:"elasticsearch_indexed_field_length_limit"` ElasticsearchIndexedFileSizeLimitKB int `json:"elasticsearch_indexed_file_size_limit_kb"` ElasticsearchIndexing bool `json:"elasticsearch_indexing"` ElasticsearchLimitIndexing bool `json:"elasticsearch_limit_indexing"` ElasticsearchMaxBulkConcurrency int `json:"elasticsearch_max_bulk_concurrency"` ElasticsearchMaxBulkSizeMB int `json:"elasticsearch_max_bulk_size_mb"` ElasticsearchMaxCodeIndexingConcurrency int `json:"elasticsearch_max_code_indexing_concurrency"` ElasticsearchNamespaceIDs []int `json:"elasticsearch_namespace_ids"` ElasticsearchPassword string `json:"elasticsearch_password"` ElasticsearchPauseIndexing bool `json:"elasticsearch_pause_indexing"` ElasticsearchProjectIDs []int `json:"elasticsearch_project_ids"` ElasticsearchReplicas int `json:"elasticsearch_replicas"` ElasticsearchRequeueWorkers bool `json:"elasticsearch_requeue_workers"` ElasticsearchSearch bool `json:"elasticsearch_search"` ElasticsearchShards int `json:"elasticsearch_shards"` ElasticsearchURL []string `json:"elasticsearch_url"` ElasticsearchUsername string `json:"elasticsearch_username"` ElasticsearchWorkerNumberOfShards int `json:"elasticsearch_worker_number_of_shards"` EmailAdditionalText string `json:"email_additional_text"` EmailAuthorInBody bool `json:"email_author_in_body"` EmailConfirmationSetting string `json:"email_confirmation_setting"` EmailRestrictions string `json:"email_restrictions"` EmailRestrictionsEnabled bool `json:"email_restrictions_enabled"` EnableArtifactExternalRedirectWarningPage bool `json:"enable_artifact_external_redirect_warning_page"` EnabledGitAccessProtocol string `json:"enabled_git_access_protocol"` EnforceNamespaceStorageLimit bool `json:"enforce_namespace_storage_limit"` EnforcePATExpiration bool `json:"enforce_pat_expiration"` EnforceSSHKeyExpiration bool `json:"enforce_ssh_key_expiration"` EnforceTerms bool `json:"enforce_terms"` ExternalAuthClientCert string `json:"external_auth_client_cert"` ExternalAuthClientKey string `json:"external_auth_client_key"` ExternalAuthClientKeyPass string `json:"external_auth_client_key_pass"` ExternalAuthorizationServiceDefaultLabel string `json:"external_authorization_service_default_label"` ExternalAuthorizationServiceEnabled bool `json:"external_authorization_service_enabled"` ExternalAuthorizationServiceTimeout float64 `json:"external_authorization_service_timeout"` ExternalAuthorizationServiceURL string `json:"external_authorization_service_url"` ExternalPipelineValidationServiceTimeout int `json:"external_pipeline_validation_service_timeout"` ExternalPipelineValidationServiceToken string `json:"external_pipeline_validation_service_token"` ExternalPipelineValidationServiceURL string `json:"external_pipeline_validation_service_url"` FailedLoginAttemptsUnlockPeriodInMinutes int `json:"failed_login_attempts_unlock_period_in_minutes"` FileTemplateProjectID int `json:"file_template_project_id"` FirstDayOfWeek int `json:"first_day_of_week"` FlocEnabled bool `json:"floc_enabled"` GeoNodeAllowedIPs string `json:"geo_node_allowed_ips"` GeoStatusTimeout int `json:"geo_status_timeout"` GitRateLimitUsersAlertlist []string `json:"git_rate_limit_users_alertlist"` GitTwoFactorSessionExpiry int `json:"git_two_factor_session_expiry"` GitalyTimeoutDefault int `json:"gitaly_timeout_default"` GitalyTimeoutFast int `json:"gitaly_timeout_fast"` GitalyTimeoutMedium int `json:"gitaly_timeout_medium"` GitlabDedicatedInstance bool `json:"gitlab_dedicated_instance"` GitlabEnvironmentToolkitInstance bool `json:"gitlab_environment_toolkit_instance"` GitlabShellOperationLimit int `json:"gitlab_shell_operation_limit"` GitpodEnabled bool `json:"gitpod_enabled"` GitpodURL string `json:"gitpod_url"` GitRateLimitUsersAllowlist []string `json:"git_rate_limit_users_allowlist"` GloballyAllowedIPs string `json:"globally_allowed_ips"` GrafanaEnabled bool `json:"grafana_enabled"` GrafanaURL string `json:"grafana_url"` GravatarEnabled bool `json:"gravatar_enabled"` GroupDownloadExportLimit int `json:"group_download_export_limit"` GroupExportLimit int `json:"group_export_limit"` GroupImportLimit int `json:"group_import_limit"` GroupOwnersCanManageDefaultBranchProtection bool `json:"group_owners_can_manage_default_branch_protection"` GroupRunnerTokenExpirationInterval int `json:"group_runner_token_expiration_interval"` HTMLEmailsEnabled bool `json:"html_emails_enabled"` HashedStorageEnabled bool `json:"hashed_storage_enabled"` HelpPageDocumentationBaseURL string `json:"help_page_documentation_base_url"` HelpPageHideCommercialContent bool `json:"help_page_hide_commercial_content"` HelpPageSupportURL string `json:"help_page_support_url"` HelpPageText string `json:"help_page_text"` HelpText string `json:"help_text"` HideThirdPartyOffers bool `json:"hide_third_party_offers"` HomePageURL string `json:"home_page_url"` HousekeepingBitmapsEnabled bool `json:"housekeeping_bitmaps_enabled"` HousekeepingEnabled bool `json:"housekeeping_enabled"` HousekeepingFullRepackPeriod int `json:"housekeeping_full_repack_period"` HousekeepingGcPeriod int `json:"housekeeping_gc_period"` HousekeepingIncrementalRepackPeriod int `json:"housekeeping_incremental_repack_period"` HousekeepingOptimizeRepositoryPeriod int `json:"housekeeping_optimize_repository_period"` ImportSources []string `json:"import_sources"` InactiveProjectsDeleteAfterMonths int `json:"inactive_projects_delete_after_months"` InactiveProjectsMinSizeMB int `json:"inactive_projects_min_size_mb"` InactiveProjectsSendWarningEmailAfterMonths int `json:"inactive_projects_send_warning_email_after_months"` IncludeOptionalMetricsInServicePing bool `json:"include_optional_metrics_in_service_ping"` InProductMarketingEmailsEnabled bool `json:"in_product_marketing_emails_enabled"` InvisibleCaptchaEnabled bool `json:"invisible_captcha_enabled"` IssuesCreateLimit int `json:"issues_create_limit"` JiraConnectApplicationKey string `json:"jira_connect_application_key"` JiraConnectPublicKeyStorageEnabled bool `json:"jira_connect_public_key_storage_enabled"` JiraConnectProxyURL string `json:"jira_connect_proxy_url"` KeepLatestArtifact bool `json:"keep_latest_artifact"` KrokiEnabled bool `json:"kroki_enabled"` KrokiFormats map[string]bool `json:"kroki_formats"` KrokiURL string `json:"kroki_url"` LocalMarkdownVersion int `json:"local_markdown_version"` LockDuoFeaturesEnabled bool `json:"lock_duo_features_enabled"` LockMembershipsToLDAP bool `json:"lock_memberships_to_ldap"` LoginRecaptchaProtectionEnabled bool `json:"login_recaptcha_protection_enabled"` MailgunEventsEnabled bool `json:"mailgun_events_enabled"` MailgunSigningKey string `json:"mailgun_signing_key"` MaintenanceMode bool `json:"maintenance_mode"` MaintenanceModeMessage string `json:"maintenance_mode_message"` MavenPackageRequestsForwarding bool `json:"maven_package_requests_forwarding"` MaxArtifactsSize int `json:"max_artifacts_size"` MaxAttachmentSize int `json:"max_attachment_size"` MaxDecompressedArchiveSize int `json:"max_decompressed_archive_size"` MaxExportSize int `json:"max_export_size"` MaxImportRemoteFileSize int `json:"max_import_remote_file_size"` MaxImportSize int `json:"max_import_size"` MaxLoginAttempts int `json:"max_login_attempts"` MaxNumberOfRepositoryDownloads int `json:"max_number_of_repository_downloads"` MaxNumberOfRepositoryDownloadsWithinTimePeriod int `json:"max_number_of_repository_downloads_within_time_period"` MaxPagesSize int `json:"max_pages_size"` MaxPersonalAccessTokenLifetime int `json:"max_personal_access_token_lifetime"` MaxSSHKeyLifetime int `json:"max_ssh_key_lifetime"` MaxTerraformStateSizeBytes int `json:"max_terraform_state_size_bytes"` MaxYAMLDepth int `json:"max_yaml_depth"` MaxYAMLSizeBytes int `json:"max_yaml_size_bytes"` MetricsMethodCallThreshold int `json:"metrics_method_call_threshold"` MinimumPasswordLength int `json:"minimum_password_length"` MirrorAvailable bool `json:"mirror_available"` MirrorCapacityThreshold int `json:"mirror_capacity_threshold"` MirrorMaxCapacity int `json:"mirror_max_capacity"` MirrorMaxDelay int `json:"mirror_max_delay"` NPMPackageRequestsForwarding bool `json:"npm_package_requests_forwarding"` NotesCreateLimit int `json:"notes_create_limit"` NotifyOnUnknownSignIn bool `json:"notify_on_unknown_sign_in"` NugetSkipMetadataURLValidation bool `json:"nuget_skip_metadata_url_validation"` OutboundLocalRequestsAllowlistRaw string `json:"outbound_local_requests_allowlist_raw"` OutboundLocalRequestsWhitelist []string `json:"outbound_local_requests_whitelist"` PackageMetadataPURLTypes []int `json:"package_metadata_purl_types"` PackageRegistryAllowAnyoneToPullOption bool `json:"package_registry_allow_anyone_to_pull_option"` PackageRegistryCleanupPoliciesWorkerCapacity int `json:"package_registry_cleanup_policies_worker_capacity"` PagesDomainVerificationEnabled bool `json:"pages_domain_verification_enabled"` PasswordAuthenticationEnabledForGit bool `json:"password_authentication_enabled_for_git"` PasswordAuthenticationEnabledForWeb bool `json:"password_authentication_enabled_for_web"` PasswordNumberRequired bool `json:"password_number_required"` PasswordSymbolRequired bool `json:"password_symbol_required"` PasswordUppercaseRequired bool `json:"password_uppercase_required"` PasswordLowercaseRequired bool `json:"password_lowercase_required"` PerformanceBarAllowedGroupID int `json:"performance_bar_allowed_group_id"` PerformanceBarAllowedGroupPath string `json:"performance_bar_allowed_group_path"` PerformanceBarEnabled bool `json:"performance_bar_enabled"` PersonalAccessTokenPrefix string `json:"personal_access_token_prefix"` PipelineLimitPerProjectUserSha int `json:"pipeline_limit_per_project_user_sha"` PlantumlEnabled bool `json:"plantuml_enabled"` PlantumlURL string `json:"plantuml_url"` PollingIntervalMultiplier float64 `json:"polling_interval_multiplier,string"` PreventMergeRequestsAuthorApproval bool `json:"prevent_merge_request_author_approval"` PreventMergeRequestsCommittersApproval bool `json:"prevent_merge_request_committers_approval"` ProjectDownloadExportLimit int `json:"project_download_export_limit"` ProjectExportEnabled bool `json:"project_export_enabled"` ProjectExportLimit int `json:"project_export_limit"` ProjectImportLimit int `json:"project_import_limit"` ProjectJobsAPIRateLimit int `json:"project_jobs_api_rate_limit"` ProjectRunnerTokenExpirationInterval int `json:"project_runner_token_expiration_interval"` ProjectsAPIRateLimitUnauthenticated int `json:"projects_api_rate_limit_unauthenticated"` PrometheusMetricsEnabled bool `json:"prometheus_metrics_enabled"` ProtectedCIVariables bool `json:"protected_ci_variables"` PseudonymizerEnabled bool `json:"pseudonymizer_enabled"` PushEventActivitiesLimit int `json:"push_event_activities_limit"` PushEventHooksLimit int `json:"push_event_hooks_limit"` PyPIPackageRequestsForwarding bool `json:"pypi_package_requests_forwarding"` RSAKeyRestriction int `json:"rsa_key_restriction"` RateLimitingResponseText string `json:"rate_limiting_response_text"` RawBlobRequestLimit int `json:"raw_blob_request_limit"` RecaptchaEnabled bool `json:"recaptcha_enabled"` RecaptchaPrivateKey string `json:"recaptcha_private_key"` RecaptchaSiteKey string `json:"recaptcha_site_key"` ReceiveMaxInputSize int `json:"receive_max_input_size"` ReceptiveClusterAgentsEnabled bool `json:"receptive_cluster_agents_enabled"` RememberMeEnabled bool `json:"remember_me_enabled"` RepositoryChecksEnabled bool `json:"repository_checks_enabled"` RepositorySizeLimit int `json:"repository_size_limit"` RepositoryStorages []string `json:"repository_storages"` RepositoryStoragesWeighted map[string]int `json:"repository_storages_weighted"` RequireAdminApprovalAfterUserSignup bool `json:"require_admin_approval_after_user_signup"` RequireAdminTwoFactorAuthentication bool `json:"require_admin_two_factor_authentication"` RequirePersonalAccessTokenExpiry bool `json:"require_personal_access_token_expiry"` RequireTwoFactorAuthentication bool `json:"require_two_factor_authentication"` RestrictedVisibilityLevels []VisibilityValue `json:"restricted_visibility_levels"` RunnerTokenExpirationInterval int `json:"runner_token_expiration_interval"` SearchRateLimit int `json:"search_rate_limit"` SearchRateLimitUnauthenticated int `json:"search_rate_limit_unauthenticated"` SecretDetectionRevocationTokenTypesURL string `json:"secret_detection_revocation_token_types_url"` SecretDetectionTokenRevocationEnabled bool `json:"secret_detection_token_revocation_enabled"` SecretDetectionTokenRevocationToken string `json:"secret_detection_token_revocation_token"` SecretDetectionTokenRevocationURL string `json:"secret_detection_token_revocation_url"` SecurityApprovalPoliciesLimit int `json:"security_approval_policies_limit"` SecurityPolicyGlobalGroupApproversEnabled bool `json:"security_policy_global_group_approvers_enabled"` SecurityTXTContent string `json:"security_txt_content"` SendUserConfirmationEmail bool `json:"send_user_confirmation_email"` SentryClientsideDSN string `json:"sentry_clientside_dsn"` SentryDSN string `json:"sentry_dsn"` SentryEnabled bool `json:"sentry_enabled"` SentryEnvironment string `json:"sentry_environment"` ServiceAccessTokensExpirationEnforced bool `json:"service_access_tokens_expiration_enforced"` SessionExpireDelay int `json:"session_expire_delay"` SharedRunnersEnabled bool `json:"shared_runners_enabled"` SharedRunnersMinutes int `json:"shared_runners_minutes"` SharedRunnersText string `json:"shared_runners_text"` SidekiqJobLimiterCompressionThresholdBytes int `json:"sidekiq_job_limiter_compression_threshold_bytes"` SidekiqJobLimiterLimitBytes int `json:"sidekiq_job_limiter_limit_bytes"` SidekiqJobLimiterMode string `json:"sidekiq_job_limiter_mode"` SignInText string `json:"sign_in_text"` SignupEnabled bool `json:"signup_enabled"` SilentAdminExportsEnabled bool `json:"silent_admin_exports_enabled"` SilentModeEnabled bool `json:"silent_mode_enabled"` SlackAppEnabled bool `json:"slack_app_enabled"` SlackAppID string `json:"slack_app_id"` SlackAppSecret string `json:"slack_app_secret"` SlackAppSigningSecret string `json:"slack_app_signing_secret"` SlackAppVerificationToken string `json:"slack_app_verification_token"` SnippetSizeLimit int `json:"snippet_size_limit"` SnowplowAppID string `json:"snowplow_app_id"` SnowplowCollectorHostname string `json:"snowplow_collector_hostname"` SnowplowCookieDomain string `json:"snowplow_cookie_domain"` SnowplowDatabaseCollectorHostname string `json:"snowplow_database_collector_hostname"` SnowplowEnabled bool `json:"snowplow_enabled"` SourcegraphEnabled bool `json:"sourcegraph_enabled"` SourcegraphPublicOnly bool `json:"sourcegraph_public_only"` SourcegraphURL string `json:"sourcegraph_url"` SpamCheckAPIKey string `json:"spam_check_api_key"` SpamCheckEndpointEnabled bool `json:"spam_check_endpoint_enabled"` SpamCheckEndpointURL string `json:"spam_check_endpoint_url"` StaticObjectsExternalStorageAuthToken string `json:"static_objects_external_storage_auth_token"` StaticObjectsExternalStorageURL string `json:"static_objects_external_storage_url"` SuggestPipelineEnabled bool `json:"suggest_pipeline_enabled"` TerminalMaxSessionTime int `json:"terminal_max_session_time"` Terms string `json:"terms"` ThrottleAuthenticatedAPIEnabled bool `json:"throttle_authenticated_api_enabled"` ThrottleAuthenticatedAPIPeriodInSeconds int `json:"throttle_authenticated_api_period_in_seconds"` ThrottleAuthenticatedAPIRequestsPerPeriod int `json:"throttle_authenticated_api_requests_per_period"` ThrottleAuthenticatedDeprecatedAPIEnabled bool `json:"throttle_authenticated_deprecated_api_enabled"` ThrottleAuthenticatedDeprecatedAPIPeriodInSeconds int `json:"throttle_authenticated_deprecated_api_period_in_seconds"` ThrottleAuthenticatedDeprecatedAPIRequestsPerPeriod int `json:"throttle_authenticated_deprecated_api_requests_per_period"` ThrottleAuthenticatedFilesAPIEnabled bool `json:"throttle_authenticated_files_api_enabled"` ThrottleAuthenticatedFilesAPIPeriodInSeconds int `json:"throttle_authenticated_files_api_period_in_seconds"` ThrottleAuthenticatedFilesAPIRequestsPerPeriod int `json:"throttle_authenticated_files_api_requests_per_period"` ThrottleAuthenticatedGitLFSEnabled bool `json:"throttle_authenticated_git_lfs_enabled"` ThrottleAuthenticatedGitLFSPeriodInSeconds int `json:"throttle_authenticated_git_lfs_period_in_seconds"` ThrottleAuthenticatedGitLFSRequestsPerPeriod int `json:"throttle_authenticated_git_lfs_requests_per_period"` ThrottleAuthenticatedPackagesAPIEnabled bool `json:"throttle_authenticated_packages_api_enabled"` ThrottleAuthenticatedPackagesAPIPeriodInSeconds int `json:"throttle_authenticated_packages_api_period_in_seconds"` ThrottleAuthenticatedPackagesAPIRequestsPerPeriod int `json:"throttle_authenticated_packages_api_requests_per_period"` ThrottleAuthenticatedWebEnabled bool `json:"throttle_authenticated_web_enabled"` ThrottleAuthenticatedWebPeriodInSeconds int `json:"throttle_authenticated_web_period_in_seconds"` ThrottleAuthenticatedWebRequestsPerPeriod int `json:"throttle_authenticated_web_requests_per_period"` ThrottleIncidentManagementNotificationEnabled bool `json:"throttle_incident_management_notification_enabled"` ThrottleIncidentManagementNotificationPerPeriod int `json:"throttle_incident_management_notification_per_period"` ThrottleIncidentManagementNotificationPeriodInSeconds int `json:"throttle_incident_management_notification_period_in_seconds"` ThrottleProtectedPathsEnabled bool `json:"throttle_protected_paths_enabled"` ThrottleProtectedPathsPeriodInSeconds int `json:"throttle_protected_paths_period_in_seconds"` ThrottleProtectedPathsRequestsPerPeriod int `json:"throttle_protected_paths_requests_per_period"` ThrottleUnauthenticatedAPIEnabled bool `json:"throttle_unauthenticated_api_enabled"` ThrottleUnauthenticatedAPIPeriodInSeconds int `json:"throttle_unauthenticated_api_period_in_seconds"` ThrottleUnauthenticatedAPIRequestsPerPeriod int `json:"throttle_unauthenticated_api_requests_per_period"` ThrottleUnauthenticatedDeprecatedAPIEnabled bool `json:"throttle_unauthenticated_deprecated_api_enabled"` ThrottleUnauthenticatedDeprecatedAPIPeriodInSeconds int `json:"throttle_unauthenticated_deprecated_api_period_in_seconds"` ThrottleUnauthenticatedDeprecatedAPIRequestsPerPeriod int `json:"throttle_unauthenticated_deprecated_api_requests_per_period"` ThrottleUnauthenticatedFilesAPIEnabled bool `json:"throttle_unauthenticated_files_api_enabled"` ThrottleUnauthenticatedFilesAPIPeriodInSeconds int `json:"throttle_unauthenticated_files_api_period_in_seconds"` ThrottleUnauthenticatedFilesAPIRequestsPerPeriod int `json:"throttle_unauthenticated_files_api_requests_per_period"` ThrottleUnauthenticatedGitLFSEnabled bool `json:"throttle_unauthenticated_git_lfs_enabled"` ThrottleUnauthenticatedGitLFSPeriodInSeconds int `json:"throttle_unauthenticated_git_lfs_period_in_seconds"` ThrottleUnauthenticatedGitLFSRequestsPerPeriod int `json:"throttle_unauthenticated_git_lfs_requests_per_period"` ThrottleUnauthenticatedPackagesAPIEnabled bool `json:"throttle_unauthenticated_packages_api_enabled"` ThrottleUnauthenticatedPackagesAPIPeriodInSeconds int `json:"throttle_unauthenticated_packages_api_period_in_seconds"` ThrottleUnauthenticatedPackagesAPIRequestsPerPeriod int `json:"throttle_unauthenticated_packages_api_requests_per_period"` ThrottleUnauthenticatedWebEnabled bool `json:"throttle_unauthenticated_web_enabled"` ThrottleUnauthenticatedWebPeriodInSeconds int `json:"throttle_unauthenticated_web_period_in_seconds"` ThrottleUnauthenticatedWebRequestsPerPeriod int `json:"throttle_unauthenticated_web_requests_per_period"` TimeTrackingLimitToHours bool `json:"time_tracking_limit_to_hours"` TwoFactorGracePeriod int `json:"two_factor_grace_period"` UnconfirmedUsersDeleteAfterDays int `json:"unconfirmed_users_delete_after_days"` UniqueIPsLimitEnabled bool `json:"unique_ips_limit_enabled"` UniqueIPsLimitPerUser int `json:"unique_ips_limit_per_user"` UniqueIPsLimitTimeWindow int `json:"unique_ips_limit_time_window"` UpdateRunnerVersionsEnabled bool `json:"update_runner_versions_enabled"` UpdatedAt *time.Time `json:"updated_at"` UpdatingNameDisabledForUsers bool `json:"updating_name_disabled_for_users"` UsagePingEnabled bool `json:"usage_ping_enabled"` UsagePingFeaturesEnabled bool `json:"usage_ping_features_enabled"` UseClickhouseForAnalytics bool `json:"use_clickhouse_for_analytics"` UserDeactivationEmailsEnabled bool `json:"user_deactivation_emails_enabled"` UserDefaultExternal bool `json:"user_default_external"` UserDefaultInternalRegex string `json:"user_default_internal_regex"` UserDefaultsToPrivateProfile bool `json:"user_defaults_to_private_profile"` UserOauthApplications bool `json:"user_oauth_applications"` UserShowAddSSHKeyMessage bool `json:"user_show_add_ssh_key_message"` UsersGetByIDLimit int `json:"users_get_by_id_limit"` UsersGetByIDLimitAllowlistRaw string `json:"users_get_by_id_limit_allowlist_raw"` ValidRunnerRegistrars []string `json:"valid_runner_registrars"` VersionCheckEnabled bool `json:"version_check_enabled"` WebIDEClientsidePreviewEnabled bool `json:"web_ide_clientside_preview_enabled"` WhatsNewVariant string `json:"whats_new_variant"` WikiPageMaxContentBytes int `json:"wiki_page_max_content_bytes"` // Deprecated: Use AbuseNotificationEmail instead. AdminNotificationEmail string `json:"admin_notification_email"` // Deprecated: Use AllowLocalRequestsFromWebHooksAndServices instead. AllowLocalRequestsFromHooksAndServices bool `json:"allow_local_requests_from_hooks_and_services"` // Deprecated: Use AssetProxyAllowlist instead. AssetProxyWhitelist []string `json:"asset_proxy_whitelist"` // Deprecated: Use ThrottleUnauthenticatedWebEnabled or ThrottleUnauthenticatedAPIEnabled instead. (Deprecated in GitLab 14.3) ThrottleUnauthenticatedEnabled bool `json:"throttle_unauthenticated_enabled"` // Deprecated: Use ThrottleUnauthenticatedWebPeriodInSeconds or ThrottleUnauthenticatedAPIPeriodInSeconds instead. (Deprecated in GitLab 14.3) ThrottleUnauthenticatedPeriodInSeconds int `json:"throttle_unauthenticated_period_in_seconds"` // Deprecated: Use ThrottleUnauthenticatedWebRequestsPerPeriod or ThrottleUnauthenticatedAPIRequestsPerPeriod instead. (Deprecated in GitLab 14.3) ThrottleUnauthenticatedRequestsPerPeriod int `json:"throttle_unauthenticated_requests_per_period"` // Deprecated: Replaced by SearchRateLimit in GitLab 14.9 (removed in 15.0). UserEmailLookupLimit int `json:"user_email_lookup_limit"` } // Settings requires a custom unmarshaller in order to properly unmarshal // `container_registry_import_created_before` which is either a time.Time or // an empty string if no value is set. func (s *Settings) UnmarshalJSON(data []byte) error { type Alias Settings raw := make(map[string]interface{}) err := json.Unmarshal(data, &raw) if err != nil { return err } // If empty string, remove the value to leave it nil in the response. if v, ok := raw["container_registry_import_created_before"]; ok && v == "" { delete(raw, "container_registry_import_created_before") data, err = json.Marshal(raw) if err != nil { return err } } return json.Unmarshal(data, (*Alias)(s)) } func (s Settings) String() string { return Stringify(s) } // GetSettings gets the current application settings. // // GitLab API docs: // https://docs.gitlab.com/ee/api/settings.html#get-current-application-settings func (s *SettingsService) GetSettings(options ...RequestOptionFunc) (*Settings, *Response, error) { req, err := s.client.NewRequest(http.MethodGet, "application/settings", nil, options) if err != nil { return nil, nil, err } as := new(Settings) resp, err := s.client.Do(req, as) if err != nil { return nil, resp, err } return as, resp, nil } // UpdateSettingsOptions represents the available UpdateSettings() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/settings.html#change-application-settings type UpdateSettingsOptions struct { AbuseNotificationEmail *string `url:"abuse_notification_email,omitempty" json:"abuse_notification_email,omitempty"` AdminMode *bool `url:"admin_mode,omitempty" json:"admin_mode,omitempty"` AdminNotificationEmail *string `url:"admin_notification_email,omitempty" json:"admin_notification_email,omitempty"` AfterSignOutPath *string `url:"after_sign_out_path,omitempty" json:"after_sign_out_path,omitempty"` AfterSignUpText *string `url:"after_sign_up_text,omitempty" json:"after_sign_up_text,omitempty"` AkismetAPIKey *string `url:"akismet_api_key,omitempty" json:"akismet_api_key,omitempty"` AkismetEnabled *bool `url:"akismet_enabled,omitempty" json:"akismet_enabled,omitempty"` AllowAccountDeletion *bool `url:"allow_account_deletion,omitempty" json:"allow_account_deletion,omitempty"` AllowGroupOwnersToManageLDAP *bool `url:"allow_group_owners_to_manage_ldap,omitempty" json:"allow_group_owners_to_manage_ldap,omitempty"` AllowLocalRequestsFromHooksAndServices *bool `url:"allow_local_requests_from_hooks_and_services,omitempty" json:"allow_local_requests_from_hooks_and_services,omitempty"` AllowLocalRequestsFromSystemHooks *bool `url:"allow_local_requests_from_system_hooks,omitempty" json:"allow_local_requests_from_system_hooks,omitempty"` AllowLocalRequestsFromWebHooksAndServices *bool `url:"allow_local_requests_from_web_hooks_and_services,omitempty" json:"allow_local_requests_from_web_hooks_and_services,omitempty"` AllowProjectCreationForGuestAndBelow *bool `url:"allow_project_creation_for_guest_and_below,omitempty" json:"allow_project_creation_for_guest_and_below,omitempty"` AllowRunnerRegistrationToken *bool `url:"allow_runner_registration_token,omitempty" json:"allow_runner_registration_token,omitempty"` ArchiveBuildsInHumanReadable *string `url:"archive_builds_in_human_readable,omitempty" json:"archive_builds_in_human_readable,omitempty"` ASCIIDocMaxIncludes *int `url:"asciidoc_max_includes,omitempty" json:"asciidoc_max_includes,omitempty"` AssetProxyAllowlist *[]string `url:"asset_proxy_allowlist,omitempty" json:"asset_proxy_allowlist,omitempty"` AssetProxyEnabled *bool `url:"asset_proxy_enabled,omitempty" json:"asset_proxy_enabled,omitempty"` AssetProxySecretKey *string `url:"asset_proxy_secret_key,omitempty" json:"asset_proxy_secret_key,omitempty"` AssetProxyURL *string `url:"asset_proxy_url,omitempty" json:"asset_proxy_url,omitempty"` AssetProxyWhitelist *[]string `url:"asset_proxy_whitelist,omitempty" json:"asset_proxy_whitelist,omitempty"` AuthorizedKeysEnabled *bool `url:"authorized_keys_enabled,omitempty" json:"authorized_keys_enabled,omitempty"` AutoBanUserOnExcessiveProjectsDownload *bool `url:"auto_ban_user_on_excessive_projects_download,omitempty" json:"auto_ban_user_on_excessive_projects_download,omitempty"` AutoDevOpsDomain *string `url:"auto_devops_domain,omitempty" json:"auto_devops_domain,omitempty"` AutoDevOpsEnabled *bool `url:"auto_devops_enabled,omitempty" json:"auto_devops_enabled,omitempty"` AutomaticPurchasedStorageAllocation *bool `url:"automatic_purchased_storage_allocation,omitempty" json:"automatic_purchased_storage_allocation,omitempty"` BulkImportConcurrentPipelineBatchLimit *int `url:"bulk_import_concurrent_pipeline_batch_limit,omitempty" json:"bulk_import_concurrent_pipeline_batch_limit,omitempty"` BulkImportEnabled *bool `url:"bulk_import_enabled,omitempty" json:"bulk_import_enabled,omitempty"` BulkImportMaxDownloadFileSize *int `url:"bulk_import_max_download_file_size,omitempty" json:"bulk_import_max_download_file_size,omitempty"` CanCreateGroup *bool `url:"can_create_group,omitempty" json:"can_create_group,omitempty"` CheckNamespacePlan *bool `url:"check_namespace_plan,omitempty" json:"check_namespace_plan,omitempty"` CIMaxIncludes *int `url:"ci_max_includes,omitempty" json:"ci_max_includes,omitempty"` CIMaxTotalYAMLSizeBytes *int `url:"ci_max_total_yaml_size_bytes,omitempty" json:"ci_max_total_yaml_size_bytes,omitempty"` CommitEmailHostname *string `url:"commit_email_hostname,omitempty" json:"commit_email_hostname,omitempty"` ConcurrentBitbucketImportJobsLimit *int `url:"concurrent_bitbucket_import_jobs_limit,omitempty" json:"concurrent_bitbucket_import_jobs_limit,omitempty"` ConcurrentBitbucketServerImportJobsLimit *int `url:"concurrent_bitbucket_server_import_jobs_limit,omitempty" json:"concurrent_bitbucket_server_import_jobs_limit,omitempty"` ConcurrentGitHubImportJobsLimit *int `url:"concurrent_github_import_jobs_limit,omitempty" json:"concurrent_github_import_jobs_limit,omitempty"` ContainerExpirationPoliciesEnableHistoricEntries *bool `url:"container_expiration_policies_enable_historic_entries,omitempty" json:"container_expiration_policies_enable_historic_entries,omitempty"` ContainerRegistryCleanupTagsServiceMaxListSize *int `url:"container_registry_cleanup_tags_service_max_list_size,omitempty" json:"container_registry_cleanup_tags_service_max_list_size,omitempty"` ContainerRegistryDeleteTagsServiceTimeout *int `url:"container_registry_delete_tags_service_timeout,omitempty" json:"container_registry_delete_tags_service_timeout,omitempty"` ContainerRegistryExpirationPoliciesCaching *bool `url:"container_registry_expiration_policies_caching,omitempty" json:"container_registry_expiration_policies_caching,omitempty"` ContainerRegistryExpirationPoliciesWorkerCapacity *int `url:"container_registry_expiration_policies_worker_capacity,omitempty" json:"container_registry_expiration_policies_worker_capacity,omitempty"` ContainerRegistryImportCreatedBefore *time.Time `url:"container_registry_import_created_before,omitempty" json:"container_registry_import_created_before,omitempty"` ContainerRegistryImportMaxRetries *int `url:"container_registry_import_max_retries,omitempty" json:"container_registry_import_max_retries,omitempty"` ContainerRegistryImportMaxStepDuration *int `url:"container_registry_import_max_step_duration,omitempty" json:"container_registry_import_max_step_duration,omitempty"` ContainerRegistryImportMaxTagsCount *int `url:"container_registry_import_max_tags_count,omitempty" json:"container_registry_import_max_tags_count,omitempty"` ContainerRegistryImportStartMaxRetries *int `url:"container_registry_import_start_max_retries,omitempty" json:"container_registry_import_start_max_retries,omitempty"` ContainerRegistryImportTargetPlan *string `url:"container_registry_import_target_plan,omitempty" json:"container_registry_import_target_plan,omitempty"` ContainerRegistryTokenExpireDelay *int `url:"container_registry_token_expire_delay,omitempty" json:"container_registry_token_expire_delay,omitempty"` CustomHTTPCloneURLRoot *string `url:"custom_http_clone_url_root,omitempty" json:"custom_http_clone_url_root,omitempty"` DNSRebindingProtectionEnabled *bool `url:"dns_rebinding_protection_enabled,omitempty" json:"dns_rebinding_protection_enabled,omitempty"` DSAKeyRestriction *int `url:"dsa_key_restriction,omitempty" json:"dsa_key_restriction,omitempty"` DeactivateDormantUsers *bool `url:"deactivate_dormant_users,omitempty" json:"deactivate_dormant_users,omitempty"` DeactivateDormantUsersPeriod *int `url:"deactivate_dormant_users_period,omitempty" json:"deactivate_dormant_users_period,omitempty"` DecompressArchiveFileTimeout *int `url:"decompress_archive_file_timeout,omitempty" json:"decompress_archive_file_timeout,omitempty"` DefaultArtifactsExpireIn *string `url:"default_artifacts_expire_in,omitempty" json:"default_artifacts_expire_in,omitempty"` DefaultBranchName *string `url:"default_branch_name,omitempty" json:"default_branch_name,omitempty"` DefaultBranchProtection *int `url:"default_branch_protection,omitempty" json:"default_branch_protection,omitempty"` DefaultBranchProtectionDefaults *DefaultBranchProtectionDefaultsOptions `url:"default_branch_protection_defaults,omitempty" json:"default_branch_protection_defaults,omitempty"` DefaultCiConfigPath *string `url:"default_ci_config_path,omitempty" json:"default_ci_config_path,omitempty"` DefaultGroupVisibility *VisibilityValue `url:"default_group_visibility,omitempty" json:"default_group_visibility,omitempty"` DefaultPreferredLanguage *string `url:"default_preferred_language,omitempty" json:"default_preferred_language,omitempty"` DefaultProjectCreation *int `url:"default_project_creation,omitempty" json:"default_project_creation,omitempty"` DefaultProjectDeletionProtection *bool `url:"default_project_deletion_protection,omitempty" json:"default_project_deletion_protection,omitempty"` DefaultProjectVisibility *VisibilityValue `url:"default_project_visibility,omitempty" json:"default_project_visibility,omitempty"` DefaultProjectsLimit *int `url:"default_projects_limit,omitempty" json:"default_projects_limit,omitempty"` DefaultSnippetVisibility *VisibilityValue `url:"default_snippet_visibility,omitempty" json:"default_snippet_visibility,omitempty"` DefaultSyntaxHighlightingTheme *int `url:"default_syntax_highlighting_theme,omitempty" json:"default_syntax_highlighting_theme,omitempty"` DelayedGroupDeletion *bool `url:"delayed_group_deletion,omitempty" json:"delayed_group_deletion,omitempty"` DelayedProjectDeletion *bool `url:"delayed_project_deletion,omitempty" json:"delayed_project_deletion,omitempty"` DeleteInactiveProjects *bool `url:"delete_inactive_projects,omitempty" json:"delete_inactive_projects,omitempty"` DeleteUnconfirmedUsers *bool `url:"delete_unconfirmed_users,omitempty" json:"delete_unconfirmed_users,omitempty"` DeletionAdjournedPeriod *int `url:"deletion_adjourned_period,omitempty" json:"deletion_adjourned_period,omitempty"` DiagramsnetEnabled *bool `url:"diagramsnet_enabled,omitempty" json:"diagramsnet_enabled,omitempty"` DiagramsnetURL *string `url:"diagramsnet_url,omitempty" json:"diagramsnet_url,omitempty"` DiffMaxFiles *int `url:"diff_max_files,omitempty" json:"diff_max_files,omitempty"` DiffMaxLines *int `url:"diff_max_lines,omitempty" json:"diff_max_lines,omitempty"` DiffMaxPatchBytes *int `url:"diff_max_patch_bytes,omitempty" json:"diff_max_patch_bytes,omitempty"` DisableFeedToken *bool `url:"disable_feed_token,omitempty" json:"disable_feed_token,omitempty"` DisableAdminOAuthScopes *bool `url:"disable_admin_oauth_scopes,omitempty" json:"disable_admin_oauth_scopes,omitempty"` DisableOverridingApproversPerMergeRequest *bool `url:"disable_overriding_approvers_per_merge_request,omitempty" json:"disable_overriding_approvers_per_merge_request,omitempty"` DisablePersonalAccessTokens *bool `url:"disable_personal_access_tokens,omitempty" json:"disable_personal_access_tokens,omitempty"` DisabledOauthSignInSources *[]string `url:"disabled_oauth_sign_in_sources,omitempty" json:"disabled_oauth_sign_in_sources,omitempty"` DomainAllowlist *[]string `url:"domain_allowlist,omitempty" json:"domain_allowlist,omitempty"` DomainDenylist *[]string `url:"domain_denylist,omitempty" json:"domain_denylist,omitempty"` DomainDenylistEnabled *bool `url:"domain_denylist_enabled,omitempty" json:"domain_denylist_enabled,omitempty"` DownstreamPipelineTriggerLimitPerProjectUserSHA *int `url:"downstream_pipeline_trigger_limit_per_project_user_sha,omitempty" json:"downstream_pipeline_trigger_limit_per_project_user_sha,omitempty"` DuoFeaturesEnabled *bool `url:"duo_features_enabled,omitempty" json:"duo_features_enabled,omitempty"` ECDSAKeyRestriction *int `url:"ecdsa_key_restriction,omitempty" json:"ecdsa_key_restriction,omitempty"` ECDSASKKeyRestriction *int `url:"ecdsa_sk_key_restriction,omitempty" json:"ecdsa_sk_key_restriction,omitempty"` EKSAccessKeyID *string `url:"eks_access_key_id,omitempty" json:"eks_access_key_id,omitempty"` EKSAccountID *string `url:"eks_account_id,omitempty" json:"eks_account_id,omitempty"` EKSIntegrationEnabled *bool `url:"eks_integration_enabled,omitempty" json:"eks_integration_enabled,omitempty"` EKSSecretAccessKey *string `url:"eks_secret_access_key,omitempty" json:"eks_secret_access_key,omitempty"` Ed25519KeyRestriction *int `url:"ed25519_key_restriction,omitempty" json:"ed25519_key_restriction,omitempty"` Ed25519SKKeyRestriction *int `url:"ed25519_sk_key_restriction,omitempty" json:"ed25519_sk_key_restriction,omitempty"` ElasticsearchAWS *bool `url:"elasticsearch_aws,omitempty" json:"elasticsearch_aws,omitempty"` ElasticsearchAWSAccessKey *string `url:"elasticsearch_aws_access_key,omitempty" json:"elasticsearch_aws_access_key,omitempty"` ElasticsearchAWSRegion *string `url:"elasticsearch_aws_region,omitempty" json:"elasticsearch_aws_region,omitempty"` ElasticsearchAWSSecretAccessKey *string `url:"elasticsearch_aws_secret_access_key,omitempty" json:"elasticsearch_aws_secret_access_key,omitempty"` ElasticsearchAnalyzersKuromojiEnabled *bool `url:"elasticsearch_analyzers_kuromoji_enabled,omitempty" json:"elasticsearch_analyzers_kuromoji_enabled,omitempty"` ElasticsearchAnalyzersKuromojiSearch *int `url:"elasticsearch_analyzers_kuromoji_search,omitempty" json:"elasticsearch_analyzers_kuromoji_search,omitempty"` ElasticsearchAnalyzersSmartCNEnabled *bool `url:"elasticsearch_analyzers_smartcn_enabled,omitempty" json:"elasticsearch_analyzers_smartcn_enabled,omitempty"` ElasticsearchAnalyzersSmartCNSearch *int `url:"elasticsearch_analyzers_smartcn_search,omitempty" json:"elasticsearch_analyzers_smartcn_search,omitempty"` ElasticsearchClientRequestTimeout *int `url:"elasticsearch_client_request_timeout,omitempty" json:"elasticsearch_client_request_timeout,omitempty"` ElasticsearchIndexedFieldLengthLimit *int `url:"elasticsearch_indexed_field_length_limit,omitempty" json:"elasticsearch_indexed_field_length_limit,omitempty"` ElasticsearchIndexedFileSizeLimitKB *int `url:"elasticsearch_indexed_file_size_limit_kb,omitempty" json:"elasticsearch_indexed_file_size_limit_kb,omitempty"` ElasticsearchIndexing *bool `url:"elasticsearch_indexing,omitempty" json:"elasticsearch_indexing,omitempty"` ElasticsearchLimitIndexing *bool `url:"elasticsearch_limit_indexing,omitempty" json:"elasticsearch_limit_indexing,omitempty"` ElasticsearchMaxBulkConcurrency *int `url:"elasticsearch_max_bulk_concurrency,omitempty" json:"elasticsearch_max_bulk_concurrency,omitempty"` ElasticsearchMaxBulkSizeMB *int `url:"elasticsearch_max_bulk_size_mb,omitempty" json:"elasticsearch_max_bulk_size_mb,omitempty"` ElasticsearchMaxCodeIndexingConcurrency *int `url:"elasticsearch_max_code_indexing_concurrency,omitempty" json:"elasticsearch_max_code_indexing_concurrency,omitempty"` ElasticsearchNamespaceIDs *[]int `url:"elasticsearch_namespace_ids,omitempty" json:"elasticsearch_namespace_ids,omitempty"` ElasticsearchPassword *string `url:"elasticsearch_password,omitempty" json:"elasticsearch_password,omitempty"` ElasticsearchPauseIndexing *bool `url:"elasticsearch_pause_indexing,omitempty" json:"elasticsearch_pause_indexing,omitempty"` ElasticsearchProjectIDs *[]int `url:"elasticsearch_project_ids,omitempty" json:"elasticsearch_project_ids,omitempty"` ElasticsearchReplicas *int `url:"elasticsearch_replicas,omitempty" json:"elasticsearch_replicas,omitempty"` ElasticsearchRequeueWorkers *bool `url:"elasticsearch_requeue_workers,omitempty" json:"elasticsearch_requeue_workers,omitempty"` ElasticsearchSearch *bool `url:"elasticsearch_search,omitempty" json:"elasticsearch_search,omitempty"` ElasticsearchShards *int `url:"elasticsearch_shards,omitempty" json:"elasticsearch_shards,omitempty"` ElasticsearchURL *string `url:"elasticsearch_url,omitempty" json:"elasticsearch_url,omitempty"` ElasticsearchUsername *string `url:"elasticsearch_username,omitempty" json:"elasticsearch_username,omitempty"` ElasticsearchWorkerNumberOfShards *int `url:"elasticsearch_worker_number_of_shards,omitempty" json:"elasticsearch_worker_number_of_shards,omitempty"` EmailAdditionalText *string `url:"email_additional_text,omitempty" json:"email_additional_text,omitempty"` EmailAuthorInBody *bool `url:"email_author_in_body,omitempty" json:"email_author_in_body,omitempty"` EmailConfirmationSetting *string `url:"email_confirmation_setting,omitempty" json:"email_confirmation_setting,omitempty"` EmailRestrictions *string `url:"email_restrictions,omitempty" json:"email_restrictions,omitempty"` EmailRestrictionsEnabled *bool `url:"email_restrictions_enabled,omitempty" json:"email_restrictions_enabled,omitempty"` EnableArtifactExternalRedirectWarningPage *bool `url:"enable_artifact_external_redirect_warning_page,omitempty" json:"enable_artifact_external_redirect_warning_page,omitempty"` EnabledGitAccessProtocol *string `url:"enabled_git_access_protocol,omitempty" json:"enabled_git_access_protocol,omitempty"` EnforceNamespaceStorageLimit *bool `url:"enforce_namespace_storage_limit,omitempty" json:"enforce_namespace_storage_limit,omitempty"` EnforcePATExpiration *bool `url:"enforce_pat_expiration,omitempty" json:"enforce_pat_expiration,omitempty"` EnforceSSHKeyExpiration *bool `url:"enforce_ssh_key_expiration,omitempty" json:"enforce_ssh_key_expiration,omitempty"` EnforceTerms *bool `url:"enforce_terms,omitempty" json:"enforce_terms,omitempty"` ExternalAuthClientCert *string `url:"external_auth_client_cert,omitempty" json:"external_auth_client_cert,omitempty"` ExternalAuthClientKey *string `url:"external_auth_client_key,omitempty" json:"external_auth_client_key,omitempty"` ExternalAuthClientKeyPass *string `url:"external_auth_client_key_pass,omitempty" json:"external_auth_client_key_pass,omitempty"` ExternalAuthorizationServiceDefaultLabel *string `url:"external_authorization_service_default_label,omitempty" json:"external_authorization_service_default_label,omitempty"` ExternalAuthorizationServiceEnabled *bool `url:"external_authorization_service_enabled,omitempty" json:"external_authorization_service_enabled,omitempty"` ExternalAuthorizationServiceTimeout *float64 `url:"external_authorization_service_timeout,omitempty" json:"external_authorization_service_timeout,omitempty"` ExternalAuthorizationServiceURL *string `url:"external_authorization_service_url,omitempty" json:"external_authorization_service_url,omitempty"` ExternalPipelineValidationServiceTimeout *int `url:"external_pipeline_validation_service_timeout,omitempty" json:"external_pipeline_validation_service_timeout,omitempty"` ExternalPipelineValidationServiceToken *string `url:"external_pipeline_validation_service_token,omitempty" json:"external_pipeline_validation_service_token,omitempty"` ExternalPipelineValidationServiceURL *string `url:"external_pipeline_validation_service_url,omitempty" json:"external_pipeline_validation_service_url,omitempty"` FailedLoginAttemptsUnlockPeriodInMinutes *int `url:"failed_login_attempts_unlock_period_in_minutes,omitempty" json:"failed_login_attempts_unlock_period_in_minutes,omitempty"` FileTemplateProjectID *int `url:"file_template_project_id,omitempty" json:"file_template_project_id,omitempty"` FirstDayOfWeek *int `url:"first_day_of_week,omitempty" json:"first_day_of_week,omitempty"` FlocEnabled *bool `url:"floc_enabled,omitempty" json:"floc_enabled,omitempty"` GeoNodeAllowedIPs *string `url:"geo_node_allowed_ips,omitempty" json:"geo_node_allowed_ips,omitempty"` GeoStatusTimeout *int `url:"geo_status_timeout,omitempty" json:"geo_status_timeout,omitempty"` GitRateLimitUsersAlertlist *[]string `url:"git_rate_limit_users_alertlist,omitempty" json:"git_rate_limit_users_alertlist,omitempty"` GitTwoFactorSessionExpiry *int `url:"git_two_factor_session_expiry,omitempty" json:"git_two_factor_session_expiry,omitempty"` GitalyTimeoutDefault *int `url:"gitaly_timeout_default,omitempty" json:"gitaly_timeout_default,omitempty"` GitalyTimeoutFast *int `url:"gitaly_timeout_fast,omitempty" json:"gitaly_timeout_fast,omitempty"` GitalyTimeoutMedium *int `url:"gitaly_timeout_medium,omitempty" json:"gitaly_timeout_medium,omitempty"` GitlabDedicatedInstance *bool `url:"gitlab_dedicated_instance,omitempty" json:"gitlab_dedicated_instance,omitempty"` GitlabEnvironmentToolkitInstance *bool `url:"gitlab_environment_toolkit_instance,omitempty" json:"gitlab_environment_toolkit_instance,omitempty"` GitlabShellOperationLimit *int `url:"gitlab_shell_operation_limit,omitempty" json:"gitlab_shell_operation_limit,omitempty"` GitpodEnabled *bool `url:"gitpod_enabled,omitempty" json:"gitpod_enabled,omitempty"` GitpodURL *string `url:"gitpod_url,omitempty" json:"gitpod_url,omitempty"` GitRateLimitUsersAllowlist *[]string `url:"git_rate_limit_users_allowlist,omitempty" json:"git_rate_limit_users_allowlist,omitempty"` GloballyAllowedIPs *string `url:"globally_allowed_ips,omitempty" json:"globally_allowed_ips,omitempty"` GrafanaEnabled *bool `url:"grafana_enabled,omitempty" json:"grafana_enabled,omitempty"` GrafanaURL *string `url:"grafana_url,omitempty" json:"grafana_url,omitempty"` GravatarEnabled *bool `url:"gravatar_enabled,omitempty" json:"gravatar_enabled,omitempty"` GroupDownloadExportLimit *int `url:"group_download_export_limit,omitempty" json:"group_download_export_limit,omitempty"` GroupExportLimit *int `url:"group_export_limit,omitempty" json:"group_export_limit,omitempty"` GroupImportLimit *int `url:"group_import_limit,omitempty" json:"group_import_limit,omitempty"` GroupOwnersCanManageDefaultBranchProtection *bool `url:"group_owners_can_manage_default_branch_protection,omitempty" json:"group_owners_can_manage_default_branch_protection,omitempty"` GroupRunnerTokenExpirationInterval *int `url:"group_runner_token_expiration_interval,omitempty" json:"group_runner_token_expiration_interval,omitempty"` HTMLEmailsEnabled *bool `url:"html_emails_enabled,omitempty" json:"html_emails_enabled,omitempty"` HashedStorageEnabled *bool `url:"hashed_storage_enabled,omitempty" json:"hashed_storage_enabled,omitempty"` HelpPageDocumentationBaseURL *string `url:"help_page_documentation_base_url,omitempty" json:"help_page_documentation_base_url,omitempty"` HelpPageHideCommercialContent *bool `url:"help_page_hide_commercial_content,omitempty" json:"help_page_hide_commercial_content,omitempty"` HelpPageSupportURL *string `url:"help_page_support_url,omitempty" json:"help_page_support_url,omitempty"` HelpPageText *string `url:"help_page_text,omitempty" json:"help_page_text,omitempty"` HelpText *string `url:"help_text,omitempty" json:"help_text,omitempty"` HideThirdPartyOffers *bool `url:"hide_third_party_offers,omitempty" json:"hide_third_party_offers,omitempty"` HomePageURL *string `url:"home_page_url,omitempty" json:"home_page_url,omitempty"` HousekeepingBitmapsEnabled *bool `url:"housekeeping_bitmaps_enabled,omitempty" json:"housekeeping_bitmaps_enabled,omitempty"` HousekeepingEnabled *bool `url:"housekeeping_enabled,omitempty" json:"housekeeping_enabled,omitempty"` HousekeepingFullRepackPeriod *int `url:"housekeeping_full_repack_period,omitempty" json:"housekeeping_full_repack_period,omitempty"` HousekeepingGcPeriod *int `url:"housekeeping_gc_period,omitempty" json:"housekeeping_gc_period,omitempty"` HousekeepingIncrementalRepackPeriod *int `url:"housekeeping_incremental_repack_period,omitempty" json:"housekeeping_incremental_repack_period,omitempty"` HousekeepingOptimizeRepositoryPeriod *int `url:"housekeeping_optimize_repository_period,omitempty" json:"housekeeping_optimize_repository_period,omitempty"` ImportSources *[]string `url:"import_sources,omitempty" json:"import_sources,omitempty"` InactiveProjectsDeleteAfterMonths *int `url:"inactive_projects_delete_after_months,omitempty" json:"inactive_projects_delete_after_months,omitempty"` InactiveProjectsMinSizeMB *int `url:"inactive_projects_min_size_mb,omitempty" json:"inactive_projects_min_size_mb,omitempty"` InactiveProjectsSendWarningEmailAfterMonths *int `url:"inactive_projects_send_warning_email_after_months,omitempty" json:"inactive_projects_send_warning_email_after_months,omitempty"` IncludeOptionalMetricsInServicePing *bool `url:"include_optional_metrics_in_service_ping,omitempty" json:"include_optional_metrics_in_service_ping,omitempty"` InProductMarketingEmailsEnabled *bool `url:"in_product_marketing_emails_enabled,omitempty" json:"in_product_marketing_emails_enabled,omitempty"` InvisibleCaptchaEnabled *bool `url:"invisible_captcha_enabled,omitempty" json:"invisible_captcha_enabled,omitempty"` IssuesCreateLimit *int `url:"issues_create_limit,omitempty" json:"issues_create_limit,omitempty"` JiraConnectApplicationKey *string `url:"jira_connect_application_key,omitempty" json:"jira_connect_application_key,omitempty"` JiraConnectPublicKeyStorageEnabled *bool `url:"jira_connect_public_key_storage_enabled,omitempty" json:"jira_connect_public_key_storage_enabled,omitempty"` JiraConnectProxyURL *string `url:"jira_connect_proxy_url,omitempty" json:"jira_connect_proxy_url,omitempty"` KeepLatestArtifact *bool `url:"keep_latest_artifact,omitempty" json:"keep_latest_artifact,omitempty"` KrokiEnabled *bool `url:"kroki_enabled,omitempty" json:"kroki_enabled,omitempty"` KrokiFormats *map[string]bool `url:"kroki_formats,omitempty" json:"kroki_formats,omitempty"` KrokiURL *string `url:"kroki_url,omitempty" json:"kroki_url,omitempty"` LocalMarkdownVersion *int `url:"local_markdown_version,omitempty" json:"local_markdown_version,omitempty"` LockDuoFeaturesEnabled *bool `url:"lock_duo_features_enabled,omitempty" json:"lock_duo_features_enabled,omitempty"` LockMembershipsToLDAP *bool `url:"lock_memberships_to_ldap,omitempty" json:"lock_memberships_to_ldap,omitempty"` LoginRecaptchaProtectionEnabled *bool `url:"login_recaptcha_protection_enabled,omitempty" json:"login_recaptcha_protection_enabled,omitempty"` MailgunEventsEnabled *bool `url:"mailgun_events_enabled,omitempty" json:"mailgun_events_enabled,omitempty"` MailgunSigningKey *string `url:"mailgun_signing_key,omitempty" json:"mailgun_signing_key,omitempty"` MaintenanceMode *bool `url:"maintenance_mode,omitempty" json:"maintenance_mode,omitempty"` MaintenanceModeMessage *string `url:"maintenance_mode_message,omitempty" json:"maintenance_mode_message,omitempty"` MavenPackageRequestsForwarding *bool `url:"maven_package_requests_forwarding,omitempty" json:"maven_package_requests_forwarding,omitempty"` MaxArtifactsSize *int `url:"max_artifacts_size,omitempty" json:"max_artifacts_size,omitempty"` MaxAttachmentSize *int `url:"max_attachment_size,omitempty" json:"max_attachment_size,omitempty"` MaxDecompressedArchiveSize *int `url:"max_decompressed_archive_size,omitempty" json:"max_decompressed_archive_size,omitempty"` MaxExportSize *int `url:"max_export_size,omitempty" json:"max_export_size,omitempty"` MaxImportRemoteFileSize *int `url:"max_import_remote_file_size,omitempty" json:"max_import_remote_file_size,omitempty"` MaxImportSize *int `url:"max_import_size,omitempty" json:"max_import_size,omitempty"` MaxLoginAttempts *int `url:"max_login_attempts,omitempty" json:"max_login_attempts,omitempty"` MaxNumberOfRepositoryDownloads *int `url:"max_number_of_repository_downloads,omitempty" json:"max_number_of_repository_downloads,omitempty"` MaxNumberOfRepositoryDownloadsWithinTimePeriod *int `url:"max_number_of_repository_downloads_within_time_period,omitempty" json:"max_number_of_repository_downloads_within_time_period,omitempty"` MaxPagesSize *int `url:"max_pages_size,omitempty" json:"max_pages_size,omitempty"` MaxPersonalAccessTokenLifetime *int `url:"max_personal_access_token_lifetime,omitempty" json:"max_personal_access_token_lifetime,omitempty"` MaxSSHKeyLifetime *int `url:"max_ssh_key_lifetime,omitempty" json:"max_ssh_key_lifetime,omitempty"` MaxTerraformStateSizeBytes *int `url:"max_terraform_state_size_bytes,omitempty" json:"max_terraform_state_size_bytes,omitempty"` MaxYAMLDepth *int `url:"max_yaml_depth,omitempty" json:"max_yaml_depth,omitempty"` MaxYAMLSizeBytes *int `url:"max_yaml_size_bytes,omitempty" json:"max_yaml_size_bytes,omitempty"` MetricsMethodCallThreshold *int `url:"metrics_method_call_threshold,omitempty" json:"metrics_method_call_threshold,omitempty"` MinimumPasswordLength *int `url:"minimum_password_length,omitempty" json:"minimum_password_length,omitempty"` MirrorAvailable *bool `url:"mirror_available,omitempty" json:"mirror_available,omitempty"` MirrorCapacityThreshold *int `url:"mirror_capacity_threshold,omitempty" json:"mirror_capacity_threshold,omitempty"` MirrorMaxCapacity *int `url:"mirror_max_capacity,omitempty" json:"mirror_max_capacity,omitempty"` MirrorMaxDelay *int `url:"mirror_max_delay,omitempty" json:"mirror_max_delay,omitempty"` NPMPackageRequestsForwarding *bool `url:"npm_package_requests_forwarding,omitempty" json:"npm_package_requests_forwarding,omitempty"` NotesCreateLimit *int `url:"notes_create_limit,omitempty" json:"notes_create_limit,omitempty"` NotifyOnUnknownSignIn *bool `url:"notify_on_unknown_sign_in,omitempty" json:"notify_on_unknown_sign_in,omitempty"` NugetSkipMetadataURLValidation *bool `url:"nuget_skip_metadata_url_validation,omitempty" json:"nuget_skip_metadata_url_validation,omitempty"` OutboundLocalRequestsAllowlistRaw *string `url:"outbound_local_requests_allowlist_raw,omitempty" json:"outbound_local_requests_allowlist_raw,omitempty"` OutboundLocalRequestsWhitelist *[]string `url:"outbound_local_requests_whitelist,omitempty" json:"outbound_local_requests_whitelist,omitempty"` PackageMetadataPURLTypes *[]int `url:"package_metadata_purl_types,omitempty" json:"package_metadata_purl_types,omitempty"` PackageRegistryAllowAnyoneToPullOption *bool `url:"package_registry_allow_anyone_to_pull_option,omitempty" json:"package_registry_allow_anyone_to_pull_option,omitempty"` PackageRegistryCleanupPoliciesWorkerCapacity *int `url:"package_registry_cleanup_policies_worker_capacity,omitempty" json:"package_registry_cleanup_policies_worker_capacity,omitempty"` PagesDomainVerificationEnabled *bool `url:"pages_domain_verification_enabled,omitempty" json:"pages_domain_verification_enabled,omitempty"` PasswordAuthenticationEnabledForGit *bool `url:"password_authentication_enabled_for_git,omitempty" json:"password_authentication_enabled_for_git,omitempty"` PasswordAuthenticationEnabledForWeb *bool `url:"password_authentication_enabled_for_web,omitempty" json:"password_authentication_enabled_for_web,omitempty"` PasswordNumberRequired *bool `url:"password_number_required,omitempty" json:"password_number_required,omitempty"` PasswordSymbolRequired *bool `url:"password_symbol_required,omitempty" json:"password_symbol_required,omitempty"` PasswordUppercaseRequired *bool `url:"password_uppercase_required,omitempty" json:"password_uppercase_required,omitempty"` PasswordLowercaseRequired *bool `url:"password_lowercase_required,omitempty" json:"password_lowercase_required,omitempty"` PerformanceBarAllowedGroupID *int `url:"performance_bar_allowed_group_id,omitempty" json:"performance_bar_allowed_group_id,omitempty"` PerformanceBarAllowedGroupPath *string `url:"performance_bar_allowed_group_path,omitempty" json:"performance_bar_allowed_group_path,omitempty"` PerformanceBarEnabled *bool `url:"performance_bar_enabled,omitempty" json:"performance_bar_enabled,omitempty"` PersonalAccessTokenPrefix *string `url:"personal_access_token_prefix,omitempty" json:"personal_access_token_prefix,omitempty"` PlantumlEnabled *bool `url:"plantuml_enabled,omitempty" json:"plantuml_enabled,omitempty"` PlantumlURL *string `url:"plantuml_url,omitempty" json:"plantuml_url,omitempty"` PipelineLimitPerProjectUserSha *int `url:"pipeline_limit_per_project_user_sha,omitempty" json:"pipeline_limit_per_project_user_sha,omitempty"` PollingIntervalMultiplier *float64 `url:"polling_interval_multiplier,omitempty" json:"polling_interval_multiplier,omitempty"` PreventMergeRequestsAuthorApproval *bool `url:"prevent_merge_requests_author_approval,omitempty" json:"prevent_merge_requests_author_approval,omitempty"` PreventMergeRequestsCommittersApproval *bool `url:"prevent_merge_requests_committers_approval,omitempty" json:"prevent_merge_requests_committers_approval,omitempty"` ProjectDownloadExportLimit *int `url:"project_download_export_limit,omitempty" json:"project_download_export_limit,omitempty"` ProjectExportEnabled *bool `url:"project_export_enabled,omitempty" json:"project_export_enabled,omitempty"` ProjectExportLimit *int `url:"project_export_limit,omitempty" json:"project_export_limit,omitempty"` ProjectImportLimit *int `url:"project_import_limit,omitempty" json:"project_import_limit,omitempty"` ProjectJobsAPIRateLimit *int `url:"project_jobs_api_rate_limit,omitempty" json:"project_jobs_api_rate_limit,omitempty"` ProjectRunnerTokenExpirationInterval *int `url:"project_runner_token_expiration_interval,omitempty" json:"project_runner_token_expiration_interval,omitempty"` ProjectsAPIRateLimitUnauthenticated *int `url:"projects_api_rate_limit_unauthenticated,omitempty" json:"projects_api_rate_limit_unauthenticated,omitempty"` PrometheusMetricsEnabled *bool `url:"prometheus_metrics_enabled,omitempty" json:"prometheus_metrics_enabled,omitempty"` ProtectedCIVariables *bool `url:"protected_ci_variables,omitempty" json:"protected_ci_variables,omitempty"` PseudonymizerEnabled *bool `url:"pseudonymizer_enabled,omitempty" json:"pseudonymizer_enabled,omitempty"` PushEventActivitiesLimit *int `url:"push_event_activities_limit,omitempty" json:"push_event_activities_limit,omitempty"` PushEventHooksLimit *int `url:"push_event_hooks_limit,omitempty" json:"push_event_hooks_limit,omitempty"` PyPIPackageRequestsForwarding *bool `url:"pypi_package_requests_forwarding,omitempty" json:"pypi_package_requests_forwarding,omitempty"` RSAKeyRestriction *int `url:"rsa_key_restriction,omitempty" json:"rsa_key_restriction,omitempty"` RateLimitingResponseText *string `url:"rate_limiting_response_text,omitempty" json:"rate_limiting_response_text,omitempty"` RawBlobRequestLimit *int `url:"raw_blob_request_limit,omitempty" json:"raw_blob_request_limit,omitempty"` RecaptchaEnabled *bool `url:"recaptcha_enabled,omitempty" json:"recaptcha_enabled,omitempty"` RecaptchaPrivateKey *string `url:"recaptcha_private_key,omitempty" json:"recaptcha_private_key,omitempty"` RecaptchaSiteKey *string `url:"recaptcha_site_key,omitempty" json:"recaptcha_site_key,omitempty"` ReceiveMaxInputSize *int `url:"receive_max_input_size,omitempty" json:"receive_max_input_size,omitempty"` ReceptiveClusterAgentsEnabled *bool `url:"receptive_cluster_agents_enabled,omitempty" json:"receptive_cluster_agents_enabled,omitempty"` RememberMeEnabled *bool `url:"remember_me_enabled,omitempty" json:"remember_me_enabled,omitempty"` RepositoryChecksEnabled *bool `url:"repository_checks_enabled,omitempty" json:"repository_checks_enabled,omitempty"` RepositorySizeLimit *int `url:"repository_size_limit,omitempty" json:"repository_size_limit,omitempty"` RepositoryStorages *[]string `url:"repository_storages,omitempty" json:"repository_storages,omitempty"` RepositoryStoragesWeighted *map[string]int `url:"repository_storages_weighted,omitempty" json:"repository_storages_weighted,omitempty"` RequireAdminApprovalAfterUserSignup *bool `url:"require_admin_approval_after_user_signup,omitempty" json:"require_admin_approval_after_user_signup,omitempty"` RequireAdminTwoFactorAuthentication *bool `url:"require_admin_two_factor_authentication,omitempty" json:"require_admin_two_factor_authentication,omitempty"` RequirePersonalAccessTokenExpiry *bool `url:"require_personal_access_token_expiry,omitempty" json:"require_personal_access_token_expiry,omitempty"` RequireTwoFactorAuthentication *bool `url:"require_two_factor_authentication,omitempty" json:"require_two_factor_authentication,omitempty"` RestrictedVisibilityLevels *[]VisibilityValue `url:"restricted_visibility_levels,omitempty" json:"restricted_visibility_levels,omitempty"` RunnerTokenExpirationInterval *int `url:"runner_token_expiration_interval,omitempty" json:"runner_token_expiration_interval,omitempty"` SearchRateLimit *int `url:"search_rate_limit,omitempty" json:"search_rate_limit,omitempty"` SearchRateLimitUnauthenticated *int `url:"search_rate_limit_unauthenticated,omitempty" json:"search_rate_limit_unauthenticated,omitempty"` SecretDetectionRevocationTokenTypesURL *string `url:"secret_detection_revocation_token_types_url,omitempty" json:"secret_detection_revocation_token_types_url,omitempty"` SecretDetectionTokenRevocationEnabled *bool `url:"secret_detection_token_revocation_enabled,omitempty" json:"secret_detection_token_revocation_enabled,omitempty"` SecretDetectionTokenRevocationToken *string `url:"secret_detection_token_revocation_token,omitempty" json:"secret_detection_token_revocation_token,omitempty"` SecretDetectionTokenRevocationURL *string `url:"secret_detection_token_revocation_url,omitempty" json:"secret_detection_token_revocation_url,omitempty"` SecurityApprovalPoliciesLimit *int `url:"security_approval_policies_limit,omitempty" json:"security_approval_policies_limit,omitempty"` SecurityPolicyGlobalGroupApproversEnabled *bool `url:"security_policy_global_group_approvers_enabled,omitempty" json:"security_policy_global_group_approvers_enabled,omitempty"` SecurityTXTContent *string `url:"security_txt_content,omitempty" json:"security_txt_content,omitempty"` SendUserConfirmationEmail *bool `url:"send_user_confirmation_email,omitempty" json:"send_user_confirmation_email,omitempty"` SentryClientsideDSN *string `url:"sentry_clientside_dsn,omitempty" json:"sentry_clientside_dsn,omitempty"` SentryDSN *string `url:"sentry_dsn,omitempty" json:"sentry_dsn,omitempty"` SentryEnabled *string `url:"sentry_enabled,omitempty" json:"sentry_enabled,omitempty"` SentryEnvironment *string `url:"sentry_environment,omitempty" json:"sentry_environment,omitempty"` ServiceAccessTokensExpirationEnforced *bool `url:"service_access_tokens_expiration_enforced,omitempty" json:"service_access_tokens_expiration_enforced,omitempty"` SessionExpireDelay *int `url:"session_expire_delay,omitempty" json:"session_expire_delay,omitempty"` SharedRunnersEnabled *bool `url:"shared_runners_enabled,omitempty" json:"shared_runners_enabled,omitempty"` SharedRunnersMinutes *int `url:"shared_runners_minutes,omitempty" json:"shared_runners_minutes,omitempty"` SharedRunnersText *string `url:"shared_runners_text,omitempty" json:"shared_runners_text,omitempty"` SidekiqJobLimiterCompressionThresholdBytes *int `url:"sidekiq_job_limiter_compression_threshold_bytes,omitempty" json:"sidekiq_job_limiter_compression_threshold_bytes,omitempty"` SidekiqJobLimiterLimitBytes *int `url:"sidekiq_job_limiter_limit_bytes,omitempty" json:"sidekiq_job_limiter_limit_bytes,omitempty"` SidekiqJobLimiterMode *string `url:"sidekiq_job_limiter_mode,omitempty" json:"sidekiq_job_limiter_mode,omitempty"` SignInText *string `url:"sign_in_text,omitempty" json:"sign_in_text,omitempty"` SignupEnabled *bool `url:"signup_enabled,omitempty" json:"signup_enabled,omitempty"` SilentAdminExportsEnabled *bool `url:"silent_admin_exports_enabled,omitempty" json:"silent_admin_exports_enabled,omitempty"` SilentModeEnabled *bool `url:"silent_mode_enabled,omitempty" json:"silent_mode_enabled,omitempty"` SlackAppEnabled *bool `url:"slack_app_enabled,omitempty" json:"slack_app_enabled,omitempty"` SlackAppID *string `url:"slack_app_id,omitempty" json:"slack_app_id,omitempty"` SlackAppSecret *string `url:"slack_app_secret,omitempty" json:"slack_app_secret,omitempty"` SlackAppSigningSecret *string `url:"slack_app_signing_secret,omitempty" json:"slack_app_signing_secret,omitempty"` SlackAppVerificationToken *string `url:"slack_app_verification_token,omitempty" json:"slack_app_verification_token,omitempty"` SnippetSizeLimit *int `url:"snippet_size_limit,omitempty" json:"snippet_size_limit,omitempty"` SnowplowAppID *string `url:"snowplow_app_id,omitempty" json:"snowplow_app_id,omitempty"` SnowplowCollectorHostname *string `url:"snowplow_collector_hostname,omitempty" json:"snowplow_collector_hostname,omitempty"` SnowplowCookieDomain *string `url:"snowplow_cookie_domain,omitempty" json:"snowplow_cookie_domain,omitempty"` SnowplowDatabaseCollectorHostname *string `url:"snowplow_database_collector_hostname,omitempty" json:"snowplow_database_collector_hostname,omitempty"` SnowplowEnabled *bool `url:"snowplow_enabled,omitempty" json:"snowplow_enabled,omitempty"` SourcegraphEnabled *bool `url:"sourcegraph_enabled,omitempty" json:"sourcegraph_enabled,omitempty"` SourcegraphPublicOnly *bool `url:"sourcegraph_public_only,omitempty" json:"sourcegraph_public_only,omitempty"` SourcegraphURL *string `url:"sourcegraph_url,omitempty" json:"sourcegraph_url,omitempty"` SpamCheckAPIKey *string `url:"spam_check_api_key,omitempty" json:"spam_check_api_key,omitempty"` SpamCheckEndpointEnabled *bool `url:"spam_check_endpoint_enabled,omitempty" json:"spam_check_endpoint_enabled,omitempty"` SpamCheckEndpointURL *string `url:"spam_check_endpoint_url,omitempty" json:"spam_check_endpoint_url,omitempty"` StaticObjectsExternalStorageAuthToken *string `url:"static_objects_external_storage_auth_token,omitempty" json:"static_objects_external_storage_auth_token,omitempty"` StaticObjectsExternalStorageURL *string `url:"static_objects_external_storage_url,omitempty" json:"static_objects_external_storage_url,omitempty"` SuggestPipelineEnabled *bool `url:"suggest_pipeline_enabled,omitempty" json:"suggest_pipeline_enabled,omitempty"` TerminalMaxSessionTime *int `url:"terminal_max_session_time,omitempty" json:"terminal_max_session_time,omitempty"` Terms *string `url:"terms,omitempty" json:"terms,omitempty"` ThrottleAuthenticatedAPIEnabled *bool `url:"throttle_authenticated_api_enabled,omitempty" json:"throttle_authenticated_api_enabled,omitempty"` ThrottleAuthenticatedAPIPeriodInSeconds *int `url:"throttle_authenticated_api_period_in_seconds,omitempty" json:"throttle_authenticated_api_period_in_seconds,omitempty"` ThrottleAuthenticatedAPIRequestsPerPeriod *int `url:"throttle_authenticated_api_requests_per_period,omitempty" json:"throttle_authenticated_api_requests_per_period,omitempty"` ThrottleAuthenticatedDeprecatedAPIEnabled *bool `url:"throttle_authenticated_deprecated_api_enabled,omitempty" json:"throttle_authenticated_deprecated_api_enabled,omitempty"` ThrottleAuthenticatedDeprecatedAPIPeriodInSeconds *int `url:"throttle_authenticated_deprecated_api_period_in_seconds,omitempty" json:"throttle_authenticated_deprecated_api_period_in_seconds,omitempty"` ThrottleAuthenticatedDeprecatedAPIRequestsPerPeriod *int `url:"throttle_authenticated_deprecated_api_requests_per_period,omitempty" json:"throttle_authenticated_deprecated_api_requests_per_period,omitempty"` ThrottleAuthenticatedFilesAPIEnabled *bool `url:"throttle_authenticated_files_api_enabled,omitempty" json:"throttle_authenticated_files_api_enabled,omitempty"` ThrottleAuthenticatedFilesAPIPeriodInSeconds *int `url:"throttle_authenticated_files_api_period_in_seconds,omitempty" json:"throttle_authenticated_files_api_period_in_seconds,omitempty"` ThrottleAuthenticatedFilesAPIRequestsPerPeriod *int `url:"throttle_authenticated_files_api_requests_per_period,omitempty" json:"throttle_authenticated_files_api_requests_per_period,omitempty"` ThrottleAuthenticatedGitLFSEnabled *bool `url:"throttle_authenticated_git_lfs_enabled,omitempty" json:"throttle_authenticated_git_lfs_enabled,omitempty"` ThrottleAuthenticatedGitLFSPeriodInSeconds *int `url:"throttle_authenticated_git_lfs_period_in_seconds,omitempty" json:"throttle_authenticated_git_lfs_period_in_seconds,omitempty"` ThrottleAuthenticatedGitLFSRequestsPerPeriod *int `url:"throttle_authenticated_git_lfs_requests_per_period,omitempty" json:"throttle_authenticated_git_lfs_requests_per_period,omitempty"` ThrottleAuthenticatedPackagesAPIEnabled *bool `url:"throttle_authenticated_packages_api_enabled,omitempty" json:"throttle_authenticated_packages_api_enabled,omitempty"` ThrottleAuthenticatedPackagesAPIPeriodInSeconds *int `url:"throttle_authenticated_packages_api_period_in_seconds,omitempty" json:"throttle_authenticated_packages_api_period_in_seconds,omitempty"` ThrottleAuthenticatedPackagesAPIRequestsPerPeriod *int `url:"throttle_authenticated_packages_api_requests_per_period,omitempty" json:"throttle_authenticated_packages_api_requests_per_period,omitempty"` ThrottleAuthenticatedWebEnabled *bool `url:"throttle_authenticated_web_enabled,omitempty" json:"throttle_authenticated_web_enabled,omitempty"` ThrottleAuthenticatedWebPeriodInSeconds *int `url:"throttle_authenticated_web_period_in_seconds,omitempty" json:"throttle_authenticated_web_period_in_seconds,omitempty"` ThrottleAuthenticatedWebRequestsPerPeriod *int `url:"throttle_authenticated_web_requests_per_period,omitempty" json:"throttle_authenticated_web_requests_per_period,omitempty"` ThrottleIncidentManagementNotificationEnabled *bool `url:"throttle_incident_management_notification_enabled,omitempty" json:"throttle_incident_management_notification_enabled,omitempty"` ThrottleIncidentManagementNotificationPerPeriod *int `url:"throttle_incident_management_notification_per_period,omitempty" json:"throttle_incident_management_notification_per_period,omitempty"` ThrottleIncidentManagementNotificationPeriodInSeconds *int `url:"throttle_incident_management_notification_period_in_seconds,omitempty" json:"throttle_incident_management_notification_period_in_seconds,omitempty"` ThrottleProtectedPathsEnabled *bool `url:"throttle_protected_paths_enabled_enabled,omitempty" json:"throttle_protected_paths_enabled,omitempty"` ThrottleProtectedPathsPeriodInSeconds *int `url:"throttle_protected_paths_enabled_period_in_seconds,omitempty" json:"throttle_protected_paths_period_in_seconds,omitempty"` ThrottleProtectedPathsRequestsPerPeriod *int `url:"throttle_protected_paths_enabled_requests_per_period,omitempty" json:"throttle_protected_paths_per_period,omitempty"` ThrottleUnauthenticatedAPIEnabled *bool `url:"throttle_unauthenticated_api_enabled,omitempty" json:"throttle_unauthenticated_api_enabled,omitempty"` ThrottleUnauthenticatedAPIPeriodInSeconds *int `url:"throttle_unauthenticated_api_period_in_seconds,omitempty" json:"throttle_unauthenticated_api_period_in_seconds,omitempty"` ThrottleUnauthenticatedAPIRequestsPerPeriod *int `url:"throttle_unauthenticated_api_requests_per_period,omitempty" json:"throttle_unauthenticated_api_requests_per_period,omitempty"` ThrottleUnauthenticatedDeprecatedAPIEnabled *bool `url:"throttle_unauthenticated_deprecated_api_enabled,omitempty" json:"throttle_unauthenticated_deprecated_api_enabled,omitempty"` ThrottleUnauthenticatedDeprecatedAPIPeriodInSeconds *int `url:"throttle_unauthenticated_deprecated_api_period_in_seconds,omitempty" json:"throttle_unauthenticated_deprecated_api_period_in_seconds,omitempty"` ThrottleUnauthenticatedDeprecatedAPIRequestsPerPeriod *int `url:"throttle_unauthenticated_deprecated_api_requests_per_period,omitempty" json:"throttle_unauthenticated_deprecated_api_requests_per_period,omitempty"` ThrottleUnauthenticatedEnabled *bool `url:"throttle_unauthenticated_enabled,omitempty" json:"throttle_unauthenticated_enabled,omitempty"` ThrottleUnauthenticatedFilesAPIEnabled *bool `url:"throttle_unauthenticated_files_api_enabled,omitempty" json:"throttle_unauthenticated_files_api_enabled,omitempty"` ThrottleUnauthenticatedFilesAPIPeriodInSeconds *int `url:"throttle_unauthenticated_files_api_period_in_seconds,omitempty" json:"throttle_unauthenticated_files_api_period_in_seconds,omitempty"` ThrottleUnauthenticatedFilesAPIRequestsPerPeriod *int `url:"throttle_unauthenticated_files_api_requests_per_period,omitempty" json:"throttle_unauthenticated_files_api_requests_per_period,omitempty"` ThrottleUnauthenticatedGitLFSEnabled *bool `url:"throttle_unauthenticated_git_lfs_enabled,omitempty" json:"throttle_unauthenticated_git_lfs_enabled,omitempty"` ThrottleUnauthenticatedGitLFSPeriodInSeconds *int `url:"throttle_unauthenticated_git_lfs_period_in_seconds,omitempty" json:"throttle_unauthenticated_git_lfs_period_in_seconds,omitempty"` ThrottleUnauthenticatedGitLFSRequestsPerPeriod *int `url:"throttle_unauthenticated_git_lfs_requests_per_period,omitempty" json:"throttle_unauthenticated_git_lfs_requests_per_period,omitempty"` ThrottleUnauthenticatedPackagesAPIEnabled *bool `url:"throttle_unauthenticated_packages_api_enabled,omitempty" json:"throttle_unauthenticated_packages_api_enabled,omitempty"` ThrottleUnauthenticatedPackagesAPIPeriodInSeconds *int `url:"throttle_unauthenticated_packages_api_period_in_seconds,omitempty" json:"throttle_unauthenticated_packages_api_period_in_seconds,omitempty"` ThrottleUnauthenticatedPackagesAPIRequestsPerPeriod *int `url:"throttle_unauthenticated_packages_api_requests_per_period,omitempty" json:"throttle_unauthenticated_packages_api_requests_per_period,omitempty"` ThrottleUnauthenticatedPeriodInSeconds *int `url:"throttle_unauthenticated_period_in_seconds,omitempty" json:"throttle_unauthenticated_period_in_seconds,omitempty"` ThrottleUnauthenticatedRequestsPerPeriod *int `url:"throttle_unauthenticated_requests_per_period,omitempty" json:"throttle_unauthenticated_requests_per_period,omitempty"` ThrottleUnauthenticatedWebEnabled *bool `url:"throttle_unauthenticated_web_enabled,omitempty" json:"throttle_unauthenticated_web_enabled,omitempty"` ThrottleUnauthenticatedWebPeriodInSeconds *int `url:"throttle_unauthenticated_web_period_in_seconds,omitempty" json:"throttle_unauthenticated_web_period_in_seconds,omitempty"` ThrottleUnauthenticatedWebRequestsPerPeriod *int `url:"throttle_unauthenticated_web_requests_per_period,omitempty" json:"throttle_unauthenticated_web_requests_per_period,omitempty"` TimeTrackingLimitToHours *bool `url:"time_tracking_limit_to_hours,omitempty" json:"time_tracking_limit_to_hours,omitempty"` TwoFactorGracePeriod *int `url:"two_factor_grace_period,omitempty" json:"two_factor_grace_period,omitempty"` UnconfirmedUsersDeleteAfterDays *int `url:"unconfirmed_users_delete_after_days,omitempty" json:"unconfirmed_users_delete_after_days,omitempty"` UniqueIPsLimitEnabled *bool `url:"unique_ips_limit_enabled,omitempty" json:"unique_ips_limit_enabled,omitempty"` UniqueIPsLimitPerUser *int `url:"unique_ips_limit_per_user,omitempty" json:"unique_ips_limit_per_user,omitempty"` UniqueIPsLimitTimeWindow *int `url:"unique_ips_limit_time_window,omitempty" json:"unique_ips_limit_time_window,omitempty"` UpdateRunnerVersionsEnabled *bool `url:"update_runner_versions_enabled,omitempty" json:"update_runner_versions_enabled,omitempty"` UpdatingNameDisabledForUsers *bool `url:"updating_name_disabled_for_users,omitempty" json:"updating_name_disabled_for_users,omitempty"` UsagePingEnabled *bool `url:"usage_ping_enabled,omitempty" json:"usage_ping_enabled,omitempty"` UsagePingFeaturesEnabled *bool `url:"usage_ping_features_enabled,omitempty" json:"usage_ping_features_enabled,omitempty"` UseClickhouseForAnalytics *bool `url:"use_clickhouse_for_analytics,omitempty" json:"use_clickhouse_for_analytics,omitempty"` UserDeactivationEmailsEnabled *bool `url:"user_deactivation_emails_enabled,omitempty" json:"user_deactivation_emails_enabled,omitempty"` UserDefaultExternal *bool `url:"user_default_external,omitempty" json:"user_default_external,omitempty"` UserDefaultInternalRegex *string `url:"user_default_internal_regex,omitempty" json:"user_default_internal_regex,omitempty"` UserDefaultsToPrivateProfile *bool `url:"user_defaults_to_private_profile,omitempty" json:"user_defaults_to_private_profile,omitempty"` UserEmailLookupLimit *int `url:"user_email_lookup_limit,omitempty" json:"user_email_lookup_limit,omitempty"` UserOauthApplications *bool `url:"user_oauth_applications,omitempty" json:"user_oauth_applications,omitempty"` UserShowAddSSHKeyMessage *bool `url:"user_show_add_ssh_key_message,omitempty" json:"user_show_add_ssh_key_message,omitempty"` UsersGetByIDLimit *int `url:"users_get_by_id_limit,omitempty" json:"users_get_by_id_limit,omitempty"` UsersGetByIDLimitAllowlistRaw *string `url:"users_get_by_id_limit_allowlist_raw,omitempty" json:"users_get_by_id_limit_allowlist_raw,omitempty"` ValidRunnerRegistrars *[]string `url:"valid_runner_registrars,omitempty" json:"valid_runner_registrars,omitempty"` VersionCheckEnabled *bool `url:"version_check_enabled,omitempty" json:"version_check_enabled,omitempty"` WebIDEClientsidePreviewEnabled *bool `url:"web_ide_clientside_preview_enabled,omitempty" json:"web_ide_clientside_preview_enabled,omitempty"` WhatsNewVariant *string `url:"whats_new_variant,omitempty" json:"whats_new_variant,omitempty"` WikiPageMaxContentBytes *int `url:"wiki_page_max_content_bytes,omitempty" json:"wiki_page_max_content_bytes,omitempty"` } // BranchProtectionDefaultsOptions represents default Git protected branch permissions options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/groups.html#options-for-default_branch_protection_defaults type BranchProtectionDefaultsOptions struct { AllowedToPush *[]int `url:"allowed_to_push,omitempty" json:"allowed_to_push,omitempty"` AllowForcePush *bool `url:"allow_force_push,omitempty" json:"allow_force_push,omitempty"` AllowedToMerge *[]int `url:"allowed_to_merge,omitempty" json:"allowed_to_merge,omitempty"` DeveloperCanInitialPush *bool `url:"developer_can_initial_push,omitempty" json:"developer_can_initial_push,omitempty"` } // UpdateSettings updates the application settings. // // GitLab API docs: // https://docs.gitlab.com/ee/api/settings.html#change-application-settings func (s *SettingsService) UpdateSettings(opt *UpdateSettingsOptions, options ...RequestOptionFunc) (*Settings, *Response, error) { req, err := s.client.NewRequest(http.MethodPut, "application/settings", opt, options) if err != nil { return nil, nil, err } as := new(Settings) resp, err := s.client.Do(req, as) if err != nil { return nil, resp, err } return as, resp, nil } golang-gitlab-gitlab-org-api-client-go-0.123.0/settings_test.go000066400000000000000000000072021475761473200243060ustar00rootroot00000000000000// // Copyright 2021, Sander van Harmelen // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "encoding/json" "fmt" "net/http" "reflect" "testing" "github.com/stretchr/testify/assert" ) func TestGetSettings(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/application/settings", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, `{"id":1, "default_projects_limit" : 100000}`) }) settings, _, err := client.Settings.GetSettings() if err != nil { t.Fatal(err) } want := &Settings{ID: 1, DefaultProjectsLimit: 100000} if !reflect.DeepEqual(settings, want) { t.Errorf("Settings.GetSettings returned %+v, want %+v", settings, want) } } func TestUpdateSettings(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/application/settings", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPut) fmt.Fprint(w, `{"default_projects_limit" : 100}`) }) options := &UpdateSettingsOptions{ DefaultProjectsLimit: Ptr(100), } settings, _, err := client.Settings.UpdateSettings(options) if err != nil { t.Fatal(err) } want := &Settings{DefaultProjectsLimit: 100} if !reflect.DeepEqual(settings, want) { t.Errorf("Settings.UpdateSettings returned %+v, want %+v", settings, want) } } func TestSettingsWithEmptyContainerRegistry(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/application/settings", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, `{"id":1, "container_registry_import_created_before": ""}`) }) settings, _, err := client.Settings.GetSettings() if err != nil { t.Fatal(err) } want := &Settings{ID: 1, ContainerRegistryImportCreatedBefore: nil} if !reflect.DeepEqual(settings, want) { t.Errorf("Settings.UpdateSettings returned %+v, want %+v", settings, want) } } func TestSettingsDefaultBranchProtectionDefaults(t *testing.T) { mux, client := setup(t) var requestBody map[string]interface{} mux.HandleFunc("/api/v4/application/settings", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPut) // Read the request body into `requestBody` by unmarshalling it err := json.NewDecoder(r.Body).Decode(&requestBody) if err != nil { t.Fatal(err) } fmt.Fprint(w, `{"id":1, "default_projects_limit" : 100000}`) }) _, _, err := client.Settings.UpdateSettings(&UpdateSettingsOptions{ DefaultBranchProtectionDefaults: &DefaultBranchProtectionDefaultsOptions{ AllowedToPush: &[]*GroupAccessLevel{ {AccessLevel: Ptr(DeveloperPermissions)}, }, }, }) if err != nil { t.Fatal(err) } // This is the payload that should be produced. Float vs int won't matter when converted to a JSON string, so don't bother investigating why // it uses float insead of int when unmarshalled. want := map[string]interface{}{ "default_branch_protection_defaults": map[string]interface{}{ "allowed_to_push": []interface{}{ map[string]interface{}{"access_level": float64(30)}, }, }, } assert.Equal(t, want["default_branch_protection_defaults"], requestBody["default_branch_protection_defaults"]) } golang-gitlab-gitlab-org-api-client-go-0.123.0/sidekiq_metrics.go000066400000000000000000000111441475761473200245660ustar00rootroot00000000000000// // Copyright 2021, Sander van Harmelen // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "net/http" "time" ) // SidekiqService handles communication with the sidekiq service // // GitLab API docs: https://docs.gitlab.com/ee/api/sidekiq_metrics.html type SidekiqService struct { client *Client } // QueueMetrics represents the GitLab sidekiq queue metrics. // // GitLab API docs: // https://docs.gitlab.com/ee/api/sidekiq_metrics.html#get-the-current-queue-metrics type QueueMetrics struct { Queues map[string]struct { Backlog int `json:"backlog"` Latency int `json:"latency"` } `json:"queues"` } // GetQueueMetrics lists information about all the registered queues, // their backlog and their latency. // // GitLab API docs: // https://docs.gitlab.com/ee/api/sidekiq_metrics.html#get-the-current-queue-metrics func (s *SidekiqService) GetQueueMetrics(options ...RequestOptionFunc) (*QueueMetrics, *Response, error) { req, err := s.client.NewRequest(http.MethodGet, "/sidekiq/queue_metrics", nil, options) if err != nil { return nil, nil, err } q := new(QueueMetrics) resp, err := s.client.Do(req, q) if err != nil { return nil, resp, err } return q, resp, nil } // ProcessMetrics represents the GitLab sidekiq process metrics. // // GitLab API docs: // https://docs.gitlab.com/ee/api/sidekiq_metrics.html#get-the-current-process-metrics type ProcessMetrics struct { Processes []struct { Hostname string `json:"hostname"` Pid int `json:"pid"` Tag string `json:"tag"` StartedAt *time.Time `json:"started_at"` Queues []string `json:"queues"` Labels []string `json:"labels"` Concurrency int `json:"concurrency"` Busy int `json:"busy"` } `json:"processes"` } // GetProcessMetrics lists information about all the Sidekiq workers registered // to process your queues. // // GitLab API docs: // https://docs.gitlab.com/ee/api/sidekiq_metrics.html#get-the-current-process-metrics func (s *SidekiqService) GetProcessMetrics(options ...RequestOptionFunc) (*ProcessMetrics, *Response, error) { req, err := s.client.NewRequest(http.MethodGet, "/sidekiq/process_metrics", nil, options) if err != nil { return nil, nil, err } p := new(ProcessMetrics) resp, err := s.client.Do(req, p) if err != nil { return nil, resp, err } return p, resp, nil } // JobStats represents the GitLab sidekiq job stats. // // GitLab API docs: // https://docs.gitlab.com/ee/api/sidekiq_metrics.html#get-the-current-job-statistics type JobStats struct { Jobs struct { Processed int `json:"processed"` Failed int `json:"failed"` Enqueued int `json:"enqueued"` } `json:"jobs"` } // GetJobStats list information about the jobs that Sidekiq has performed. // // GitLab API docs: // https://docs.gitlab.com/ee/api/sidekiq_metrics.html#get-the-current-job-statistics func (s *SidekiqService) GetJobStats(options ...RequestOptionFunc) (*JobStats, *Response, error) { req, err := s.client.NewRequest(http.MethodGet, "/sidekiq/job_stats", nil, options) if err != nil { return nil, nil, err } j := new(JobStats) resp, err := s.client.Do(req, j) if err != nil { return nil, resp, err } return j, resp, nil } // CompoundMetrics represents the GitLab sidekiq compounded stats. // // GitLab API docs: // https://docs.gitlab.com/ee/api/sidekiq_metrics.html#get-a-compound-response-of-all-the-previously-mentioned-metrics type CompoundMetrics struct { QueueMetrics ProcessMetrics JobStats } // GetCompoundMetrics lists all the currently available information about Sidekiq. // Get a compound response of all the previously mentioned metrics // // GitLab API docs: https://docs.gitlab.com/ee/api/sidekiq_metrics.html#get-a-compound-response-of-all-the-previously-mentioned-metrics func (s *SidekiqService) GetCompoundMetrics(options ...RequestOptionFunc) (*CompoundMetrics, *Response, error) { req, err := s.client.NewRequest(http.MethodGet, "/sidekiq/compound_metrics", nil, options) if err != nil { return nil, nil, err } c := new(CompoundMetrics) resp, err := s.client.Do(req, c) if err != nil { return nil, resp, err } return c, resp, nil } golang-gitlab-gitlab-org-api-client-go-0.123.0/sidekiq_metrics_test.go000066400000000000000000000067031475761473200256320ustar00rootroot00000000000000package gitlab import ( "fmt" "net/http" "testing" "time" "github.com/stretchr/testify/require" ) func TestSidekiqService_GetQueueMetrics(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/sidekiq/queue_metrics", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprintf(w, `{"queues": {"default": {"backlog": 0,"latency": 0}}}`) }) qm, _, err := client.Sidekiq.GetQueueMetrics() require.NoError(t, err) want := &QueueMetrics{Queues: map[string]struct { Backlog int `json:"backlog"` Latency int `json:"latency"` }{"default": {Backlog: 0, Latency: 0}}} require.Equal(t, want, qm) } func TestSidekiqService_GetProcessMetrics(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/sidekiq/process_metrics", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, `{"processes": [{"hostname": "gitlab.example.com","pid": 5649,"tag": "gitlab","concurrency": 25,"busy": 0}]}`) }) pm, _, err := client.Sidekiq.GetProcessMetrics() require.NoError(t, err) want := &ProcessMetrics{[]struct { Hostname string `json:"hostname"` Pid int `json:"pid"` Tag string `json:"tag"` StartedAt *time.Time `json:"started_at"` Queues []string `json:"queues"` Labels []string `json:"labels"` Concurrency int `json:"concurrency"` Busy int `json:"busy"` }{{Hostname: "gitlab.example.com", Pid: 5649, Tag: "gitlab", Concurrency: 25, Busy: 0}}} require.Equal(t, want, pm) } func TestSidekiqService_GetJobStats(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/sidekiq/job_stats", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, `{"jobs": {"processed": 2,"failed": 0,"enqueued": 0}}`) }) js, _, err := client.Sidekiq.GetJobStats() require.NoError(t, err) want := &JobStats{struct { Processed int `json:"processed"` Failed int `json:"failed"` Enqueued int `json:"enqueued"` }(struct { Processed int Failed int Enqueued int }{Processed: 2, Failed: 0, Enqueued: 0})} require.Equal(t, want, js) } func TestSidekiqService_GetCompoundMetrics(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/sidekiq/compound_metrics", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) mustWriteHTTPResponse(t, w, "testdata/get_compound_metrics.json") }) cm, _, err := client.Sidekiq.GetCompoundMetrics() require.NoError(t, err) want := &CompoundMetrics{ QueueMetrics: QueueMetrics{Queues: map[string]struct { Backlog int `json:"backlog"` Latency int `json:"latency"` }{"default": { Backlog: 0, Latency: 0, }}}, ProcessMetrics: ProcessMetrics{Processes: []struct { Hostname string `json:"hostname"` Pid int `json:"pid"` Tag string `json:"tag"` StartedAt *time.Time `json:"started_at"` Queues []string `json:"queues"` Labels []string `json:"labels"` Concurrency int `json:"concurrency"` Busy int `json:"busy"` }{{Hostname: "gitlab.example.com", Pid: 5649, Tag: "gitlab", Concurrency: 25, Busy: 0}}}, JobStats: JobStats{Jobs: struct { Processed int `json:"processed"` Failed int `json:"failed"` Enqueued int `json:"enqueued"` }(struct { Processed int Failed int Enqueued int }{Processed: 2, Failed: 0, Enqueued: 0})}, } require.Equal(t, want, cm) } golang-gitlab-gitlab-org-api-client-go-0.123.0/snippet_repository_storage_move.go000066400000000000000000000172011475761473200301420ustar00rootroot00000000000000// // Copyright 2023, Nick Westbury // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "fmt" "net/http" "time" ) // SnippetRepositoryStorageMoveService handles communication with the // snippets related methods of the GitLab API. // // GitLab API docs: // https://docs.gitlab.com/ee/api/snippet_repository_storage_moves.html type SnippetRepositoryStorageMoveService struct { client *Client } // SnippetRepositoryStorageMove represents the status of a repository move. // // GitLab API docs: // https://docs.gitlab.com/ee/api/snippet_repository_storage_moves.html type SnippetRepositoryStorageMove struct { ID int `json:"id"` CreatedAt *time.Time `json:"created_at"` State string `json:"state"` SourceStorageName string `json:"source_storage_name"` DestinationStorageName string `json:"destination_storage_name"` Snippet *RepositorySnippet `json:"snippet"` } type RepositorySnippet struct { ID int `json:"id"` Title string `json:"title"` Description string `json:"description"` Visibility VisibilityValue `json:"visibility"` UpdatedAt *time.Time `json:"updated_at"` CreatedAt *time.Time `json:"created_at"` ProjectID int `json:"project_id"` WebURL string `json:"web_url"` RawURL string `json:"raw_url"` SSHURLToRepo string `json:"ssh_url_to_repo"` HTTPURLToRepo string `json:"http_url_to_repo"` } // RetrieveAllSnippetStorageMovesOptions represents the available // RetrieveAllStorageMoves() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/snippet_repository_storage_moves.html#retrieve-all-repository-storage-moves-for-a-snippet type RetrieveAllSnippetStorageMovesOptions ListOptions // RetrieveAllStorageMoves retrieves all snippet repository storage moves // accessible by the authenticated user. // // GitLab API docs: // https://docs.gitlab.com/ee/api/snippet_repository_storage_moves.html#retrieve-all-repository-storage-moves-for-a-snippet func (s SnippetRepositoryStorageMoveService) RetrieveAllStorageMoves(opts RetrieveAllSnippetStorageMovesOptions, options ...RequestOptionFunc) ([]*SnippetRepositoryStorageMove, *Response, error) { req, err := s.client.NewRequest(http.MethodGet, "snippet_repository_storage_moves", opts, options) if err != nil { return nil, nil, err } var ssms []*SnippetRepositoryStorageMove resp, err := s.client.Do(req, &ssms) if err != nil { return nil, resp, err } return ssms, resp, err } // RetrieveAllStorageMovesForSnippet retrieves all repository storage moves for // a single snippet accessible by the authenticated user. // // GitLab API docs: // https://docs.gitlab.com/ee/api/snippet_repository_storage_moves.html#retrieve-all-repository-storage-moves-for-a-snippet func (s SnippetRepositoryStorageMoveService) RetrieveAllStorageMovesForSnippet(snippet int, opts RetrieveAllSnippetStorageMovesOptions, options ...RequestOptionFunc) ([]*SnippetRepositoryStorageMove, *Response, error) { u := fmt.Sprintf("snippets/%d/repository_storage_moves", snippet) req, err := s.client.NewRequest(http.MethodGet, u, opts, options) if err != nil { return nil, nil, err } var ssms []*SnippetRepositoryStorageMove resp, err := s.client.Do(req, &ssms) if err != nil { return nil, resp, err } return ssms, resp, err } // GetStorageMove gets a single snippet repository storage move. // // GitLab API docs: // https://docs.gitlab.com/ee/api/snippet_repository_storage_moves.html#get-a-single-snippet-repository-storage-move func (s SnippetRepositoryStorageMoveService) GetStorageMove(repositoryStorage int, options ...RequestOptionFunc) (*SnippetRepositoryStorageMove, *Response, error) { u := fmt.Sprintf("snippet_repository_storage_moves/%d", repositoryStorage) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } ssm := new(SnippetRepositoryStorageMove) resp, err := s.client.Do(req, ssm) if err != nil { return nil, resp, err } return ssm, resp, err } // GetStorageMoveForSnippet gets a single repository storage move for a snippet. // // GitLab API docs: // https://docs.gitlab.com/ee/api/snippet_repository_storage_moves.html#get-a-single-repository-storage-move-for-a-snippet func (s SnippetRepositoryStorageMoveService) GetStorageMoveForSnippet(snippet int, repositoryStorage int, options ...RequestOptionFunc) (*SnippetRepositoryStorageMove, *Response, error) { u := fmt.Sprintf("snippets/%d/repository_storage_moves/%d", snippet, repositoryStorage) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } ssm := new(SnippetRepositoryStorageMove) resp, err := s.client.Do(req, ssm) if err != nil { return nil, resp, err } return ssm, resp, err } // ScheduleStorageMoveForSnippetOptions represents the available // ScheduleStorageMoveForSnippet() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/snippet_repository_storage_moves.html#schedule-a-repository-storage-move-for-a-snippet type ScheduleStorageMoveForSnippetOptions struct { DestinationStorageName *string `url:"destination_storage_name,omitempty" json:"destination_storage_name,omitempty"` } // ScheduleStorageMoveForSnippet schedule a repository to be moved for a snippet. // // GitLab API docs: // https://docs.gitlab.com/ee/api/snippet_repository_storage_moves.html#schedule-a-repository-storage-move-for-a-snippet func (s SnippetRepositoryStorageMoveService) ScheduleStorageMoveForSnippet(snippet int, opts ScheduleStorageMoveForSnippetOptions, options ...RequestOptionFunc) (*SnippetRepositoryStorageMove, *Response, error) { u := fmt.Sprintf("snippets/%d/repository_storage_moves", snippet) req, err := s.client.NewRequest(http.MethodPost, u, opts, options) if err != nil { return nil, nil, err } ssm := new(SnippetRepositoryStorageMove) resp, err := s.client.Do(req, ssm) if err != nil { return nil, resp, err } return ssm, resp, err } // ScheduleAllSnippetStorageMovesOptions represents the available // ScheduleAllStorageMoves() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/snippet_repository_storage_moves.html#schedule-repository-storage-moves-for-all-snippets-on-a-storage-shard type ScheduleAllSnippetStorageMovesOptions struct { SourceStorageName *string `url:"source_storage_name,omitempty" json:"source_storage_name,omitempty"` DestinationStorageName *string `url:"destination_storage_name,omitempty" json:"destination_storage_name,omitempty"` } // ScheduleAllStorageMoves schedules all snippet repositories to be moved. // // GitLab API docs: // https://docs.gitlab.com/ee/api/snippet_repository_storage_moves.html#schedule-repository-storage-moves-for-all-snippets-on-a-storage-shard func (s SnippetRepositoryStorageMoveService) ScheduleAllStorageMoves(opts ScheduleAllSnippetStorageMovesOptions, options ...RequestOptionFunc) (*Response, error) { req, err := s.client.NewRequest(http.MethodPost, "snippet_repository_storage_moves", opts, options) if err != nil { return nil, err } return s.client.Do(req, nil) } golang-gitlab-gitlab-org-api-client-go-0.123.0/snippet_repository_storage_move_test.go000066400000000000000000000111161475761473200312000ustar00rootroot00000000000000package gitlab import ( "fmt" "net/http" "testing" "github.com/stretchr/testify/require" ) func TestSnippetRepositoryStorageMove_RetrieveAllSnippetStorageMoves(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/snippet_repository_storage_moves", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, `[ { "id":123, "state":"scheduled", "snippet":{ "id":65, "title":"Test Snippet" } }, { "id":122, "state":"finished", "snippet":{ "id":64, "title":"Test Snippet 2" } }]`) }) opts := RetrieveAllSnippetStorageMovesOptions{Page: 1, PerPage: 2} ssms, _, err := client.SnippetRepositoryStorageMove.RetrieveAllStorageMoves(opts) require.NoError(t, err) want := []*SnippetRepositoryStorageMove{ { ID: 123, State: "scheduled", Snippet: &RepositorySnippet{ ID: 65, Title: "Test Snippet", }, }, { ID: 122, State: "finished", Snippet: &RepositorySnippet{ ID: 64, Title: "Test Snippet 2", }, }, } require.Equal(t, want, ssms) } func TestSnippetRepositoryStorageMove_RetrieveAllStorageMovesForSnippet(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/snippets/65/repository_storage_moves", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, `[ { "id":123, "state":"scheduled", "snippet":{ "id":65, "title":"Test Snippet" } }, { "id":122, "state":"finished", "snippet":{ "id":65, "title":"Test Snippet" } }]`) }) opts := RetrieveAllSnippetStorageMovesOptions{Page: 1, PerPage: 2} ssms, _, err := client.SnippetRepositoryStorageMove.RetrieveAllStorageMovesForSnippet(65, opts) require.NoError(t, err) want := []*SnippetRepositoryStorageMove{ { ID: 123, State: "scheduled", Snippet: &RepositorySnippet{ ID: 65, Title: "Test Snippet", }, }, { ID: 122, State: "finished", Snippet: &RepositorySnippet{ ID: 65, Title: "Test Snippet", }, }, } require.Equal(t, want, ssms) } func TestSnippetRepositoryStorageMove_GetStorageMove(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/snippet_repository_storage_moves/123", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, ` { "id":123, "state":"scheduled", "snippet":{ "id":65, "title":"Test Snippet" } }`) }) ssm, _, err := client.SnippetRepositoryStorageMove.GetStorageMove(123) require.NoError(t, err) want := &SnippetRepositoryStorageMove{ ID: 123, State: "scheduled", Snippet: &RepositorySnippet{ ID: 65, Title: "Test Snippet", }, } require.Equal(t, want, ssm) } func TestSnippetRepositoryStorageMove_GetStorageMoveForSnippet(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/snippets/65/repository_storage_moves/123", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, ` { "id":123, "state":"scheduled", "snippet":{ "id":65, "title":"Test Snippet" } }`) }) ssm, _, err := client.SnippetRepositoryStorageMove.GetStorageMoveForSnippet(65, 123) require.NoError(t, err) want := &SnippetRepositoryStorageMove{ ID: 123, State: "scheduled", Snippet: &RepositorySnippet{ ID: 65, Title: "Test Snippet", }, } require.Equal(t, want, ssm) } func TestSnippetRepositoryStorageMove_ScheduleStorageMoveForSnippet(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/snippets/65/repository_storage_moves", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) fmt.Fprint(w, ` { "id":124, "state":"scheduled", "snippet":{ "id":65, "title":"Test Snippet" } }`) }) ssm, _, err := client.SnippetRepositoryStorageMove.ScheduleStorageMoveForSnippet(65, ScheduleStorageMoveForSnippetOptions{}) require.NoError(t, err) want := &SnippetRepositoryStorageMove{ ID: 124, State: "scheduled", Snippet: &RepositorySnippet{ ID: 65, Title: "Test Snippet", }, } require.Equal(t, want, ssm) } func TestSnippetRepositoryStorageMove_ScheduleAllStorageMoves(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/snippet_repository_storage_moves", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) fmt.Fprint(w, `{"message": "202 Accepted"}`) }) _, err := client.SnippetRepositoryStorageMove.ScheduleAllStorageMoves( ScheduleAllSnippetStorageMovesOptions{ SourceStorageName: Ptr("default"), }, ) require.NoError(t, err) } golang-gitlab-gitlab-org-api-client-go-0.123.0/snippets.go000066400000000000000000000237711475761473200232650ustar00rootroot00000000000000// // Copyright 2021, Sander van Harmelen // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "bytes" "fmt" "net/http" "time" ) // SnippetsService handles communication with the snippets // related methods of the GitLab API. // // GitLab API docs: https://docs.gitlab.com/ee/api/snippets.html type SnippetsService struct { client *Client } // Snippet represents a GitLab snippet. // // GitLab API docs: https://docs.gitlab.com/ee/api/snippets.html type Snippet struct { ID int `json:"id"` Title string `json:"title"` FileName string `json:"file_name"` Description string `json:"description"` Visibility string `json:"visibility"` Author struct { ID int `json:"id"` Username string `json:"username"` Email string `json:"email"` Name string `json:"name"` State string `json:"state"` CreatedAt *time.Time `json:"created_at"` } `json:"author"` UpdatedAt *time.Time `json:"updated_at"` CreatedAt *time.Time `json:"created_at"` ProjectID int `json:"project_id"` WebURL string `json:"web_url"` RawURL string `json:"raw_url"` Files []struct { Path string `json:"path"` RawURL string `json:"raw_url"` } `json:"files"` RepositoryStorage string `json:"repository_storage"` } func (s Snippet) String() string { return Stringify(s) } // ListSnippetsOptions represents the available ListSnippets() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/snippets.html#list-all-snippets-for-a-user type ListSnippetsOptions ListOptions // ListSnippets gets a list of snippets. // // GitLab API docs: // https://docs.gitlab.com/ee/api/snippets.html#list-all-snippets-for-a-user func (s *SnippetsService) ListSnippets(opt *ListSnippetsOptions, options ...RequestOptionFunc) ([]*Snippet, *Response, error) { req, err := s.client.NewRequest(http.MethodGet, "snippets", opt, options) if err != nil { return nil, nil, err } var ps []*Snippet resp, err := s.client.Do(req, &ps) if err != nil { return nil, resp, err } return ps, resp, nil } // GetSnippet gets a single snippet // // GitLab API docs: // https://docs.gitlab.com/ee/api/snippets.html#get-a-single-snippet func (s *SnippetsService) GetSnippet(snippet int, options ...RequestOptionFunc) (*Snippet, *Response, error) { u := fmt.Sprintf("snippets/%d", snippet) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } ps := new(Snippet) resp, err := s.client.Do(req, ps) if err != nil { return nil, resp, err } return ps, resp, nil } // SnippetContent gets a single snippet’s raw contents. // // GitLab API docs: // https://docs.gitlab.com/ee/api/snippets.html#single-snippet-contents func (s *SnippetsService) SnippetContent(snippet int, options ...RequestOptionFunc) ([]byte, *Response, error) { u := fmt.Sprintf("snippets/%d/raw", snippet) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } var b bytes.Buffer resp, err := s.client.Do(req, &b) if err != nil { return nil, resp, err } return b.Bytes(), resp, err } // SnippetFileContent returns the raw file content as plain text. // // GitLab API docs: // https://docs.gitlab.com/ee/api/snippets.html#snippet-repository-file-content func (s *SnippetsService) SnippetFileContent(snippet int, ref, filename string, options ...RequestOptionFunc) ([]byte, *Response, error) { filepath := PathEscape(filename) u := fmt.Sprintf("snippets/%d/files/%s/%s/raw", snippet, ref, filepath) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } var b bytes.Buffer resp, err := s.client.Do(req, &b) if err != nil { return nil, resp, err } return b.Bytes(), resp, err } // CreateSnippetFileOptions represents the create snippet file options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/snippets.html#create-new-snippet type CreateSnippetFileOptions struct { FilePath *string `url:"file_path,omitempty" json:"file_path,omitempty"` Content *string `url:"content,omitempty" json:"content,omitempty"` } // CreateSnippetOptions represents the available CreateSnippet() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/snippets.html#create-new-snippet type CreateSnippetOptions struct { Title *string `url:"title,omitempty" json:"title,omitempty"` FileName *string `url:"file_name,omitempty" json:"file_name,omitempty"` Description *string `url:"description,omitempty" json:"description,omitempty"` Content *string `url:"content,omitempty" json:"content,omitempty"` Visibility *VisibilityValue `url:"visibility,omitempty" json:"visibility,omitempty"` Files *[]*CreateSnippetFileOptions `url:"files,omitempty" json:"files,omitempty"` } // CreateSnippet creates a new snippet. The user must have permission // to create new snippets. // // GitLab API docs: // https://docs.gitlab.com/ee/api/snippets.html#create-new-snippet func (s *SnippetsService) CreateSnippet(opt *CreateSnippetOptions, options ...RequestOptionFunc) (*Snippet, *Response, error) { req, err := s.client.NewRequest(http.MethodPost, "snippets", opt, options) if err != nil { return nil, nil, err } ps := new(Snippet) resp, err := s.client.Do(req, ps) if err != nil { return nil, resp, err } return ps, resp, nil } // UpdateSnippetFileOptions represents the update snippet file options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/snippets.html#update-snippet type UpdateSnippetFileOptions struct { Action *string `url:"action,omitempty" json:"action,omitempty"` FilePath *string `url:"file_path,omitempty" json:"file_path,omitempty"` Content *string `url:"content,omitempty" json:"content,omitempty"` PreviousPath *string `url:"previous_path,omitempty" json:"previous_path,omitempty"` } // UpdateSnippetOptions represents the available UpdateSnippet() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/snippets.html#update-snippet type UpdateSnippetOptions struct { Title *string `url:"title,omitempty" json:"title,omitempty"` FileName *string `url:"file_name,omitempty" json:"file_name,omitempty"` Description *string `url:"description,omitempty" json:"description,omitempty"` Content *string `url:"content,omitempty" json:"content,omitempty"` Visibility *VisibilityValue `url:"visibility,omitempty" json:"visibility,omitempty"` Files *[]*UpdateSnippetFileOptions `url:"files,omitempty" json:"files,omitempty"` } // UpdateSnippet updates an existing snippet. The user must have // permission to change an existing snippet. // // GitLab API docs: // https://docs.gitlab.com/ee/api/snippets.html#update-snippet func (s *SnippetsService) UpdateSnippet(snippet int, opt *UpdateSnippetOptions, options ...RequestOptionFunc) (*Snippet, *Response, error) { u := fmt.Sprintf("snippets/%d", snippet) req, err := s.client.NewRequest(http.MethodPut, u, opt, options) if err != nil { return nil, nil, err } ps := new(Snippet) resp, err := s.client.Do(req, ps) if err != nil { return nil, resp, err } return ps, resp, nil } // DeleteSnippet deletes an existing snippet. This is an idempotent // function and deleting a non-existent snippet still returns a 200 OK status // code. // // GitLab API docs: // https://docs.gitlab.com/ee/api/snippets.html#delete-snippet func (s *SnippetsService) DeleteSnippet(snippet int, options ...RequestOptionFunc) (*Response, error) { u := fmt.Sprintf("snippets/%d", snippet) req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } // ExploreSnippetsOptions represents the available ExploreSnippets() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/snippets.html#list-all-public-snippets type ExploreSnippetsOptions ListOptions // ExploreSnippets gets the list of public snippets. // // GitLab API docs: // https://docs.gitlab.com/ee/api/snippets.html#list-all-public-snippets func (s *SnippetsService) ExploreSnippets(opt *ExploreSnippetsOptions, options ...RequestOptionFunc) ([]*Snippet, *Response, error) { req, err := s.client.NewRequest(http.MethodGet, "snippets/public", opt, options) if err != nil { return nil, nil, err } var ps []*Snippet resp, err := s.client.Do(req, &ps) if err != nil { return nil, resp, err } return ps, resp, nil } // ListAllSnippetsOptions represents the available ListAllSnippets() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/snippets.html#list-all-snippets type ListAllSnippetsOptions struct { ListOptions CreatedAfter *ISOTime `url:"created_after,omitempty" json:"created_after,omitempty"` CreatedBefore *ISOTime `url:"created_before,omitempty" json:"created_before,omitempty"` RepositoryStorage *string `url:"repository_storage,omitempty" json:"repository_storage,omitempty"` } // ListAllSnippets gets all snippets the current user has access to. // // GitLab API docs: // https://docs.gitlab.com/ee/api/snippets.html#list-all-snippets func (s *SnippetsService) ListAllSnippets(opt *ListAllSnippetsOptions, options ...RequestOptionFunc) ([]*Snippet, *Response, error) { req, err := s.client.NewRequest(http.MethodGet, "snippets/all", opt, options) if err != nil { return nil, nil, err } var ps []*Snippet resp, err := s.client.Do(req, &ps) if err != nil { return nil, resp, err } return ps, resp, nil } golang-gitlab-gitlab-org-api-client-go-0.123.0/snippets_test.go000066400000000000000000000074421475761473200243210ustar00rootroot00000000000000package gitlab import ( "fmt" "net/http" "testing" "github.com/stretchr/testify/require" ) func TestSnippetsService_ListSnippets(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/snippets", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, `[{"id":42,"title":"test"}]`) }) opt := &ListSnippetsOptions{Page: 1, PerPage: 10} ss, _, err := client.Snippets.ListSnippets(opt) require.NoError(t, err) want := []*Snippet{{ID: 42, Title: "test"}} require.Equal(t, want, ss) } func TestSnippetsService_GetSnippet(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/snippets/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, `{"id":1, "title":"test"}`) }) s, _, err := client.Snippets.GetSnippet(1) require.NoError(t, err) want := &Snippet{ID: 1, Title: "test"} require.Equal(t, want, s) } func TestSnippetsService_CreateSnippet(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/snippets", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) fmt.Fprint(w, `{"id":1, "title":"test"}`) }) opt := &CreateSnippetOptions{ Title: Ptr("test"), FileName: Ptr("file"), Description: Ptr("description"), Content: Ptr("content"), Visibility: Ptr(PublicVisibility), } s, _, err := client.Snippets.CreateSnippet(opt) require.NoError(t, err) want := &Snippet{ID: 1, Title: "test"} require.Equal(t, want, s) } func TestSnippetsService_UpdateSnippet(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/snippets/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPut) fmt.Fprint(w, `{"id":1, "title":"test"}`) }) opt := &UpdateSnippetOptions{ Title: Ptr("test"), FileName: Ptr("file"), Description: Ptr("description"), Content: Ptr("content"), Visibility: Ptr(PublicVisibility), } s, _, err := client.Snippets.UpdateSnippet(1, opt) require.NoError(t, err) want := &Snippet{ID: 1, Title: "test"} require.Equal(t, want, s) } func TestSnippetsService_DeleteSnippet(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/snippets/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodDelete) w.WriteHeader(http.StatusNoContent) }) _, err := client.Snippets.DeleteSnippet(1) require.NoError(t, err) } func TestSnippetsService_SnippetContent(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/snippets/1/raw", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, "Hello World") }) b, _, err := client.Snippets.SnippetContent(1) require.NoError(t, err) want := []byte("Hello World") require.Equal(t, want, b) } func TestSnippetsService_ExploreSnippets(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/snippets/public", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, `[{"id":42,"title":"test"}]`) }) opt := &ExploreSnippetsOptions{Page: 1, PerPage: 10} ss, _, err := client.Snippets.ExploreSnippets(opt) require.NoError(t, err) want := []*Snippet{{ID: 42, Title: "test"}} require.Equal(t, want, ss) } func TestSnippetsService_ListAllSnippets(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/snippets/all", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, `[{"id":113,"title":"Internal Snippet"}, {"id":114,"title":"Private Snippet"}]`) }) opt := &ListAllSnippetsOptions{ListOptions: ListOptions{Page: 1, PerPage: 10}} ss, _, err := client.Snippets.ListAllSnippets(opt) require.NoError(t, err) want := []*Snippet{{ID: 113, Title: "Internal Snippet"}, {ID: 114, Title: "Private Snippet"}} require.Equal(t, want, ss) } golang-gitlab-gitlab-org-api-client-go-0.123.0/strings.go000066400000000000000000000040571475761473200231050ustar00rootroot00000000000000// // Copyright 2021, Sander van Harmelen // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "bytes" "fmt" "reflect" ) // Stringify attempts to create a reasonable string representation of types in // the Gitlab 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 v := reflect.ValueOf(message) stringifyValue(&buf, v) return buf.String() } // stringifyValue was heavily inspired by the goprotobuf library. func stringifyValue(buf *bytes.Buffer, val reflect.Value) { if val.Kind() == reflect.Ptr && val.IsNil() { buf.WriteString("") return } v := reflect.Indirect(val) switch v.Kind() { case reflect.String: fmt.Fprintf(buf, `"%s"`, v) case reflect.Slice: buf.WriteByte('[') for i := 0; i < v.Len(); i++ { if i > 0 { buf.WriteByte(' ') } stringifyValue(buf, v.Index(i)) } buf.WriteByte(']') return case reflect.Struct: if v.Type().Name() != "" { buf.WriteString(v.Type().String()) } buf.WriteByte('{') var sep bool for i := 0; i < v.NumField(); i++ { fv := v.Field(i) if fv.Kind() == reflect.Ptr && fv.IsNil() { continue } if fv.Kind() == reflect.Slice && fv.IsNil() { continue } if sep { buf.WriteString(", ") } else { sep = true } buf.WriteString(v.Type().Field(i).Name) buf.WriteByte(':') stringifyValue(buf, fv) } buf.WriteByte('}') default: if v.CanInterface() { fmt.Fprint(buf, v.Interface()) } } } golang-gitlab-gitlab-org-api-client-go-0.123.0/strings_test.go000066400000000000000000000021101475761473200241300ustar00rootroot00000000000000package gitlab import ( "testing" "github.com/stretchr/testify/assert" ) type Person struct { Name string Age int NickNames []string Address Address Company *Company } type Address struct { Street string City string Province string Country string } type Company struct { Name string Address Address Country string } func TestStringify_nil(t *testing.T) { var person *Person resp := Stringify(person) assert.Equal(t, "", resp) } func TestStringify(t *testing.T) { person := &Person{"name", 16, []string{"n", "a", "m", "e"}, Address{}, nil} resp := Stringify(person) want := "gitlab.Person{Name:\"name\", Age:16, NickNames:[\"n\" \"a\" \"m\" \"e\"], Address:gitlab.Address{Street:\"\", City:\"\", Province:\"\", Country:\"\"}}" assert.Equal(t, want, resp) } func TestStringify_emptySlice(t *testing.T) { person := &Person{"name", 16, nil, Address{}, nil} resp := Stringify(person) want := "gitlab.Person{Name:\"name\", Age:16, Address:gitlab.Address{Street:\"\", City:\"\", Province:\"\", Country:\"\"}}" assert.Equal(t, want, resp) } golang-gitlab-gitlab-org-api-client-go-0.123.0/system_hooks.go000066400000000000000000000124141475761473200241370ustar00rootroot00000000000000// // Copyright 2021, Sander van Harmelen // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "fmt" "net/http" "time" ) // SystemHooksService handles communication with the system hooks related // methods of the GitLab API. // // GitLab API docs: https://docs.gitlab.com/ee/api/system_hooks.html type SystemHooksService struct { client *Client } // Hook represents a GitLap system hook. // // GitLab API docs: https://docs.gitlab.com/ee/api/system_hooks.html type Hook struct { ID int `json:"id"` URL string `json:"url"` CreatedAt *time.Time `json:"created_at"` PushEvents bool `json:"push_events"` TagPushEvents bool `json:"tag_push_events"` MergeRequestsEvents bool `json:"merge_requests_events"` RepositoryUpdateEvents bool `json:"repository_update_events"` EnableSSLVerification bool `json:"enable_ssl_verification"` } func (h Hook) String() string { return Stringify(h) } // ListHooks gets a list of system hooks. // // GitLab API docs: // https://docs.gitlab.com/ee/api/system_hooks.html#list-system-hooks func (s *SystemHooksService) ListHooks(options ...RequestOptionFunc) ([]*Hook, *Response, error) { req, err := s.client.NewRequest(http.MethodGet, "hooks", nil, options) if err != nil { return nil, nil, err } var h []*Hook resp, err := s.client.Do(req, &h) if err != nil { return nil, resp, err } return h, resp, nil } // GetHook get a single system hook. // // GitLab API docs: // https://docs.gitlab.com/ee/api/system_hooks.html#get-system-hook func (s *SystemHooksService) GetHook(hook int, options ...RequestOptionFunc) (*Hook, *Response, error) { u := fmt.Sprintf("hooks/%d", hook) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } var h *Hook resp, err := s.client.Do(req, &h) if err != nil { return nil, resp, err } return h, resp, nil } // AddHookOptions represents the available AddHook() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/system_hooks.html#add-new-system-hook type AddHookOptions struct { URL *string `url:"url,omitempty" json:"url,omitempty"` Token *string `url:"token,omitempty" json:"token,omitempty"` PushEvents *bool `url:"push_events,omitempty" json:"push_events,omitempty"` TagPushEvents *bool `url:"tag_push_events,omitempty" json:"tag_push_events,omitempty"` MergeRequestsEvents *bool `url:"merge_requests_events,omitempty" json:"merge_requests_events,omitempty"` RepositoryUpdateEvents *bool `url:"repository_update_events,omitempty" json:"repository_update_events,omitempty"` EnableSSLVerification *bool `url:"enable_ssl_verification,omitempty" json:"enable_ssl_verification,omitempty"` } // AddHook adds a new system hook hook. // // GitLab API docs: // https://docs.gitlab.com/ee/api/system_hooks.html#add-new-system-hook func (s *SystemHooksService) AddHook(opt *AddHookOptions, options ...RequestOptionFunc) (*Hook, *Response, error) { req, err := s.client.NewRequest(http.MethodPost, "hooks", opt, options) if err != nil { return nil, nil, err } h := new(Hook) resp, err := s.client.Do(req, h) if err != nil { return nil, resp, err } return h, resp, nil } // HookEvent represents an event trigger by a GitLab system hook. // // GitLab API docs: https://docs.gitlab.com/ee/api/system_hooks.html type HookEvent struct { EventName string `json:"event_name"` Name string `json:"name"` Path string `json:"path"` ProjectID int `json:"project_id"` OwnerName string `json:"owner_name"` OwnerEmail string `json:"owner_email"` } func (h HookEvent) String() string { return Stringify(h) } // TestHook tests a system hook. // // GitLab API docs: // https://docs.gitlab.com/ee/api/system_hooks.html#test-system-hook func (s *SystemHooksService) TestHook(hook int, options ...RequestOptionFunc) (*HookEvent, *Response, error) { u := fmt.Sprintf("hooks/%d", hook) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } h := new(HookEvent) resp, err := s.client.Do(req, h) if err != nil { return nil, resp, err } return h, resp, nil } // DeleteHook deletes a system hook. This is an idempotent API function and // returns 200 OK even if the hook is not available. If the hook is deleted it // is also returned as JSON. // // GitLab API docs: // https://docs.gitlab.com/ee/api/system_hooks.html#delete-system-hook func (s *SystemHooksService) DeleteHook(hook int, options ...RequestOptionFunc) (*Response, error) { u := fmt.Sprintf("hooks/%d", hook) req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } golang-gitlab-gitlab-org-api-client-go-0.123.0/system_hooks_test.go000066400000000000000000000067471475761473200252120ustar00rootroot00000000000000package gitlab import ( "fmt" "net/http" "testing" "time" "github.com/stretchr/testify/require" ) func TestSystemHooksService_ListHooks(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/hooks", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, ` [ { "id":1, "url":"https://gitlab.example.com/hook", "created_at":"2016-10-31T12:32:15.192Z", "push_events":true, "tag_push_events":false, "merge_requests_events": true, "repository_update_events": true, "enable_ssl_verification":true } ]`) }) hooks, _, err := client.SystemHooks.ListHooks() require.NoError(t, err) createdAt := time.Date(2016, 10, 31, 12, 32, 15, 192000000, time.UTC) want := []*Hook{{ ID: 1, URL: "https://gitlab.example.com/hook", CreatedAt: &createdAt, PushEvents: true, TagPushEvents: false, MergeRequestsEvents: true, RepositoryUpdateEvents: true, EnableSSLVerification: true, }} require.Equal(t, want, hooks) } func TestSystemHooksService_GetHook(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/hooks/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, ` { "id":1, "url":"https://gitlab.example.com/hook", "created_at":"2016-10-31T12:32:15.192Z", "push_events":true, "tag_push_events":false, "merge_requests_events": true, "repository_update_events": true, "enable_ssl_verification":true }`) }) hooks, _, err := client.SystemHooks.GetHook(1) require.NoError(t, err) createdAt := time.Date(2016, 10, 31, 12, 32, 15, 192000000, time.UTC) want := &Hook{ ID: 1, URL: "https://gitlab.example.com/hook", CreatedAt: &createdAt, PushEvents: true, TagPushEvents: false, MergeRequestsEvents: true, RepositoryUpdateEvents: true, EnableSSLVerification: true, } require.Equal(t, want, hooks) } func TestSystemHooksService_AddHook(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/hooks", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) fmt.Fprint(w, `{"id": 1, "url": "https://gitlab.example.com/hook"}`) }) opt := &AddHookOptions{ URL: Ptr("https://gitlab.example.com/hook"), } hook, _, err := client.SystemHooks.AddHook(opt) require.NoError(t, err) want := &Hook{ID: 1, URL: "https://gitlab.example.com/hook", CreatedAt: (*time.Time)(nil)} require.Equal(t, want, hook) } func TestSystemHooksService_TestHook(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/hooks/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, `{"project_id" : 1,"owner_email" : "example@gitlabhq.com","owner_name" : "Someone", "name" : "Ruby","path" : "ruby","event_name" : "project_create"}`) }) hook, _, err := client.SystemHooks.TestHook(1) require.NoError(t, err) want := &HookEvent{ EventName: "project_create", Name: "Ruby", Path: "ruby", ProjectID: 1, OwnerName: "Someone", OwnerEmail: "example@gitlabhq.com", } require.Equal(t, want, hook) } func TestSystemHooksService_DeleteHook(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/hooks/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodDelete) }) _, err := client.SystemHooks.DeleteHook(1) require.NoError(t, err) } golang-gitlab-gitlab-org-api-client-go-0.123.0/tags.go000066400000000000000000000164521475761473200223540ustar00rootroot00000000000000// // Copyright 2021, Sander van Harmelen // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "fmt" "net/http" "net/url" ) // TagsService handles communication with the tags related methods // of the GitLab API. // // GitLab API docs: https://docs.gitlab.com/ee/api/tags.html type TagsService struct { client *Client } // Tag represents a GitLab tag. // // GitLab API docs: https://docs.gitlab.com/ee/api/tags.html type Tag struct { Commit *Commit `json:"commit"` Release *ReleaseNote `json:"release"` Name string `json:"name"` Message string `json:"message"` Protected bool `json:"protected"` Target string `json:"target"` } // ReleaseNote represents a GitLab version release. // // GitLab API docs: https://docs.gitlab.com/ee/api/tags.html type ReleaseNote struct { TagName string `json:"tag_name"` Description string `json:"description"` } func (t Tag) String() string { return Stringify(t) } // ListTagsOptions represents the available ListTags() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/tags.html#list-project-repository-tags type ListTagsOptions struct { ListOptions OrderBy *string `url:"order_by,omitempty" json:"order_by,omitempty"` Search *string `url:"search,omitempty" json:"search,omitempty"` Sort *string `url:"sort,omitempty" json:"sort,omitempty"` } // ListTags gets a list of tags from a project, sorted by name in reverse // alphabetical order. // // GitLab API docs: // https://docs.gitlab.com/ee/api/tags.html#list-project-repository-tags func (s *TagsService) ListTags(pid interface{}, opt *ListTagsOptions, options ...RequestOptionFunc) ([]*Tag, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/repository/tags", PathEscape(project)) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } var t []*Tag resp, err := s.client.Do(req, &t) if err != nil { return nil, resp, err } return t, resp, nil } // GetTag a specific repository tag determined by its name. It returns 200 together // with the tag information if the tag exists. It returns 404 if the tag does not exist. // // GitLab API docs: // https://docs.gitlab.com/ee/api/tags.html#get-a-single-repository-tag func (s *TagsService) GetTag(pid interface{}, tag string, options ...RequestOptionFunc) (*Tag, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/repository/tags/%s", PathEscape(project), url.PathEscape(tag)) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } var t *Tag resp, err := s.client.Do(req, &t) if err != nil { return nil, resp, err } return t, resp, nil } // CreateTagOptions represents the available CreateTag() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/tags.html#create-a-new-tag type CreateTagOptions struct { TagName *string `url:"tag_name,omitempty" json:"tag_name,omitempty"` Ref *string `url:"ref,omitempty" json:"ref,omitempty"` Message *string `url:"message,omitempty" json:"message,omitempty"` // Deprecated: Use the Releases API instead. (Deprecated in GitLab 11.7) ReleaseDescription *string `url:"release_description:omitempty" json:"release_description,omitempty"` } // CreateTag creates a new tag in the repository that points to the supplied ref. // // GitLab API docs: // https://docs.gitlab.com/ee/api/tags.html#create-a-new-tag func (s *TagsService) CreateTag(pid interface{}, opt *CreateTagOptions, options ...RequestOptionFunc) (*Tag, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/repository/tags", PathEscape(project)) req, err := s.client.NewRequest(http.MethodPost, u, opt, options) if err != nil { return nil, nil, err } t := new(Tag) resp, err := s.client.Do(req, t) if err != nil { return nil, resp, err } return t, resp, nil } // DeleteTag deletes a tag of a repository with given name. // // GitLab API docs: // https://docs.gitlab.com/ee/api/tags.html#delete-a-tag func (s *TagsService) DeleteTag(pid interface{}, tag string, options ...RequestOptionFunc) (*Response, error) { project, err := parseID(pid) if err != nil { return nil, err } u := fmt.Sprintf("projects/%s/repository/tags/%s", PathEscape(project), url.PathEscape(tag)) req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } // CreateReleaseNoteOptions represents the available CreateReleaseNote() options. // // Deprecated: This feature was deprecated in GitLab 11.7. // // GitLab API docs: // https://docs.gitlab.com/ee/api/tags.html#create-a-new-release type CreateReleaseNoteOptions struct { Description *string `url:"description:omitempty" json:"description,omitempty"` } // CreateReleaseNote Add release notes to the existing git tag. // If there already exists a release for the given tag, status code 409 is returned. // // Deprecated: This feature was deprecated in GitLab 11.7. // // GitLab API docs: // https://docs.gitlab.com/ee/api/tags.html#create-a-new-release func (s *TagsService) CreateReleaseNote(pid interface{}, tag string, opt *CreateReleaseNoteOptions, options ...RequestOptionFunc) (*ReleaseNote, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/repository/tags/%s/release", PathEscape(project), url.PathEscape(tag)) req, err := s.client.NewRequest(http.MethodPost, u, opt, options) if err != nil { return nil, nil, err } r := new(ReleaseNote) resp, err := s.client.Do(req, r) if err != nil { return nil, resp, err } return r, resp, nil } // UpdateReleaseNoteOptions represents the available UpdateReleaseNote() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/tags.html#update-a-release type UpdateReleaseNoteOptions struct { Description *string `url:"description:omitempty" json:"description,omitempty"` } // UpdateReleaseNote Updates the release notes of a given release. // // Deprecated: This feature was deprecated in GitLab 11.7. // // GitLab API docs: // https://docs.gitlab.com/ee/api/tags.html#update-a-release func (s *TagsService) UpdateReleaseNote(pid interface{}, tag string, opt *UpdateReleaseNoteOptions, options ...RequestOptionFunc) (*ReleaseNote, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/repository/tags/%s/release", PathEscape(project), url.PathEscape(tag)) req, err := s.client.NewRequest(http.MethodPut, u, opt, options) if err != nil { return nil, nil, err } r := new(ReleaseNote) resp, err := s.client.Do(req, r) if err != nil { return nil, resp, err } return r, resp, nil } golang-gitlab-gitlab-org-api-client-go-0.123.0/tags_test.go000066400000000000000000000057411475761473200234120ustar00rootroot00000000000000// // Copyright 2021, Sander van Harmelen // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "fmt" "net/http" "reflect" "testing" ) func TestTagsService_ListTags(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/repository/tags", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, `[ { "name": "1.0.0", "message": "test", "target": "fffff", "protected": false },{ "name": "1.0.1", "protected": true } ]`) }) opt := &ListTagsOptions{ListOptions: ListOptions{Page: 2, PerPage: 3}} tags, _, err := client.Tags.ListTags(1, opt) if err != nil { t.Errorf("Tags.ListTags returned error: %v", err) } want := []*Tag{ { Name: "1.0.0", Message: "test", Target: "fffff", Protected: false, }, { Name: "1.0.1", Protected: true, }, } if !reflect.DeepEqual(want, tags) { t.Errorf("Tags.ListTags returned %+v, want %+v", tags, want) } } func TestTagsService_CreateReleaseNote(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/repository/tags/1.0.0/release", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) fmt.Fprint(w, `{"tag_name": "1.0.0", "description": "Amazing release. Wow"}`) }) opt := &CreateReleaseNoteOptions{Description: Ptr("Amazing release. Wow")} release, _, err := client.Tags.CreateReleaseNote(1, "1.0.0", opt) if err != nil { t.Errorf("Tags.CreateRelease returned error: %v", err) } want := &ReleaseNote{TagName: "1.0.0", Description: "Amazing release. Wow"} if !reflect.DeepEqual(want, release) { t.Errorf("Tags.CreateRelease returned %+v, want %+v", release, want) } } func TestTagsService_UpdateReleaseNote(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/repository/tags/1.0.0/release", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPut) fmt.Fprint(w, `{"tag_name": "1.0.0", "description": "Amazing release. Wow!"}`) }) opt := &UpdateReleaseNoteOptions{Description: Ptr("Amazing release. Wow!")} release, _, err := client.Tags.UpdateReleaseNote(1, "1.0.0", opt) if err != nil { t.Errorf("Tags.UpdateRelease returned error: %v", err) } want := &ReleaseNote{TagName: "1.0.0", Description: "Amazing release. Wow!"} if !reflect.DeepEqual(want, release) { t.Errorf("Tags.UpdateRelease returned %+v, want %+v", release, want) } } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/000077500000000000000000000000001475761473200226705ustar00rootroot00000000000000golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/add_merge_request_in_merge_train.json000066400000000000000000000022761475761473200323130ustar00rootroot00000000000000[ { "id": 267, "merge_request": { "id": 273, "iid": 1, "project_id": 597, "title": "My title 9", "description": null, "state": "opened", "created_at": "2022-10-31T19:06:05.725Z", "updated_at": "2022-10-31T19:06:05.725Z", "web_url": "http://localhost/namespace18/project21/-/merge_requests/1" }, "user": { "id": 933, "username": "user12", "name": "Sidney Jones31", "state": "active", "avatar_url": "https://www.gravatar.com/avatar/6c8365de387cb3db10ecc7b1880203c4?s=80\u0026d=identicon", "web_url": "http://localhost/user12" }, "pipeline": { "id": 273, "iid": 1, "project_id": 598, "sha": "b83d6e391c22777fca1ed3012fce84f633d7fed0", "ref": "main", "status": "pending", "source": "push", "created_at": "2022-10-31T19:06:06.231Z", "updated_at": "2022-10-31T19:06:06.231Z", "web_url": "http://localhost/namespace19/project22/-/pipelines/273" }, "created_at": "2022-10-31T19:06:06.237Z", "updated_at": "2022-10-31T19:06:06.237Z", "target_branch": "main", "status": "idle", "merged_at": null, "duration": null } ] golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/add_project_managed_license.json000066400000000000000000000001021475761473200312100ustar00rootroot00000000000000{ "id": 123, "name": "MIT", "approval_status": "approved" } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/create_dependency_list_export.json000066400000000000000000000003151475761473200316570ustar00rootroot00000000000000{ "id": 5678, "has_finished": false, "self": "http://gitlab.example.com/api/v4/dependency_list_exports/5678", "download": "http://gitlab.example.com/api/v4/dependency_list_exports/5678/download" } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/create_draft_note.json000066400000000000000000000003461475761473200272360ustar00rootroot00000000000000{ "id": 37349980, "author_id": 10271899, "merge_request_id": 291473309, "resolve_discussion": false, "discussion_id": null, "note": "Some new draft note", "commit_id": null, "position": null, "line_code": null } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/create_group_access_token.json000066400000000000000000000004431475761473200307640ustar00rootroot00000000000000{ "id": 1876, "name": "token 10", "revoked": false, "created_at": "2021-03-09T21:11:47.271Z", "scopes": ["api", "read_api", "read_repository", "write_repository"], "user_id": 2453, "active": true, "expires_at": null, "token": "2UsevZE1x1ZdFZW4MNzH", "access_level": 40 } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/create_group_ssh_certificates.json000066400000000000000000000002131475761473200316400ustar00rootroot00000000000000{ "id": 1876, "title": "SSH Certificate", "key": "ssh-rsa FAKE-KEY example@gitlab.com", "created_at": "2022-03-20T20:42:40.221Z" } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/create_member_role.json000066400000000000000000000013731475761473200274020ustar00rootroot00000000000000{ "id": 3, "name": "Custom guest", "description": "a sample custom role", "group_id": 84, "base_access_level": 10, "admin_cicd_variables": false, "admin_compliance_framework": false, "admin_group_member": false, "admin_merge_request": false, "admin_push_rules": false, "admin_terraform_state": false, "admin_vulnerability": false, "admin_web_hook": false, "archive_project": false, "manage_deploy_tokens": false, "manage_group_access_tokens": false, "manage_merge_request_settings": false, "manage_project_access_tokens": false, "manage_security_policy_link": false, "read_code": true, "read_runners": false, "read_dependency": false, "read_vulnerability": false, "remove_group": false, "remove_project": false } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/create_project_access_token.json000066400000000000000000000004431475761473200312760ustar00rootroot00000000000000{ "id": 1876, "name": "token 10", "revoked": false, "created_at": "2021-03-09T21:11:47.271Z", "scopes": ["api", "read_api", "read_repository", "write_repository"], "user_id": 2453, "active": true, "expires_at": null, "token": "2UsevZE1x1ZdFZW4MNzH", "access_level": 40 } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/create_project_feature_flag.json000066400000000000000000000006341475761473200312630ustar00rootroot00000000000000{ "name": "awesome_feature", "description": null, "active": true, "version": "new_version_flag", "created_at": "2020-05-13T19:56:33Z", "updated_at": "2020-05-13T19:56:33Z", "scopes": [], "strategies": [ { "id": 36, "name": "default", "parameters": {}, "scopes": [ { "id": 37, "environment_scope": "production" } ] } ] } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/create_service_account_user.json000066400000000000000000000003711475761473200313210ustar00rootroot00000000000000{ "id": 999, "username": "serviceaccount", "name": "Test Service Account", "state": "active", "locked": false, "avatar_url": "http://localhost:3000/uploads/user/avatar/999/cd8.jpeg", "web_url": "http://localhost:3000/serviceaccount" } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/download_dependency_list_export.json000066400000000000000000000015741475761473200322330ustar00rootroot00000000000000{ "bomFormat": "CycloneDX", "specVersion": "1.4", "serialNumber": "urn:uuid:3fa3b1c2-7e21-4dae-917b-b320f6d25ae1", "version": 1, "metadata": { "timestamp": "2024-11-14T23:39:16.117Z", "authors": [{ "name": "GitLab", "email": "support@gitlab.com" }], "properties": [ { "name": "gitlab:dependency_scanning:input_file:path", "value": "my_package_manager.lock" }, { "name": "gitlab:dependency_scanning:package_manager:name", "value": "my_package_manager" }, { "name": "gitlab:meta:schema_version", "value": "1" } ], "tools": [{ "vendor": "GitLab", "name": "Gemnasium", "version": "5.8.0" }] }, "components": [ { "name": "dummy", "version": "1.0.0", "purl": "pkg:testing/dummy@1.0.0", "type": "library", "licenses": [{ "license": { "name": "unknown" } }] } ] } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/edit_project_managed_license.json000066400000000000000000000001061475761473200314110ustar00rootroot00000000000000{ "id": 3, "name": "CUSTOM", "approval_status": "blacklisted" } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/get_branch.json000066400000000000000000000012111475761473200256520ustar00rootroot00000000000000{ "name": "master", "merged": false, "protected": true, "default": true, "developers_can_push": false, "developers_can_merge": false, "can_push": true, "commit": { "author_email": "john@example.com", "author_name": "John Smith", "authored_date": "2012-06-27T05:51:39.000Z", "committed_date": "2012-06-28T03:44:20.000Z", "committer_email": "john@example.com", "committer_name": "John Smith", "id": "7b5c3cc8be40ee161ae89a06bba6229da1032a0c", "short_id": "7b5c3cc", "title": "add projects API", "message": "add projects API", "parent_ids": ["4ad91d3c1144c406e50c7b33bae684bd6837faf8"] } } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/get_commit.json000066400000000000000000000014661475761473200257210ustar00rootroot00000000000000{ "id": "6104942438c14ec7bd21c6cd5bd995272b3faff6", "short_id": "6104942438c", "title": "Sanitize for network graph", "author_name": "randx", "author_email": "dmitriy.zaporozhets@gmail.com", "committer_name": "Dmitriy", "committer_email": "dmitriy.zaporozhets@gmail.com", "message": "Sanitize for network graph", "parent_ids": ["ae1d9fb46aa2b07ee9836d49862ec4e2c46fbbba"], "last_pipeline": { "id": 8, "ref": "master", "sha": "2dc6aa325a317eda67812f05600bdf0fcdc70ab0", "status": "created", "web_url": "https://gitlab.com/gitlab-org/gitlab-ce/pipelines/54268416", "created_at": "2019-11-04T15:38:53.154Z", "updated_at": "2019-11-04T15:39:03.935Z" }, "stats": { "additions": 15, "deletions": 10, "total": 25 }, "status": "running", "project_id": 13083 } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/get_compound_metrics.json000066400000000000000000000004711475761473200277760ustar00rootroot00000000000000{ "queues": { "default": { "backlog": 0, "latency": 0 } }, "processes": [ { "hostname": "gitlab.example.com", "pid": 5649, "tag": "gitlab", "concurrency": 25, "busy": 0 } ], "jobs": { "processed": 2, "failed": 0, "enqueued": 0 } } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/get_dependency_list_export.json000066400000000000000000000003141475761473200311720ustar00rootroot00000000000000{ "id": 5678, "has_finished": true, "self": "http://gitlab.example.com/api/v4/dependency_list_exports/5678", "download": "http://gitlab.example.com/api/v4/dependency_list_exports/5678/download" } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/get_draft_note.json000066400000000000000000000003421475761473200265460ustar00rootroot00000000000000{ "id": 37349978, "author_id": 10271899, "merge_request_id": 291473309, "resolve_discussion": false, "discussion_id": null, "note": "Some draft note", "commit_id": null, "position": null, "line_code": null } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/get_geo_node_status.json000066400000000000000000000153731475761473200276150ustar00rootroot00000000000000{ "geo_node_id": 1, "healthy": true, "health": "Healthy", "health_status": "Healthy", "missing_oauth_application": false, "attachments_count": 1, "attachments_synced_count": null, "attachments_failed_count": null, "attachments_synced_missing_on_primary_count": 0, "attachments_synced_in_percentage": "0.00%", "db_replication_lag_seconds": null, "lfs_objects_count": 0, "lfs_objects_synced_count": null, "lfs_objects_failed_count": null, "lfs_objects_synced_missing_on_primary_count": 0, "lfs_objects_synced_in_percentage": "0.00%", "job_artifacts_count": 2, "job_artifacts_synced_count": null, "job_artifacts_failed_count": null, "job_artifacts_synced_missing_on_primary_count": 0, "job_artifacts_synced_in_percentage": "0.00%", "container_repositories_count": 3, "container_repositories_synced_count": null, "container_repositories_failed_count": null, "container_repositories_synced_in_percentage": "0.00%", "design_repositories_count": 3, "design_repositories_synced_count": null, "design_repositories_failed_count": null, "design_repositories_synced_in_percentage": "0.00%", "projects_count": 41, "repositories_count": 41, "repositories_failed_count": null, "repositories_synced_count": null, "repositories_synced_in_percentage": "0.00%", "wikis_count": 41, "wikis_failed_count": null, "wikis_synced_count": null, "wikis_synced_in_percentage": "0.00%", "replication_slots_count": 1, "replication_slots_used_count": 1, "replication_slots_used_in_percentage": "100.00%", "replication_slots_max_retained_wal_bytes": 0, "repositories_checked_count": 20, "repositories_checked_failed_count": 20, "repositories_checked_in_percentage": "100.00%", "repositories_checksummed_count": 20, "repositories_checksum_failed_count": 5, "repositories_checksummed_in_percentage": "48.78%", "wikis_checksummed_count": 10, "wikis_checksum_failed_count": 3, "wikis_checksummed_in_percentage": "24.39%", "repositories_verified_count": 20, "repositories_verification_failed_count": 5, "repositories_verified_in_percentage": "48.78%", "repositories_checksum_mismatch_count": 3, "wikis_verified_count": 10, "wikis_verification_failed_count": 3, "wikis_verified_in_percentage": "24.39%", "wikis_checksum_mismatch_count": 1, "repositories_retrying_verification_count": 1, "wikis_retrying_verification_count": 3, "last_event_id": 23, "last_event_timestamp": 1509681166, "cursor_last_event_id": null, "cursor_last_event_timestamp": 0, "last_successful_status_check_timestamp": 1510125024, "version": "10.3.0", "revision": "33d33a096a", "merge_request_diffs_count": 5, "merge_request_diffs_checksum_total_count": 5, "merge_request_diffs_checksummed_count": 5, "merge_request_diffs_checksum_failed_count": 0, "merge_request_diffs_synced_count": null, "merge_request_diffs_failed_count": null, "merge_request_diffs_registry_count": null, "merge_request_diffs_verification_total_count": null, "merge_request_diffs_verified_count": null, "merge_request_diffs_verification_failed_count": null, "merge_request_diffs_synced_in_percentage": "0.00%", "merge_request_diffs_verified_in_percentage": "0.00%", "package_files_count": 5, "package_files_checksum_total_count": 5, "package_files_checksummed_count": 5, "package_files_checksum_failed_count": 0, "package_files_synced_count": null, "package_files_failed_count": null, "package_files_registry_count": null, "package_files_verification_total_count": null, "package_files_verified_count": null, "package_files_verification_failed_count": null, "package_files_synced_in_percentage": "0.00%", "package_files_verified_in_percentage": "0.00%", "pages_deployments_count": 5, "pages_deployments_checksum_total_count": 5, "pages_deployments_checksummed_count": 5, "pages_deployments_checksum_failed_count": 0, "pages_deployments_synced_count": null, "pages_deployments_failed_count": null, "pages_deployments_registry_count": null, "pages_deployments_verification_total_count": null, "pages_deployments_verified_count": null, "pages_deployments_verification_failed_count": null, "pages_deployments_synced_in_percentage": "0.00%", "pages_deployments_verified_in_percentage": "0.00%", "terraform_state_versions_count": 5, "terraform_state_versions_checksum_total_count": 5, "terraform_state_versions_checksummed_count": 5, "terraform_state_versions_checksum_failed_count": 0, "terraform_state_versions_synced_count": null, "terraform_state_versions_failed_count": null, "terraform_state_versions_registry_count": null, "terraform_state_versions_verification_total_count": null, "terraform_state_versions_verified_count": null, "terraform_state_versions_verification_failed_count": null, "terraform_state_versions_synced_in_percentage": "0.00%", "terraform_state_versions_verified_in_percentage": "0.00%", "snippet_repositories_count": 5, "snippet_repositories_checksum_total_count": 5, "snippet_repositories_checksummed_count": 5, "snippet_repositories_checksum_failed_count": 0, "snippet_repositories_synced_count": null, "snippet_repositories_failed_count": null, "snippet_repositories_registry_count": null, "snippet_repositories_verification_total_count": null, "snippet_repositories_verified_count": null, "snippet_repositories_verification_failed_count": null, "snippet_repositories_synced_in_percentage": "0.00%", "snippet_repositories_verified_in_percentage": "0.00%", "group_wiki_repositories_count": 5, "group_wiki_repositories_checksum_total_count": 5, "group_wiki_repositories_checksummed_count": 5, "group_wiki_repositories_checksum_failed_count": 0, "group_wiki_repositories_synced_count": null, "group_wiki_repositories_failed_count": null, "group_wiki_repositories_registry_count": null, "group_wiki_repositories_verification_total_count": null, "group_wiki_repositories_verified_count": null, "group_wiki_repositories_verification_failed_count": null, "group_wiki_repositories_synced_in_percentage": "0.00%", "group_wiki_repositories_verified_in_percentage": "0.00%", "pipeline_artifacts_count": 5, "pipeline_artifacts_checksum_total_count": 5, "pipeline_artifacts_checksummed_count": 5, "pipeline_artifacts_checksum_failed_count": 0, "pipeline_artifacts_synced_count": null, "pipeline_artifacts_failed_count": null, "pipeline_artifacts_registry_count": null, "pipeline_artifacts_verification_total_count": null, "pipeline_artifacts_verified_count": null, "pipeline_artifacts_verification_failed_count": null, "pipeline_artifacts_synced_in_percentage": "0.00%", "pipeline_artifacts_verified_in_percentage": "0.00%", "uploads_count": 5, "uploads_synced_count": null, "uploads_failed_count": 0, "uploads_registry_count": null, "uploads_synced_in_percentage": "0.00%" } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/get_group_access_token.json000066400000000000000000000003751475761473200303040ustar00rootroot00000000000000{ "id": 1, "name": "token 10", "revoked": false, "created_at": "2021-03-09T21:11:47.271Z", "scopes": ["api", "read_api", "read_repository", "write_repository"], "user_id": 2453, "active": true, "expires_at": null, "access_level": 40 } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/get_merge_request.json000066400000000000000000000121241475761473200272710ustar00rootroot00000000000000{ "id": 33092005, "iid": 14656, "project_id": 278964, "title": "Add deletion support for designs", "description": "## What does this MR do?\r\n\r\nThis adds the capability to destroy/hide designs.", "state": "opened", "created_at": "2019-07-11T22:34:43.500Z", "updated_at": "2019-08-20T09:09:56.690Z", "merged_by": null, "merged_at": null, "closed_by": null, "closed_at": null, "target_branch": "master", "source_branch": "delete-designs-v2", "user_notes_count": 245, "upvotes": 1, "downvotes": 0, "assignee": { "id": 2535118, "name": "Thong Kuah", "username": "tkuah", "state": "active", "avatar_url": "https://secure.gravatar.com/avatar/f7b51bdd49a4914d29504d7ff4c3f7b9?s=80&d=identicon", "web_url": "https://gitlab.com/tkuah" }, "author": { "id": 3614858, "name": "Alex Kalderimis", "username": "alexkalderimis", "state": "active", "avatar_url": "https://assets.gitlab-static.net/uploads/-/system/user/avatar/3614858/avatar.png", "web_url": "https://gitlab.com/alexkalderimis" }, "assignees": [ { "id": 2535118, "name": "Thong Kuah", "username": "tkuah", "state": "active", "avatar_url": "https://secure.gravatar.com/avatar/f7b51bdd49a4914d29504d7ff4c3f7b9?s=80&d=identicon", "web_url": "https://gitlab.com/tkuah" } ], "reviewers": [ { "id": 2535118, "name": "Thong Kuah", "username": "tkuah", "state": "active", "avatar_url": "https://secure.gravatar.com/avatar/f7b51bdd49a4914d29504d7ff4c3f7b9?s=80&d=identicon", "web_url": "https://gitlab.com/tkuah" } ], "source_project_id": 278964, "target_project_id": 278964, "labels": [ "GitLab Enterprise Edition", "backend", "database", "database::reviewed", "design management", "feature", "frontend", "group::knowledge", "missed:12.1" ], "work_in_progress": false, "milestone": { "id": 693521, "iid": 35, "group_id": 9970, "title": "12.2", "description": "", "state": "active", "created_at": "2018-10-30T16:48:32.567Z", "updated_at": "2019-01-16T19:50:02.455Z", "due_date": "2019-08-22", "start_date": "2019-07-08", "web_url": "https://gitlab.com/groups/gitlab-org/-/milestones/35" }, "merge_when_pipeline_succeeds": false, "detailed_merge_status": "mergeable", "merge_status": "can_be_merged", "sha": "8e0b45049b6253b8984cde9241830d2851168142", "merge_commit_sha": null, "discussion_locked": null, "should_remove_source_branch": null, "force_remove_source_branch": true, "reference": "!14656", "web_url": "https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/14656", "time_stats": { "time_estimate": 0, "total_time_spent": 0, "human_time_estimate": null, "human_total_time_spent": null }, "squash": true, "task_completion_status": { "count": 9, "completed_count": 8 }, "subscribed": false, "changes_count": "35", "latest_build_started_at": "2019-08-19T09:51:06.545Z", "latest_build_finished_at": "2019-08-19T19:22:29.632Z", "first_deployed_to_production_at": null, "pipeline": { "id": 77056819, "sha": "8e0b45049b6253b8984cde9241830d2851168142", "ref": "delete-designs-v2", "status": "success", "web_url": "https://gitlab.com/gitlab-org/gitlab-ee/pipelines/77056819", "created_at": "2019-08-19T09:50:58.157Z", "updated_at": "2019-08-19T19:22:29.647Z" }, "head_pipeline": { "id": 77056819, "sha": "8e0b45049b6253b8984cde9241830d2851168142", "ref": "delete-designs-v2", "status": "success", "web_url": "https://gitlab.com/gitlab-org/gitlab-ee/pipelines/77056819", "before_sha": "3fe568caacb261b63090886f5b879ca0d9c6f4c3", "tag": false, "yaml_errors": null, "user": { "id": 3614858, "name": "Alex Kalderimis", "username": "alexkalderimis", "state": "active", "avatar_url": "https://assets.gitlab-static.net/uploads/-/system/user/avatar/3614858/avatar.png", "web_url": "https://gitlab.com/alexkalderimis" }, "created_at": "2019-08-19T09:50:58.157Z", "updated_at": "2019-08-19T19:22:29.647Z", "started_at": "2019-08-19T09:51:06.545Z", "finished_at": "2019-08-19T19:22:29.632Z", "committed_at": null, "duration": 4916, "coverage": "82.68", "detailed_status": { "icon": "status_warning", "text": "passed", "label": "passed with warnings", "group": "success-with-warnings", "tooltip": "passed", "has_details": true, "details_path": "/gitlab-org/gitlab-ee/pipelines/77056819", "illustration": null, "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png" } }, "diff_refs": { "base_sha": "dd692733bb84bc3e9e862e66c099daec2fa00c83", "head_sha": "8e0b45049b6253b8984cde9241830d2851168142", "start_sha": "dd692733bb84bc3e9e862e66c099daec2fa00c83" }, "merge_error": null, "user": { "can_merge": false }, "first_contribution": true, "approvals_before_merge": 1, "has_conflicts": true, "draft": true } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/get_merge_request_dependencies.json000066400000000000000000000154001475761473200317770ustar00rootroot00000000000000[ { "id": 1, "blocking_merge_request": { "id": 145, "iid": 12, "project_id": 7, "title": "Interesting MR", "description": "Does interesting things.", "state": "opened", "created_at": "2024-07-05T21:29:11.172Z", "updated_at": "2024-07-05T21:29:11.172Z", "merged_by": null, "merge_user": null, "merged_at": null, "merge_after": "2018-09-07T11:16:00.000Z", "closed_by": null, "closed_at": null, "target_branch": "master", "source_branch": "v2.x", "user_notes_count": 0, "upvotes": 0, "downvotes": 0, "author": { "id": 2, "username": "aiguy123", "name": "AI GUY", "state": "active", "locked": false, "avatar_url": "https://www.gravatar.com/avatar/0?s=80&d=identicon", "web_url": "https://localhost/aiguy123" }, "assignees": [ { "id": 2, "username": "aiguy123", "name": "AI GUY", "state": "active", "locked": false, "avatar_url": "https://www.gravatar.com/avatar/0?s=80&d=identicon", "web_url": "https://localhost/aiguy123" } ], "assignee": { "id": 2, "username": "aiguy123", "name": "AI GUY", "state": "active", "locked": false, "avatar_url": "https://www.gravatar.com/avatar/0?s=80&d=identicon", "web_url": "https://localhost/aiguy123" }, "reviewers": [ { "id": 2, "username": "aiguy123", "name": "AI GUY", "state": "active", "locked": false, "avatar_url": "https://www.gravatar.com/avatar/0?s=80&d=identicon", "web_url": "https://localhost/aiguy123" }, { "id": 1, "username": "root", "name": "Administrator", "state": "active", "locked": false, "avatar_url": "https://www.gravatar.com/avatar/0?s=80&d=identicon", "web_url": "https://localhost/root" } ], "source_project_id": 7, "target_project_id": 7, "labels": [], "draft": false, "imported": false, "imported_from": "none", "work_in_progress": false, "milestone": null, "merge_when_pipeline_succeeds": false, "merge_status": "unchecked", "detailed_merge_status": "unchecked", "sha": "ce7e4f2d0ce13cb07479bb39dc10ee3b861c08a6", "merge_commit_sha": null, "squash_commit_sha": null, "discussion_locked": null, "should_remove_source_branch": null, "force_remove_source_branch": true, "prepared_at": null, "reference": "!12", "references": { "short": "!12", "relative": "!12", "full": "my-group/my-project!12" }, "web_url": "https://localhost/my-group/my-project/-/merge_requests/12", "time_stats": { "time_estimate": 0, "total_time_spent": 0, "human_time_estimate": null, "human_total_time_spent": null }, "squash": false, "squash_on_merge": false, "task_completion_status": { "count": 0, "completed_count": 0 }, "has_conflicts": false, "blocking_discussions_resolved": true, "approvals_before_merge": null }, "blocked_merge_request": { "id": 146, "iid": 13, "project_id": 7, "title": "Really cool MR", "description": "Adds some stuff", "state": "opened", "created_at": "2024-07-05T21:31:34.811Z", "updated_at": "2024-07-27T02:57:08.054Z", "merged_by": null, "merge_user": null, "merged_at": null, "merge_after": "2018-09-07T11:16:00.000Z", "closed_by": null, "closed_at": null, "target_branch": "master", "source_branch": "remove-from", "user_notes_count": 0, "upvotes": 1, "downvotes": 0, "author": { "id": 2, "username": "aiguy123", "name": "AI GUY", "state": "active", "locked": false, "avatar_url": "https://www.gravatar.com/avatar/0?s=80&d=identicon", "web_url": "https://localhost/aiguy123" }, "assignees": [ { "id": 2, "username": "aiguy123", "name": "AI GUY", "state": "active", "locked": false, "avatar_url": "https://www.gravatar.com/avatar/0?s=80&d=identicon", "web_url": "https://localhose/aiguy123" } ], "assignee": { "id": 2, "username": "aiguy123", "name": "AI GUY", "state": "active", "locked": false, "avatar_url": "https://www.gravatar.com/avatar/0?s=80&d=identicon", "web_url": "https://localhost/aiguy123" }, "reviewers": [ { "id": 1, "username": "root", "name": "Administrator", "state": "active", "locked": false, "avatar_url": "https://www.gravatar.com/avatar/0?s=80&d=identicon", "web_url": "https://localhost/root" } ], "source_project_id": 7, "target_project_id": 7, "labels": [], "draft": false, "imported": false, "imported_from": "none", "work_in_progress": false, "milestone": { "id": 59, "iid": 6, "project_id": 7, "title": "Sprint 1718897375", "description": "Accusantium omnis iusto a animi.", "state": "active", "created_at": "2024-06-20T15:29:35.739Z", "updated_at": "2024-06-20T15:29:35.739Z", "due_date": null, "start_date": null, "expired": false, "web_url": "https://localhost/my-group/my-project/-/milestones/6" }, "merge_when_pipeline_succeeds": false, "merge_status": "cannot_be_merged", "detailed_merge_status": "not_approved", "sha": "daa75b9b17918f51f43866ff533987fda71375ea", "merge_commit_sha": null, "squash_commit_sha": null, "discussion_locked": null, "should_remove_source_branch": null, "force_remove_source_branch": true, "prepared_at": "2024-07-11T18:50:46.215Z", "reference": "!13", "references": { "short": "!13", "relative": "!13", "full": "my-group/my-project!12" }, "web_url": "https://localhost/my-group/my-project/-/merge_requests/13", "time_stats": { "time_estimate": 0, "total_time_spent": 0, "human_time_estimate": null, "human_total_time_spent": null }, "squash": false, "squash_on_merge": false, "task_completion_status": { "count": 0, "completed_count": 0 }, "has_conflicts": true, "blocking_discussions_resolved": true, "approvals_before_merge": null }, "project_id": 7 } ]golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/get_merge_request_in_merge_train.json000066400000000000000000000021521475761473200323330ustar00rootroot00000000000000{ "id": 267, "merge_request": { "id": 273, "iid": 1, "project_id": 597, "title": "My title 9", "description": null, "state": "opened", "created_at": "2022-10-31T19:06:05.725Z", "updated_at": "2022-10-31T19:06:05.725Z", "web_url": "http://localhost/namespace18/project21/-/merge_requests/1" }, "user": { "id": 933, "username": "user12", "name": "Sidney Jones31", "state": "active", "avatar_url": "https://www.gravatar.com/avatar/6c8365de387cb3db10ecc7b1880203c4?s=80\u0026d=identicon", "web_url": "http://localhost/user12" }, "pipeline": { "id": 273, "iid": 1, "project_id": 598, "sha": "b83d6e391c22777fca1ed3012fce84f633d7fed0", "ref": "main", "status": "pending", "source": "push", "created_at": "2022-10-31T19:06:06.231Z", "updated_at": "2022-10-31T19:06:06.231Z", "web_url": "http://localhost/namespace19/project22/-/pipelines/273" }, "created_at": "2022-10-31T19:06:06.237Z", "updated_at": "2022-10-31T19:06:06.237Z", "target_branch": "main", "status": "idle", "merged_at": null, "duration": null } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/get_merge_requests.json000066400000000000000000000211341475761473200274550ustar00rootroot00000000000000[ { "id": 35385049, "iid": 15442, "project_id": 278964, "title": "Draft: Use structured logging for DB load balancer", "description": "## What does this MR do?", "state": "opened", "created_at": "2019-08-20T10:58:54.413Z", "updated_at": "2019-08-20T12:01:49.849Z", "merged_by": null, "merged_at": null, "closed_by": null, "closed_at": null, "target_branch": "master", "source_branch": "use-structured-logging-for-db-load-balancer", "user_notes_count": 1, "upvotes": 0, "downvotes": 0, "assignee": { "id": 4088036, "name": "Hordur Freyr Yngvason", "username": "hfyngvason", "state": "active", "avatar_url": "https://assets.gitlab-static.net/uploads/-/system/user/avatar/4088036/avatar.png", "web_url": "https://gitlab.com/hfyngvason" }, "author": { "id": 4088036, "name": "Hordur Freyr Yngvason", "username": "hfyngvason", "state": "active", "avatar_url": "https://assets.gitlab-static.net/uploads/-/system/user/avatar/4088036/avatar.png", "web_url": "https://gitlab.com/hfyngvason" }, "assignees": [ { "id": 4088036, "name": "Hordur Freyr Yngvason", "username": "hfyngvason", "state": "active", "avatar_url": "https://assets.gitlab-static.net/uploads/-/system/user/avatar/4088036/avatar.png", "web_url": "https://gitlab.com/hfyngvason" } ], "reviewers": [ { "id": 2535118, "name": "Thong Kuah", "username": "tkuah", "state": "active", "avatar_url": "https://secure.gravatar.com/avatar/f7b51bdd49a4914d29504d7ff4c3f7b9?s=80&d=identicon", "web_url": "https://gitlab.com/tkuah" } ], "source_project_id": 278964, "target_project_id": 278964, "labels": [ "backend", "backstage", "database", "database::review pending", "group::autodevops and kubernetes" ], "work_in_progress": true, "milestone": null, "merge_when_pipeline_succeeds": false, "detailed_merge_status": "mergeable", "sha": "2fc4e8b972ff3208ec63b6143e34ad67ff343ad7", "merge_commit_sha": null, "discussion_locked": null, "should_remove_source_branch": null, "force_remove_source_branch": true, "reference": "!15442", "web_url": "https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/15442", "time_stats": { "time_estimate": 0, "total_time_spent": 0, "human_time_estimate": null, "human_total_time_spent": null }, "squash": true, "task_completion_status": { "count": 12, "completed_count": 0 }, "approvals_before_merge": 1 }, { "id": 35384461, "iid": 15441, "project_id": 278964, "title": "Draft: Implement public MR-level approval rules API", "description": "## What does this MR do?", "state": "opened", "created_at": "2019-08-20T10:51:56.806Z", "updated_at": "2019-08-20T11:00:25.244Z", "merged_by": null, "merged_at": null, "closed_by": null, "closed_at": null, "target_branch": "master", "source_branch": "12055-public-mr-approval-rules-api", "user_notes_count": 1, "upvotes": 0, "downvotes": 0, "assignee": { "id": 1884221, "name": "Patrick Bajao", "username": "patrickbajao", "state": "active", "avatar_url": "https://assets.gitlab-static.net/uploads/-/system/user/avatar/1884221/avatar.png", "web_url": "https://gitlab.com/patrickbajao" }, "author": { "id": 1884221, "name": "Patrick Bajao", "username": "patrickbajao", "state": "active", "avatar_url": "https://assets.gitlab-static.net/uploads/-/system/user/avatar/1884221/avatar.png", "web_url": "https://gitlab.com/patrickbajao" }, "assignees": [ { "id": 1884221, "name": "Patrick Bajao", "username": "patrickbajao", "state": "active", "avatar_url": "https://assets.gitlab-static.net/uploads/-/system/user/avatar/1884221/avatar.png", "web_url": "https://gitlab.com/patrickbajao" } ], "source_project_id": 278964, "target_project_id": 278964, "labels": [ "Create [DEPRECATED]", "Deliverable", "GitLab Enterprise Edition", "GitLab Starter", "api", "approvals", "backend", "devops::create", "feature", "group::source code", "workflow::In dev" ], "work_in_progress": true, "milestone": { "id": 731038, "iid": 37, "group_id": 9970, "title": "12.3", "description": "", "state": "active", "created_at": "2018-12-07T12:40:55.400Z", "updated_at": "2019-01-16T19:50:20.313Z", "due_date": "2019-09-22", "start_date": "2019-08-08", "web_url": "https://gitlab.com/groups/gitlab-org/-/milestones/37" }, "merge_when_pipeline_succeeds": false, "detailed_merge_status": "mergeable", "sha": "dbb2b82236b86328f44a1754c9188c0991e707e5", "merge_commit_sha": null, "discussion_locked": null, "should_remove_source_branch": null, "force_remove_source_branch": false, "reference": "!15441", "web_url": "https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/15441", "time_stats": { "time_estimate": 0, "total_time_spent": 0, "human_time_estimate": null, "human_total_time_spent": null }, "squash": false, "task_completion_status": { "count": 8, "completed_count": 1 }, "approvals_before_merge": 1 }, { "id": 35368820, "iid": 15440, "project_id": 278964, "title": "Log in Prometheus current number of host and index", "description": "## What does this MR do?", "state": "opened", "created_at": "2019-08-20T10:20:51.687Z", "updated_at": "2019-08-20T11:06:40.659Z", "merged_by": null, "merged_at": null, "closed_by": null, "closed_at": null, "target_branch": "master", "source_branch": "load-balancing-prometheus", "user_notes_count": 2, "upvotes": 0, "downvotes": 0, "assignee": { "id": 4059128, "name": "Avielle Wolfe", "username": "avielle", "state": "active", "avatar_url": "https://assets.gitlab-static.net/uploads/-/system/user/avatar/4059128/avatar.png", "web_url": "https://gitlab.com/avielle" }, "author": { "id": 2535118, "name": "Thong Kuah", "username": "tkuah", "state": "active", "avatar_url": "https://secure.gravatar.com/avatar/f7b51bdd49a4914d29504d7ff4c3f7b9?s=80&d=identicon", "web_url": "https://gitlab.com/tkuah" }, "assignees": [ { "id": 4059128, "name": "Avielle Wolfe", "username": "avielle", "state": "active", "avatar_url": "https://assets.gitlab-static.net/uploads/-/system/user/avatar/4059128/avatar.png", "web_url": "https://gitlab.com/avielle" }, { "id": 2535118, "name": "Thong Kuah", "username": "tkuah", "state": "active", "avatar_url": "https://secure.gravatar.com/avatar/f7b51bdd49a4914d29504d7ff4c3f7b9?s=80&d=identicon", "web_url": "https://gitlab.com/tkuah" } ], "source_project_id": 278964, "target_project_id": 278964, "labels": [ "Configure [DEPRECATED]", "Observability", "P1", "backend", "backstage", "database", "database::review pending", "devops::configure", "gitlab.com", "group::autodevops and kubernetes", "infradev", "workflow::In dev" ], "work_in_progress": false, "milestone": { "id": 731038, "iid": 37, "group_id": 9970, "title": "12.3", "description": "", "state": "active", "created_at": "2018-12-07T12:40:55.400Z", "updated_at": "2019-01-16T19:50:20.313Z", "due_date": "2019-09-22", "start_date": "2019-08-08", "web_url": "https://gitlab.com/groups/gitlab-org/-/milestones/37" }, "merge_when_pipeline_succeeds": false, "detailed_merge_status": "mergeable", "sha": "eae31acf34d2df2aba2ea469e432cab1c6a05b53", "merge_commit_sha": null, "discussion_locked": null, "should_remove_source_branch": null, "force_remove_source_branch": false, "reference": "!15440", "web_url": "https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/15440", "time_stats": { "time_estimate": 0, "total_time_spent": 0, "human_time_estimate": null, "human_total_time_spent": null }, "squash": false, "task_completion_status": { "count": 12, "completed_count": 1 }, "approvals_before_merge": 1 } ] golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/get_merge_requests_author_username.json000066400000000000000000000052521475761473200327410ustar00rootroot00000000000000[ { "id": 35385049, "iid": 15442, "project_id": 278964, "title": "Draft: Use structured logging for DB load balancer", "description": "## What does this MR do?", "state": "opened", "created_at": "2019-08-20T10:58:54.413Z", "updated_at": "2019-08-20T12:01:49.849Z", "merged_by": null, "merged_at": null, "closed_by": null, "closed_at": null, "target_branch": "master", "source_branch": "use-structured-logging-for-db-load-balancer", "user_notes_count": 1, "upvotes": 0, "downvotes": 0, "assignee": { "id": 4088036, "name": "Hordur Freyr Yngvason", "username": "hfyngvason", "state": "active", "avatar_url": "https://assets.gitlab-static.net/uploads/-/system/user/avatar/4088036/avatar.png", "web_url": "https://gitlab.com/hfyngvason" }, "author": { "id": 4088036, "name": "Hordur Freyr Yngvason", "username": "hfyngvason", "state": "active", "avatar_url": "https://assets.gitlab-static.net/uploads/-/system/user/avatar/4088036/avatar.png", "web_url": "https://gitlab.com/hfyngvason" }, "assignees": [ { "id": 4088036, "name": "Hordur Freyr Yngvason", "username": "hfyngvason", "state": "active", "avatar_url": "https://assets.gitlab-static.net/uploads/-/system/user/avatar/4088036/avatar.png", "web_url": "https://gitlab.com/hfyngvason" } ], "reviewers": [ { "id": 2535118, "name": "Thong Kuah", "username": "tkuah", "state": "active", "avatar_url": "https://secure.gravatar.com/avatar/f7b51bdd49a4914d29504d7ff4c3f7b9?s=80&d=identicon", "web_url": "https://gitlab.com/tkuah" } ], "source_project_id": 278964, "target_project_id": 278964, "labels": [ "backend", "backstage", "database", "database::review pending", "group::autodevops and kubernetes" ], "work_in_progress": true, "milestone": null, "merge_when_pipeline_succeeds": false, "detailed_merge_status": "mergeable", "sha": "2fc4e8b972ff3208ec63b6143e34ad67ff343ad7", "merge_commit_sha": null, "discussion_locked": null, "should_remove_source_branch": null, "force_remove_source_branch": true, "reference": "!15442", "web_url": "https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/15442", "time_stats": { "time_estimate": 0, "total_time_spent": 0, "human_time_estimate": null, "human_total_time_spent": null }, "squash": true, "task_completion_status": { "count": 12, "completed_count": 0 }, "approvals_before_merge": 1 } ] golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/get_merge_requests_not_author_username.json000066400000000000000000000136651475761473200336300ustar00rootroot00000000000000[ { "id": 35384461, "iid": 15441, "project_id": 278964, "title": "Draft: Implement public MR-level approval rules API", "description": "## What does this MR do?", "state": "opened", "created_at": "2019-08-20T10:51:56.806Z", "updated_at": "2019-08-20T11:00:25.244Z", "merged_by": null, "merged_at": null, "closed_by": null, "closed_at": null, "target_branch": "master", "source_branch": "12055-public-mr-approval-rules-api", "user_notes_count": 1, "upvotes": 0, "downvotes": 0, "assignee": { "id": 1884221, "name": "Patrick Bajao", "username": "patrickbajao", "state": "active", "avatar_url": "https://assets.gitlab-static.net/uploads/-/system/user/avatar/1884221/avatar.png", "web_url": "https://gitlab.com/patrickbajao" }, "author": { "id": 1884221, "name": "Patrick Bajao", "username": "patrickbajao", "state": "active", "avatar_url": "https://assets.gitlab-static.net/uploads/-/system/user/avatar/1884221/avatar.png", "web_url": "https://gitlab.com/patrickbajao" }, "assignees": [ { "id": 1884221, "name": "Patrick Bajao", "username": "patrickbajao", "state": "active", "avatar_url": "https://assets.gitlab-static.net/uploads/-/system/user/avatar/1884221/avatar.png", "web_url": "https://gitlab.com/patrickbajao" } ], "source_project_id": 278964, "target_project_id": 278964, "labels": [ "Create [DEPRECATED]", "Deliverable", "GitLab Enterprise Edition", "GitLab Starter", "api", "approvals", "backend", "devops::create", "feature", "group::source code", "workflow::In dev" ], "work_in_progress": true, "milestone": { "id": 731038, "iid": 37, "group_id": 9970, "title": "12.3", "description": "", "state": "active", "created_at": "2018-12-07T12:40:55.400Z", "updated_at": "2019-01-16T19:50:20.313Z", "due_date": "2019-09-22", "start_date": "2019-08-08", "web_url": "https://gitlab.com/groups/gitlab-org/-/milestones/37" }, "merge_when_pipeline_succeeds": false, "detailed_merge_status": "mergeable", "sha": "dbb2b82236b86328f44a1754c9188c0991e707e5", "merge_commit_sha": null, "discussion_locked": null, "should_remove_source_branch": null, "force_remove_source_branch": false, "reference": "!15441", "web_url": "https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/15441", "time_stats": { "time_estimate": 0, "total_time_spent": 0, "human_time_estimate": null, "human_total_time_spent": null }, "squash": false, "task_completion_status": { "count": 8, "completed_count": 1 }, "approvals_before_merge": 1 }, { "id": 35368820, "iid": 15440, "project_id": 278964, "title": "Log in Prometheus current number of host and index", "description": "## What does this MR do?", "state": "opened", "created_at": "2019-08-20T10:20:51.687Z", "updated_at": "2019-08-20T11:06:40.659Z", "merged_by": null, "merged_at": null, "closed_by": null, "closed_at": null, "target_branch": "master", "source_branch": "load-balancing-prometheus", "user_notes_count": 2, "upvotes": 0, "downvotes": 0, "assignee": { "id": 4059128, "name": "Avielle Wolfe", "username": "avielle", "state": "active", "avatar_url": "https://assets.gitlab-static.net/uploads/-/system/user/avatar/4059128/avatar.png", "web_url": "https://gitlab.com/avielle" }, "author": { "id": 2535118, "name": "Thong Kuah", "username": "tkuah", "state": "active", "avatar_url": "https://secure.gravatar.com/avatar/f7b51bdd49a4914d29504d7ff4c3f7b9?s=80&d=identicon", "web_url": "https://gitlab.com/tkuah" }, "assignees": [ { "id": 4059128, "name": "Avielle Wolfe", "username": "avielle", "state": "active", "avatar_url": "https://assets.gitlab-static.net/uploads/-/system/user/avatar/4059128/avatar.png", "web_url": "https://gitlab.com/avielle" }, { "id": 2535118, "name": "Thong Kuah", "username": "tkuah", "state": "active", "avatar_url": "https://secure.gravatar.com/avatar/f7b51bdd49a4914d29504d7ff4c3f7b9?s=80&d=identicon", "web_url": "https://gitlab.com/tkuah" } ], "source_project_id": 278964, "target_project_id": 278964, "labels": [ "Configure [DEPRECATED]", "Observability", "P1", "backend", "backstage", "database", "database::review pending", "devops::configure", "gitlab.com", "group::autodevops and kubernetes", "infradev", "workflow::In dev" ], "work_in_progress": false, "milestone": { "id": 731038, "iid": 37, "group_id": 9970, "title": "12.3", "description": "", "state": "active", "created_at": "2018-12-07T12:40:55.400Z", "updated_at": "2019-01-16T19:50:20.313Z", "due_date": "2019-09-22", "start_date": "2019-08-08", "web_url": "https://gitlab.com/groups/gitlab-org/-/milestones/37" }, "merge_when_pipeline_succeeds": false, "detailed_merge_status": "mergeable", "sha": "eae31acf34d2df2aba2ea469e432cab1c6a05b53", "merge_commit_sha": null, "discussion_locked": null, "should_remove_source_branch": null, "force_remove_source_branch": false, "reference": "!15440", "web_url": "https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/15440", "time_stats": { "time_estimate": 0, "total_time_spent": 0, "human_time_estimate": null, "human_total_time_spent": null }, "squash": false, "task_completion_status": { "count": 12, "completed_count": 1 }, "approvals_before_merge": 1 } ] golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/get_pipeline_testreport.json000066400000000000000000000044221475761473200305240ustar00rootroot00000000000000{ "total_time": 61.502, "total_count": 9, "success_count": 5, "failed_count": 0, "skipped_count": 0, "error_count": 4, "test_suites": [ { "name": "Failing", "total_time": 60.494, "total_count": 8, "success_count": 4, "failed_count": 0, "skipped_count": 0, "error_count": 4, "suite_error": null, "test_cases": [ { "status": "error", "name": "Error testcase 1", "classname": "MyClassOne", "file": "/path/file.ext", "execution_time": 19.987, "system_output": "Failed test", "stack_trace": "java.lang.Exception: Stack trace\nat java.base/java.lang.Thread.dumpStack(Thread.java:1383)", "attachment_url": "http://foo.bar", "recent_failures": { "count": 10, "base_branch": "master" } }, { "status": "error", "name": "Error testcase 2", "classname": "MyClass", "file": null, "execution_time": 19.984, "system_output": { "message": "Failed test", "type": "MultipleExceptionError" }, "stack_trace": null, "attachment_url": null, "recent_failures": null }, { "status": "error", "name": "Error testcase 3", "classname": "MyClass", "file": null, "execution_time": 0.0, "system_output": ["Failed test a", "Failed test b"] }, { "status": "success", "name": "Succes full testcase", "classname": "MyClass", "file": null, "execution_time": 19.7799999999999985, "system_output": null, "stack_trace": null } ] }, { "name": "Succes suite", "total_time": 1.008, "total_count": 1, "success_count": 1, "failed_count": 0, "skipped_count": 0, "error_count": 0, "suite_error": null, "test_cases": [ { "status": "success", "name": "Succesfull testcase", "classname": "MyClass", "file": null, "execution_time": 1.008, "system_output": null, "stack_trace": null } ] } ] } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/get_project_access_token.json000066400000000000000000000003751475761473200306160ustar00rootroot00000000000000{ "id": 1, "name": "token 10", "revoked": false, "created_at": "2021-03-09T21:11:47.271Z", "scopes": ["api", "read_api", "read_repository", "write_repository"], "user_id": 2453, "active": true, "expires_at": null, "access_level": 40 } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/get_project_feature_flag.json000066400000000000000000000012651475761473200306000ustar00rootroot00000000000000{ "name": "awesome_feature", "description": null, "active": true, "version": "new_version_flag", "created_at": "2020-05-13T19:56:33Z", "updated_at": "2020-05-13T19:56:33Z", "scopes": [], "strategies": [ { "id": 36, "name": "default", "parameters": {}, "scopes": [ { "id": 37, "environment_scope": "production" } ] }, { "id": 24, "name": "flexibleRollout", "parameters": { "groupId": "default", "rollout": "50", "stickiness": "default" }, "scopes": [ { "id": 52, "environment_scope": "*" } ] } ] } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/get_project_managed_license.json000066400000000000000000000001031475761473200312400ustar00rootroot00000000000000{ "id": 3, "name": "ISC", "approval_status": "blacklisted" } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/get_serviceaccounts.json000066400000000000000000000002711475761473200276220ustar00rootroot00000000000000[ { "id": 114, "username": "service_account_33", "name": "Service account user" }, { "id": 137, "username": "service_account_34", "name": "john doe" } ] golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/get_signature.json000066400000000000000000000003611475761473200264230ustar00rootroot00000000000000{ "gpg_key_id": 7977, "gpg_key_primary_keyid": "627C5F589F467F17", "gpg_key_user_name": "Dmitriy Zaporozhets", "gpg_key_user_email": "dmitriy.zaporozhets@gmail.com", "verification_status": "verified", "gpg_key_subkey_id": null } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/get_user.json000066400000000000000000000011071475761473200253770ustar00rootroot00000000000000{ "id": 1, "username": "john_smith", "name": "John Smith", "state": "active", "avatar_url": "http://localhost:3000/uploads/user/avatar/1/cd8.jpeg", "web_url": "http://localhost:3000/john_smith", "created_at": "2012-05-23T08:00:58Z", "bio": "Bio of John Smith", "bot": false, "location": "USA", "public_email": "john@example.com", "skype": "john_smith", "linkedin": "john_smith", "twitter": "john_smith", "website_url": "john_smith.example.com", "organization": "Smith Inc", "job_title": "Operations Specialist", "followers": 1, "following": 1 } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/get_user_admin.json000066400000000000000000000025221475761473200265510ustar00rootroot00000000000000{ "id": 1, "username": "john_smith", "email": "john@example.com", "name": "John Smith", "state": "active", "avatar_url": "http://localhost:3000/uploads/user/avatar/1/index.jpg", "web_url": "http://localhost:3000/john_smith", "created_at": "2012-05-23T08:00:58Z", "is_admin": true, "is_auditor": true, "bio": "Bio of John Smith", "location": "USA", "public_email": "john@example.com", "skype": "john_smith", "linkedin": "john_smith", "twitter": "john_smith", "website_url": "john_smith.example.com", "organization": "Smith Inc", "job_title": "Operations Specialist", "last_sign_in_at": "2012-06-01T11:41:01Z", "confirmed_at": "2012-05-23T09:05:22Z", "theme_id": 1, "last_activity_on": "2012-05-23", "color_scheme_id": 2, "projects_limit": 100, "current_sign_in_at": "2012-06-02T06:36:55Z", "note": "DMCA Request: 2018-11-05 | DMCA Violation | Abuse | https://gitlab.zendesk.com/agent/tickets/123", "identities": [{ "provider": "github", "extern_uid": "2435223452345" }], "can_create_group": true, "can_create_project": true, "two_factor_enabled": true, "external": false, "private_profile": false, "commit_email": "john-codes@example.com", "current_sign_in_ip": "8.8.8.8", "last_sign_in_ip": "2001:db8::68", "plan": "gold", "trial": true, "sign_in_count": 1337, "namespace_id": 42 } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/get_user_associations_count.json000066400000000000000000000001411475761473200313630ustar00rootroot00000000000000{ "groups_count": 1, "projects_count": 2, "issues_count": 3, "merge_requests_count": 4 } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/get_user_bot.json000066400000000000000000000031401475761473200262420ustar00rootroot00000000000000{ "id": 2, "username": "project_1_bot_3cca1d029554e372cf8f39df76bf507d", "name": "John Bot", "state": "active", "locked": false, "avatar_url": "http://localhost:3000/uploads/user/avatar/2/index.jpg", "web_url": "http://localhost:3000/project_1_bot_3cca1d029554e372cf8f39df76bf507d", "created_at": "2012-05-23T08:00:58Z", "bio": "", "location": "", "public_email": null, "skype": "", "linkedin": "", "twitter": "", "discord": "", "website_url": "", "organization": "", "job_title": "", "pronouns": null, "bot": true, "work_information": null, "followers": 0, "following": 0, "is_followed": false, "local_time": null, "last_sign_in_at": null, "confirmed_at": "2012-05-23T08:00:58Z", "last_activity_on": "2012-05-23", "email": "project_1_bot_3cca1d029554e372cf8f39df76bf507d@example.com", "theme_id": 3, "color_scheme_id": 1, "projects_limit": 0, "current_sign_in_at": null, "identities": [ ], "can_create_group": false, "can_create_project": false, "two_factor_enabled": false, "external": false, "private_profile": false, "commit_email": "project_1_bot_3cca1d029554e372cf8f39df76bf507d@example.com", "is_admin": false, "note": null, "namespace_id": 4, "created_by": { "id": 1, "username": "john_smith", "name": "John Smith", "state": "active", "locked": false, "avatar_url": "http://localhost:3000/uploads/user/avatar/1/cd8.jpeg", "web_url": "http://localhost:3000/john_smith" }, "email_reset_offered_at": null, "highest_role": 0, "current_sign_in_ip": null, "last_sign_in_ip": null, "sign_in_count": 0 } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/get_user_memberships.json000066400000000000000000000003551475761473200300010ustar00rootroot00000000000000[ { "source_id": 1, "source_name": "Project one", "source_type": "Project", "access_level": 20 }, { "source_id": 3, "source_name": "Group three", "source_type": "Namespace", "access_level": 20 } ] golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/issue_move.json000066400000000000000000000036171475761473200257500ustar00rootroot00000000000000{ "id": 92, "iid": 11, "project_id": 5, "title": "Sit voluptas tempora quisquam aut doloribus et.", "description": "Repellat voluptas quibusdam voluptatem exercitationem.", "state": "opened", "created_at": "2016-04-05T21:41:45.652Z", "updated_at": "2016-04-07T12:20:17.596Z", "closed_at": null, "closed_by": null, "labels": [], "upvotes": 4, "downvotes": 0, "merge_requests_count": 0, "milestone": null, "assignees": [ { "name": "Miss Monserrate Beier", "username": "axel.block", "id": 12, "state": "active", "avatar_url": "http://www.gravatar.com/avatar/46f6f7dc858ada7be1853f7fb96e81da?s=80&d=identicon", "web_url": "https://gitlab.example.com/axel.block" } ], "assignee": { "name": "Miss Monserrate Beier", "username": "axel.block", "id": 12, "state": "active", "avatar_url": "http://www.gravatar.com/avatar/46f6f7dc858ada7be1853f7fb96e81da?s=80&d=identicon", "web_url": "https://gitlab.example.com/axel.block" }, "author": { "name": "Kris Steuber", "username": "solon.cremin", "id": 10, "state": "active", "avatar_url": "http://www.gravatar.com/avatar/7a190fecbaa68212a4b68aeb6e3acd10?s=80&d=identicon", "web_url": "https://gitlab.example.com/solon.cremin" }, "due_date": null, "web_url": "http://example.com/example/example/issues/11", "time_stats": { "time_estimate": 0, "total_time_spent": 0, "human_time_estimate": null, "human_total_time_spent": null }, "confidential": false, "discussion_locked": false, "_links": { "self": "http://example.com/api/v4/projects/1/issues/2", "notes": "http://example.com/api/v4/projects/1/issues/2/notes", "award_emoji": "http://example.com/api/v4/projects/1/issues/2/award_emoji", "project": "http://example.com/api/v4/projects/1" }, "task_completion_status": { "count": 0, "completed_count": 0 } } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/list_branches.json000066400000000000000000000014101475761473200263770ustar00rootroot00000000000000[ { "name": "master", "merged": false, "protected": true, "default": true, "developers_can_push": false, "developers_can_merge": false, "can_push": true, "web_url": "https://gitlab.example.com/my-group/my-project/-/tree/master", "commit": { "author_email": "john@example.com", "author_name": "John Smith", "authored_date": "2012-06-27T05:51:39.000Z", "committed_date": "2012-06-28T03:44:20.000Z", "committer_email": "john@example.com", "committer_name": "John Smith", "id": "7b5c3cc8be40ee161ae89a06bba6229da1032a0c", "short_id": "7b5c3cc", "title": "add projects API", "message": "add projects API", "parent_ids": ["4ad91d3c1144c406e50c7b33bae684bd6837faf8"] } } ] golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/list_draft_notes.json000066400000000000000000000024131475761473200271260ustar00rootroot00000000000000[ { "id": 37349978, "author_id": 10271899, "merge_request_id": 291473309, "resolve_discussion": false, "discussion_id": null, "note": "Some draft note", "commit_id": null, "position": null, "line_code": null }, { "id": 37349979, "author_id": 10271899, "merge_request_id": 291473309, "resolve_discussion": false, "discussion_id": null, "note": "Some draft note 2", "commit_id": null, "line_code": "3dacf79e0d779e2baa1c700cf56510e42f55cf85_10_9", "position": { "base_sha": "64581c4ee41beb44d943d7801f82d9038e25e453", "start_sha": "87bffbff93bf334889780f54ae1922355661f867", "head_sha": "2c972dbf9094c380f5f00dcd8112d2c69b24c859", "old_path": "src/some-dir/some-file.js", "new_path": "src/some-dir/some-file.js", "position_type": "text", "old_line": null, "new_line": 9, "line_range": { "start": { "line_code": "3dacf79e0d779e2baa1c700cf56510e42f55cf85_10_9", "type": "new", "old_line": null, "new_line": 9 }, "end": { "line_code": "3dacf79e0d779e2baa1c700cf56510e42f55cf85_10_9", "type": "new", "old_line": null, "new_line": 9 } } } } ] golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/list_geo_nodes_status.json000066400000000000000000000160751475761473200301740ustar00rootroot00000000000000[ { "geo_node_id": 1, "healthy": true, "health": "Healthy", "health_status": "Healthy", "missing_oauth_application": false, "attachments_count": 1, "attachments_synced_count": null, "attachments_failed_count": null, "attachments_synced_missing_on_primary_count": 0, "attachments_synced_in_percentage": "0.00%", "db_replication_lag_seconds": null, "lfs_objects_count": 0, "lfs_objects_synced_count": null, "lfs_objects_failed_count": null, "lfs_objects_synced_missing_on_primary_count": 0, "lfs_objects_synced_in_percentage": "0.00%", "job_artifacts_count": 2, "job_artifacts_synced_count": null, "job_artifacts_failed_count": null, "job_artifacts_synced_missing_on_primary_count": 0, "job_artifacts_synced_in_percentage": "0.00%", "container_repositories_count": 3, "container_repositories_synced_count": null, "container_repositories_failed_count": null, "container_repositories_synced_in_percentage": "0.00%", "design_repositories_count": 3, "design_repositories_synced_count": null, "design_repositories_failed_count": null, "design_repositories_synced_in_percentage": "0.00%", "projects_count": 41, "repositories_count": 41, "repositories_failed_count": null, "repositories_synced_count": null, "repositories_synced_in_percentage": "0.00%", "wikis_count": 41, "wikis_failed_count": null, "wikis_synced_count": null, "wikis_synced_in_percentage": "0.00%", "replication_slots_count": 1, "replication_slots_used_count": 1, "replication_slots_used_in_percentage": "100.00%", "replication_slots_max_retained_wal_bytes": 0, "repositories_checked_count": 20, "repositories_checked_failed_count": 20, "repositories_checked_in_percentage": "100.00%", "repositories_checksummed_count": 20, "repositories_checksum_failed_count": 5, "repositories_checksummed_in_percentage": "48.78%", "wikis_checksummed_count": 10, "wikis_checksum_failed_count": 3, "wikis_checksummed_in_percentage": "24.39%", "repositories_verified_count": 20, "repositories_verification_failed_count": 5, "repositories_verified_in_percentage": "48.78%", "repositories_checksum_mismatch_count": 3, "wikis_verified_count": 10, "wikis_verification_failed_count": 3, "wikis_verified_in_percentage": "24.39%", "wikis_checksum_mismatch_count": 1, "repositories_retrying_verification_count": 1, "wikis_retrying_verification_count": 3, "last_event_id": 23, "last_event_timestamp": 1509681166, "cursor_last_event_id": null, "cursor_last_event_timestamp": 0, "last_successful_status_check_timestamp": 1510125024, "version": "10.3.0", "revision": "33d33a096a", "merge_request_diffs_count": 5, "merge_request_diffs_checksum_total_count": 5, "merge_request_diffs_checksummed_count": 5, "merge_request_diffs_checksum_failed_count": 0, "merge_request_diffs_synced_count": null, "merge_request_diffs_failed_count": null, "merge_request_diffs_registry_count": null, "merge_request_diffs_verification_total_count": null, "merge_request_diffs_verified_count": null, "merge_request_diffs_verification_failed_count": null, "merge_request_diffs_synced_in_percentage": "0.00%", "merge_request_diffs_verified_in_percentage": "0.00%", "package_files_count": 5, "package_files_checksum_total_count": 5, "package_files_checksummed_count": 5, "package_files_checksum_failed_count": 0, "package_files_synced_count": null, "package_files_failed_count": null, "package_files_registry_count": null, "package_files_verification_total_count": null, "package_files_verified_count": null, "package_files_verification_failed_count": null, "package_files_synced_in_percentage": "0.00%", "package_files_verified_in_percentage": "0.00%", "pages_deployments_count": 5, "pages_deployments_checksum_total_count": 5, "pages_deployments_checksummed_count": 5, "pages_deployments_checksum_failed_count": 0, "pages_deployments_synced_count": null, "pages_deployments_failed_count": null, "pages_deployments_registry_count": null, "pages_deployments_verification_total_count": null, "pages_deployments_verified_count": null, "pages_deployments_verification_failed_count": null, "pages_deployments_synced_in_percentage": "0.00%", "pages_deployments_verified_in_percentage": "0.00%", "terraform_state_versions_count": 5, "terraform_state_versions_checksum_total_count": 5, "terraform_state_versions_checksummed_count": 5, "terraform_state_versions_checksum_failed_count": 0, "terraform_state_versions_synced_count": null, "terraform_state_versions_failed_count": null, "terraform_state_versions_registry_count": null, "terraform_state_versions_verification_total_count": null, "terraform_state_versions_verified_count": null, "terraform_state_versions_verification_failed_count": null, "terraform_state_versions_synced_in_percentage": "0.00%", "terraform_state_versions_verified_in_percentage": "0.00%", "snippet_repositories_count": 5, "snippet_repositories_checksum_total_count": 5, "snippet_repositories_checksummed_count": 5, "snippet_repositories_checksum_failed_count": 0, "snippet_repositories_synced_count": null, "snippet_repositories_failed_count": null, "snippet_repositories_registry_count": null, "snippet_repositories_verification_total_count": null, "snippet_repositories_verified_count": null, "snippet_repositories_verification_failed_count": null, "snippet_repositories_synced_in_percentage": "0.00%", "snippet_repositories_verified_in_percentage": "0.00%", "group_wiki_repositories_count": 5, "group_wiki_repositories_checksum_total_count": 5, "group_wiki_repositories_checksummed_count": 5, "group_wiki_repositories_checksum_failed_count": 0, "group_wiki_repositories_synced_count": null, "group_wiki_repositories_failed_count": null, "group_wiki_repositories_registry_count": null, "group_wiki_repositories_verification_total_count": null, "group_wiki_repositories_verified_count": null, "group_wiki_repositories_verification_failed_count": null, "group_wiki_repositories_synced_in_percentage": "0.00%", "group_wiki_repositories_verified_in_percentage": "0.00%", "pipeline_artifacts_count": 5, "pipeline_artifacts_checksum_total_count": 5, "pipeline_artifacts_checksummed_count": 5, "pipeline_artifacts_checksum_failed_count": 0, "pipeline_artifacts_synced_count": null, "pipeline_artifacts_failed_count": null, "pipeline_artifacts_registry_count": null, "pipeline_artifacts_verification_total_count": null, "pipeline_artifacts_verified_count": null, "pipeline_artifacts_verification_failed_count": null, "pipeline_artifacts_synced_in_percentage": "0.00%", "pipeline_artifacts_verified_in_percentage": "0.00%", "uploads_count": 5, "uploads_synced_count": null, "uploads_failed_count": 0, "uploads_registry_count": null, "uploads_synced_in_percentage": "0.00%" } ] golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/list_group_access_tokens.json000066400000000000000000000011401475761473200306520ustar00rootroot00000000000000[ { "id": 1876, "name": "token 10", "revoked": false, "created_at": "2021-03-09T21:11:47.271Z", "last_used_at": "2021-03-10T21:11:47.271Z", "scopes": ["api", "read_api", "read_repository", "write_repository"], "user_id": 2453, "active": true, "expires_at": null, "access_level": 40 }, { "id": 1877, "name": "token 8", "revoked": false, "created_at": "2021-03-09T21:11:47.340Z", "scopes": ["api", "read_api", "read_repository", "write_repository"], "user_id": 2456, "active": true, "expires_at": null, "access_level": 30 } ] golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/list_group_iterations.json000066400000000000000000000006111475761473200302110ustar00rootroot00000000000000[ { "id": 53, "iid": 13, "sequence": 1, "group_id": 5, "title": "Iteration II", "description": "Ipsum Lorem ipsum", "state": 2, "created_at": "2020-01-27T05:07:12.573Z", "updated_at": "2020-01-27T05:07:12.573Z", "due_date": "2020-02-01", "start_date": "2020-02-14", "web_url": "http://gitlab.example.com/groups/my-group/-/iterations/13" } ] golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/list_group_ssh_certificates.json000066400000000000000000000002331475761473200313520ustar00rootroot00000000000000[ { "id": 1876, "title": "SSH Certificate", "key": "ssh-rsa FAKE-KEY example@gitlab.com", "created_at": "2022-03-20T20:42:40.221Z" } ] golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/list_member_roles.json000066400000000000000000000032251475761473200272730ustar00rootroot00000000000000[ { "id": 1, "name": "GuestCodeReader", "description": "A Guest user that can read code", "group_id": 1, "base_access_level": 10, "admin_cicd_variables": false, "admin_compliance_framework": false, "admin_group_member": false, "admin_merge_request": false, "admin_push_rules": false, "admin_terraform_state": false, "admin_vulnerability": false, "admin_web_hook": false, "archive_project": false, "manage_deploy_tokens": false, "manage_group_access_tokens": false, "manage_merge_request_settings": false, "manage_project_access_tokens": false, "manage_security_policy_link": false, "read_code": true, "read_runners": false, "read_dependency": false, "read_vulnerability": false, "remove_group": false, "remove_project": false }, { "id": 2, "name": "GuestVulnerabilityReader", "description": "A Guest user that can read vulnerabilities", "group_id": 1, "base_access_level": 10, "admin_cicd_variables": false, "admin_compliance_framework": false, "admin_group_member": false, "admin_merge_request": false, "admin_push_rules": false, "admin_terraform_state": false, "admin_vulnerability": false, "admin_web_hook": false, "archive_project": false, "manage_deploy_tokens": false, "manage_group_access_tokens": false, "manage_merge_request_settings": false, "manage_project_access_tokens": false, "manage_security_policy_link": false, "read_code": false, "read_runners": false, "read_dependency": false, "read_vulnerability": true, "remove_group": false, "remove_project": false } ] golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/list_merge_request_diff.json000066400000000000000000000007011475761473200304530ustar00rootroot00000000000000[ { "old_path": "README", "new_path": "README", "a_mode": "100644", "b_mode": "100644", "diff": "@@ -1 +1 @@ -Title +README", "new_file": false, "renamed_file": false, "deleted_file": false }, { "old_path": "VERSION", "new_path": "VERSION", "a_mode": "100644", "b_mode": "100644", "diff": "@@ -1.9.7 +1.9.8", "new_file": false, "renamed_file": false, "deleted_file": false } ] golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/list_merge_requests_in_merge_train.json000066400000000000000000000022761475761473200327210ustar00rootroot00000000000000[ { "id": 267, "merge_request": { "id": 273, "iid": 1, "project_id": 597, "title": "My title 9", "description": null, "state": "opened", "created_at": "2022-10-31T19:06:05.725Z", "updated_at": "2022-10-31T19:06:05.725Z", "web_url": "http://localhost/namespace18/project21/-/merge_requests/1" }, "user": { "id": 933, "username": "user12", "name": "Sidney Jones31", "state": "active", "avatar_url": "https://www.gravatar.com/avatar/6c8365de387cb3db10ecc7b1880203c4?s=80\u0026d=identicon", "web_url": "http://localhost/user12" }, "pipeline": { "id": 273, "iid": 1, "project_id": 598, "sha": "b83d6e391c22777fca1ed3012fce84f633d7fed0", "ref": "main", "status": "pending", "source": "push", "created_at": "2022-10-31T19:06:06.231Z", "updated_at": "2022-10-31T19:06:06.231Z", "web_url": "http://localhost/namespace19/project22/-/pipelines/273" }, "created_at": "2022-10-31T19:06:06.237Z", "updated_at": "2022-10-31T19:06:06.237Z", "target_branch": "main", "status": "idle", "merged_at": null, "duration": null } ] golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/list_merge_trains_in_project.json000066400000000000000000000023551475761473200315160ustar00rootroot00000000000000[ { "id": 110, "merge_request": { "id": 126, "iid": 59, "project_id": 20, "title": "Test MR 1580978354", "description": "", "state": "merged", "created_at": "2020-02-06T08:39:14.883Z", "updated_at": "2020-02-06T08:40:57.038Z", "web_url": "http://local.gitlab.test:8181/root/merge-train-race-condition/-/merge_requests/59" }, "user": { "id": 1, "name": "Administrator", "username": "root", "state": "active", "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon", "web_url": "http://local.gitlab.test:8181/root" }, "pipeline": { "id": 246, "sha": "bcc17a8ffd51be1afe45605e714085df28b80b13", "ref": "refs/merge-requests/59/train", "status": "success", "created_at": "2020-02-06T08:40:42.410Z", "updated_at": "2020-02-06T08:40:46.912Z", "web_url": "http://local.gitlab.test:8181/root/merge-train-race-condition/pipelines/246" }, "created_at": "2020-02-06T08:39:47.217Z", "updated_at": "2020-02-06T08:40:57.720Z", "target_branch": "feature-1580973432", "status": "merged", "merged_at": "2020-02-06T08:40:57.719Z", "duration": 70 } ] golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/list_personal_access_tokens_single.json000066400000000000000000000003411475761473200327040ustar00rootroot00000000000000{ "id": 1, "name": "Test Token", "revoked": false, "created_at": "2020-07-23T14:31:47.729Z", "scopes": ["api"], "user_id": 1, "last_used_at": "2021-10-06T17:58:37.550Z", "active": true, "expires_at": null } list_personal_access_tokens_with_user_filter.json000066400000000000000000000007631475761473200347320ustar00rootroot00000000000000golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata[ { "id": 1, "name": "test 1", "revoked": true, "created_at": "2020-02-20T14:58:56.238Z", "scopes": ["api"], "user_id": 1, "last_used_at": "2021-04-20T16:31:39.105Z", "active": false, "expires_at": "2022-03-21" }, { "id": 2, "name": "test 2", "revoked": false, "created_at": "2022-03-20T03:56:18.968Z", "scopes": ["api", "read_user"], "user_id": 1, "last_used_at": null, "active": false, "expires_at": "2022-03-20" } ] list_personal_access_tokens_without_user_filter.json000066400000000000000000000007631475761473200354620ustar00rootroot00000000000000golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata[ { "id": 1, "name": "test 1", "revoked": true, "created_at": "2020-02-20T14:58:56.238Z", "scopes": ["api"], "user_id": 1, "last_used_at": "2021-04-20T16:31:39.105Z", "active": false, "expires_at": "2022-03-21" }, { "id": 2, "name": "test 2", "revoked": false, "created_at": "2022-03-20T03:56:18.968Z", "scopes": ["api", "read_user"], "user_id": 2, "last_used_at": null, "active": false, "expires_at": "2022-03-20" } ] golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/list_project_access_tokens.json000066400000000000000000000011401475761473200311640ustar00rootroot00000000000000[ { "id": 1876, "name": "token 10", "revoked": false, "created_at": "2021-03-09T21:11:47.271Z", "last_used_at": "2021-03-10T21:11:47.271Z", "scopes": ["api", "read_api", "read_repository", "write_repository"], "user_id": 2453, "active": true, "expires_at": null, "access_level": 40 }, { "id": 1877, "name": "token 8", "revoked": false, "created_at": "2021-03-09T21:11:47.340Z", "scopes": ["api", "read_api", "read_repository", "write_repository"], "user_id": 2456, "active": true, "expires_at": null, "access_level": 30 } ] golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/list_project_feature_flags.json000066400000000000000000000017571475761473200311650ustar00rootroot00000000000000[ { "name": "merge_train", "description": "This feature is about merge train", "active": true, "version": "new_version_flag", "created_at": "2019-11-04T08:13:51Z", "updated_at": "2019-11-04T08:13:11Z", "scopes": [], "strategies": [ { "id": 1, "name": "userWithId", "parameters": { "userIds": "user1" }, "scopes": [ { "id": 1, "environment_scope": "production" } ] } ] }, { "name": "new_live_trace", "description": "This is a new live trace feature", "active": true, "version": "new_version_flag", "created_at": "2019-11-04T08:13:10Z", "updated_at": "2019-11-04T08:13:10Z", "scopes": [], "strategies": [ { "id": 2, "name": "default", "parameters": {}, "scopes": [ { "id": 2, "environment_scope": "staging" } ] } ] } ] golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/list_project_managed_licenses.json000066400000000000000000000002341475761473200316240ustar00rootroot00000000000000[ { "id": 1, "name": "MIT", "approval_status": "approved" }, { "id": 3, "name": "ISC", "approval_status": "blacklisted" } ] golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/list_todos.json000066400000000000000000000004311475761473200257440ustar00rootroot00000000000000[ { "id": 1, "state": "pending", "target": { "id": 1, "approvals_before_merge": 2 } }, { "id": 2, "state": "pending", "target": { "id": "1d76d1b2e3e886108f662765c97f4687f4134d8c", "approvals_before_merge": null } } ] golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/post_bulk_imports_request.json000066400000000000000000000006621475761473200311160ustar00rootroot00000000000000{ "configuration": { "url": "https://source-gitlab-instance.example.com", "access_token": "source-gitlab-instance-access-token" }, "entities": [ { "source_type": "group_entity", "source_full_path": "gitlab-org/gitlab", "destination_slug": "destination_slug", "destination_namespace": "destination/namespace/path", "migrate_projects": true, "migrate_memberships": true } ] } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/post_bulk_imports_response.json000066400000000000000000000003701475761473200312600ustar00rootroot00000000000000{ "id": 1337, "status": "created", "source_type": "group_entity", "source_url": "https://source-gitlab-instance.example.com", "created_at": "2021-06-18T09:45:55.358Z", "updated_at": "2021-06-18T09:46:27.003Z", "has_failures": false } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/post_user_personal_access_tokens.json000066400000000000000000000003561475761473200324210ustar00rootroot00000000000000{ "id": 3, "name": "mytoken", "revoked": false, "created_at": "2020-10-14T11:58:53.526Z", "scopes": [ "k8s_proxy" ], "user_id": 42, "active": true, "expires_at": "2020-10-15", "token": "glpat-aaaaaaaa-bbbbbbbbb" } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/rotate_group_access_token.json000066400000000000000000000004051475761473200310150ustar00rootroot00000000000000{ "id": 42, "name": "Rotated Token", "revoked": false, "created_at": "2023-08-01T15:00:00.000Z", "scopes": ["api"], "user_id": 1337, "last_used_at": null, "active": true, "expires_at": "2023-08-15", "access_level": 30, "token": "s3cr3t" } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/rotate_personal_access_token.json000066400000000000000000000003571475761473200315120ustar00rootroot00000000000000{ "id": 42, "name": "Rotated Token", "revoked": false, "created_at": "2023-08-01T15:00:00.000Z", "scopes": ["api"], "user_id": 1337, "last_used_at": null, "active": true, "expires_at": "2023-08-15", "token": "s3cr3t" } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/rotate_project_access_token.json000066400000000000000000000004051475761473200313270ustar00rootroot00000000000000{ "id": 42, "name": "Rotated Token", "revoked": false, "created_at": "2023-08-01T15:00:00.000Z", "scopes": ["api"], "user_id": 1337, "last_used_at": null, "active": true, "expires_at": "2023-08-15", "access_level": 30, "token": "s3cr3t" } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/search_users.json000066400000000000000000000003601475761473200262500ustar00rootroot00000000000000[ { "id": 1, "name": "John Doe1", "username": "user1", "state": "active", "avatar_url": "http://www.gravatar.com/avatar/c922747a93b40d1ea88262bf1aebee62?s=80&d=identicon", "web_url": "http://localhost/user1" } ] golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/show_merge_request_raw_diff.txt000066400000000000000000000004661475761473200312070ustar00rootroot00000000000000diff --git a/also_testing b/also_testing index d4d510b..2a2c3b1 100644 --- a/also_testing +++ b/also_testing @@ -1,3 +1,2 @@ aaaaaaaaaaaaaaaa -bbbbbbbbbbbbbbbb cccccccccccccccc diff --git a/testing b/testing index c7c7da3..ce2cd85 100644 --- a/testing +++ b/testing @@ -1 +1,2 @@ hello there +i'm a test :) golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/systemhooks/000077500000000000000000000000001475761473200252605ustar00rootroot00000000000000golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/systemhooks/group_create.json000066400000000000000000000003421475761473200306310ustar00rootroot00000000000000{ "created_at": "2012-07-21T07:30:54Z", "updated_at": "2012-07-21T07:38:22Z", "event_name": "group_create", "name": "StoreCloud", "owner_email": null, "owner_name": null, "path": "storecloud", "group_id": 78 } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/systemhooks/group_destroy.json000066400000000000000000000003431475761473200310600ustar00rootroot00000000000000{ "created_at": "2012-07-21T07:30:54Z", "updated_at": "2012-07-21T07:38:22Z", "event_name": "group_destroy", "name": "StoreCloud", "owner_email": null, "owner_name": null, "path": "storecloud", "group_id": 78 } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/systemhooks/group_rename.json000066400000000000000000000005251475761473200306400ustar00rootroot00000000000000{ "event_name": "group_rename", "created_at": "2017-10-30T15:09:00Z", "updated_at": "2017-11-01T10:23:52Z", "name": "Better Name", "path": "better-name", "full_path": "parent-group/better-name", "group_id": 64, "owner_name": null, "owner_email": null, "old_path": "old-name", "old_full_path": "parent-group/old-name" } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/systemhooks/key_create.json000066400000000000000000000010551475761473200302670ustar00rootroot00000000000000{ "event_name": "key_create", "created_at": "2014-08-18 18:45:16 UTC", "updated_at": "2012-07-21T07:38:22Z", "username": "root", "key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC58FwqHUbebw2SdT7SP4FxZ0w+lAO/erhy2ylhlcW/tZ3GY3mBu9VeeiSGoGz8hCx80Zrz+aQv28xfFfKlC8XQFpCWwsnWnQqO2Lv9bS8V1fIHgMxOHIt5Vs+9CAWGCCvUOAurjsUDoE2ALIXLDMKnJxcxD13XjWdK54j6ZXDB4syLF0C2PnAQSVY9X7MfCYwtuFmhQhKaBussAXpaVMRHltie3UYSBUUuZaB3J4cg/7TxlmxcNd+ppPRIpSZAB0NI6aOnqoBCpimscO/VpQRJMVLr3XiSYeT6HBiDXWHnIVPfQc03OGcaFqOit6p8lYKMaP/iUQLm+pgpZqrXZ9vB john@localhost", "id": 4 } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/systemhooks/key_destroy.json000066400000000000000000000010561475761473200305160ustar00rootroot00000000000000{ "event_name": "key_destroy", "created_at": "2014-08-18 18:45:16 UTC", "updated_at": "2012-07-21T07:38:22Z", "username": "root", "key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC58FwqHUbebw2SdT7SP4FxZ0w+lAO/erhy2ylhlcW/tZ3GY3mBu9VeeiSGoGz8hCx80Zrz+aQv28xfFfKlC8XQFpCWwsnWnQqO2Lv9bS8V1fIHgMxOHIt5Vs+9CAWGCCvUOAurjsUDoE2ALIXLDMKnJxcxD13XjWdK54j6ZXDB4syLF0C2PnAQSVY9X7MfCYwtuFmhQhKaBussAXpaVMRHltie3UYSBUUuZaB3J4cg/7TxlmxcNd+ppPRIpSZAB0NI6aOnqoBCpimscO/VpQRJMVLr3XiSYeT6HBiDXWHnIVPfQc03OGcaFqOit6p8lYKMaP/iUQLm+pgpZqrXZ9vB john@localhost", "id": 4 } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/systemhooks/merge_request.json000066400000000000000000000100211475761473200310140ustar00rootroot00000000000000{ "object_kind": "merge_request", "user": { "name": "Administrator", "username": "root", "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon" }, "project": { "name": "Example", "description": "", "web_url": "http://example.com/jsmith/example", "avatar_url": null, "git_ssh_url": "git@example.com:jsmith/example.git", "git_http_url": "http://example.com/jsmith/example.git", "namespace": "Jsmith", "visibility_level": 0, "path_with_namespace": "jsmith/example", "default_branch": "master", "ci_config_path": "", "homepage": "http://example.com/jsmith/example", "url": "git@example.com:jsmith/example.git", "ssh_url": "git@example.com:jsmith/example.git", "http_url": "http://example.com/jsmith/example.git" }, "object_attributes": { "id": 90, "target_branch": "master", "source_branch": "ms-viewport", "source_project_id": 14, "author_id": 51, "assignee_id": 6, "title": "MS-Viewport", "created_at": "2017-09-20T08:31:45.944Z", "updated_at": "2017-09-28T12:23:42.365Z", "milestone_id": null, "state": "opened", "merge_status": "unchecked", "target_project_id": 14, "iid": 1, "description": "", "updated_by_id": 1, "merge_error": null, "merge_params": { "force_remove_source_branch": "0" }, "merge_when_pipeline_succeeds": false, "merge_user_id": null, "merge_commit_sha": null, "deleted_at": null, "in_progress_merge_commit_sha": null, "lock_version": 5, "time_estimate": 0, "last_edited_at": "2017-09-27T12:43:37.558Z", "last_edited_by_id": 1, "head_pipeline_id": 61, "ref_fetched": true, "merge_jid": null, "source": { "name": "Awesome Project", "description": "", "web_url": "http://example.com/awesome_space/awesome_project", "avatar_url": null, "git_ssh_url": "git@example.com:awesome_space/awesome_project.git", "git_http_url": "http://example.com/awesome_space/awesome_project.git", "namespace": "root", "visibility_level": 0, "path_with_namespace": "awesome_space/awesome_project", "default_branch": "master", "ci_config_path": "", "homepage": "http://example.com/awesome_space/awesome_project", "url": "http://example.com/awesome_space/awesome_project.git", "ssh_url": "git@example.com:awesome_space/awesome_project.git", "http_url": "http://example.com/awesome_space/awesome_project.git" }, "target": { "name": "Awesome Project", "description": "Aut reprehenderit ut est.", "web_url": "http://example.com/awesome_space/awesome_project", "avatar_url": null, "git_ssh_url": "git@example.com:awesome_space/awesome_project.git", "git_http_url": "http://example.com/awesome_space/awesome_project.git", "namespace": "Awesome Space", "visibility_level": 0, "path_with_namespace": "awesome_space/awesome_project", "default_branch": "master", "ci_config_path": "", "homepage": "http://example.com/awesome_space/awesome_project", "url": "http://example.com/awesome_space/awesome_project.git", "ssh_url": "git@example.com:awesome_space/awesome_project.git", "http_url": "http://example.com/awesome_space/awesome_project.git" }, "last_commit": { "id": "ba3e0d8ff79c80d5b0bbb4f3e2e343e0aaa662b7", "message": "fixed readme", "timestamp": "2017-09-26T16:12:57Z", "url": "http://example.com/awesome_space/awesome_project/commits/da1560886d4f094c3e6c9ef40349f7d38b5d27d7", "author": { "name": "GitLab dev user", "email": "gitlabdev@dv6700.(none)" } }, "work_in_progress": false, "total_time_spent": 0, "human_total_time_spent": null, "human_time_estimate": null }, "labels": null, "repository": { "name": "git-gpg-test", "url": "git@example.com:awesome_space/awesome_project.git", "description": "", "homepage": "http://example.com/awesome_space/awesome_project" } } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/systemhooks/project_create.json000066400000000000000000000005201475761473200311410ustar00rootroot00000000000000{ "created_at": "2012-07-21T07:30:54Z", "updated_at": "2012-07-21T07:38:22Z", "event_name": "project_create", "name": "StoreCloud", "owner_email": "johnsmith@gmail.com", "owner_name": "John Smith", "path": "storecloud", "path_with_namespace": "jsmith/storecloud", "project_id": 74, "project_visibility": "private" } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/systemhooks/project_destroy.json000066400000000000000000000005221475761473200313710ustar00rootroot00000000000000{ "created_at": "2012-07-21T07:30:58Z", "updated_at": "2012-07-21T07:38:22Z", "event_name": "project_destroy", "name": "Underscore", "owner_email": "johnsmith@gmail.com", "owner_name": "John Smith", "path": "underscore", "path_with_namespace": "jsmith/underscore", "project_id": 73, "project_visibility": "internal" } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/systemhooks/project_rename.json000066400000000000000000000006021475761473200311460ustar00rootroot00000000000000{ "created_at": "2012-07-21T07:30:58Z", "updated_at": "2012-07-21T07:38:22Z", "event_name": "project_rename", "name": "Underscore", "path": "underscore", "path_with_namespace": "jsmith/underscore", "project_id": 73, "owner_name": "John Smith", "owner_email": "johnsmith@gmail.com", "project_visibility": "internal", "old_path_with_namespace": "jsmith/overscore" } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/systemhooks/project_transfer.json000066400000000000000000000006041475761473200315250ustar00rootroot00000000000000{ "created_at": "2012-07-21T07:30:58Z", "updated_at": "2012-07-21T07:38:22Z", "event_name": "project_transfer", "name": "Underscore", "path": "underscore", "path_with_namespace": "scores/underscore", "project_id": 73, "owner_name": "John Smith", "owner_email": "johnsmith@gmail.com", "project_visibility": "internal", "old_path_with_namespace": "jsmith/overscore" } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/systemhooks/project_update.json000066400000000000000000000005201475761473200311600ustar00rootroot00000000000000{ "created_at": "2012-07-21T07:30:54Z", "updated_at": "2012-07-21T07:38:22Z", "event_name": "project_update", "name": "StoreCloud", "owner_email": "johnsmith@gmail.com", "owner_name": "John Smith", "path": "storecloud", "path_with_namespace": "jsmith/storecloud", "project_id": 74, "project_visibility": "private" } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/systemhooks/push.json000066400000000000000000000034241475761473200271350ustar00rootroot00000000000000{ "event_name": "push", "before": "95790bf891e76fee5e1747ab589903a6a1f80f22", "after": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7", "ref": "refs/heads/master", "checkout_sha": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7", "user_id": 4, "user_name": "John Smith", "user_email": "john@example.com", "user_avatar": "https://s.gravatar.com/avatar/d4c74594d841139328695756648b6bd6?s=8://s.gravatar.com/avatar/d4c74594d841139328695756648b6bd6?s=80", "project_id": 15, "project":{ "name":"Diaspora", "description":"", "web_url":"http://example.com/mike/diaspora", "avatar_url":null, "git_ssh_url":"git@example.com:mike/diaspora.git", "git_http_url":"http://example.com/mike/diaspora.git", "namespace":"Mike", "visibility_level":0, "path_with_namespace":"mike/diaspora", "default_branch":"master", "homepage":"http://example.com/mike/diaspora", "url":"git@example.com:mike/diaspora.git", "ssh_url":"git@example.com:mike/diaspora.git", "http_url":"http://example.com/mike/diaspora.git" }, "repository":{ "name": "Diaspora", "url": "git@example.com:mike/diaspora.git", "description": "", "homepage": "http://example.com/mike/diaspora", "git_http_url":"http://example.com/mike/diaspora.git", "git_ssh_url":"git@example.com:mike/diaspora.git", "visibility_level":0 }, "commits": [ { "id": "c5feabde2d8cd023215af4d2ceeb7a64839fc428", "message": "Add simple search to projects in public area", "timestamp": "2013-05-13T18:18:08+00:00", "url": "https://dev.gitlab.org/gitlab/gitlabhq/commit/c5feabde2d8cd023215af4d2ceeb7a64839fc428", "author": { "name": "Dmitriy Zaporozhets", "email": "dmitriy.zaporozhets@gmail.com" } } ], "total_commits_count": 1 } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/systemhooks/repository_update.json000066400000000000000000000021031475761473200317300ustar00rootroot00000000000000{ "event_name": "repository_update", "user_id": 1, "user_name": "John Smith", "user_email": "admin@example.com", "user_avatar": "https://s.gravatar.com/avatar/d4c74594d841139328695756648b6bd6?s=8://s.gravatar.com/avatar/d4c74594d841139328695756648b6bd6?s=80", "project_id": 1, "project": { "name":"Example", "description":"", "web_url":"http://example.com/jsmith/example", "avatar_url":null, "git_ssh_url":"git@example.com:jsmith/example.git", "git_http_url":"http://example.com/jsmith/example.git", "namespace":"Jsmith", "visibility_level":0, "path_with_namespace":"jsmith/example", "default_branch":"master", "homepage":"http://example.com/jsmith/example", "url":"git@example.com:jsmith/example.git", "ssh_url":"git@example.com:jsmith/example.git", "http_url":"http://example.com/jsmith/example.git" }, "changes": [ { "before":"8205ea8d81ce0c6b90fbe8280d118cc9fdad6130", "after":"4045ea7a3df38697b3730a20fb73c8bed8a3e69e", "ref":"refs/heads/master" } ], "refs":["refs/heads/master"] } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/systemhooks/tag_push.json000066400000000000000000000026061475761473200277710ustar00rootroot00000000000000{ "event_name": "tag_push", "before": "0000000000000000000000000000000000000000", "after": "82b3d5ae55f7080f1e6022629cdb57bfae7cccc7", "ref": "refs/tags/v1.0.0", "checkout_sha": "5937ac0a7beb003549fc5fd26fc247adbce4a52e", "user_id": 1, "user_name": "John Smith", "user_avatar": "https://s.gravatar.com/avatar/d4c74594d841139328695756648b6bd6?s=8://s.gravatar.com/avatar/d4c74594d841139328695756648b6bd6?s=80", "project_id": 1, "project": { "name": "Example", "description": "", "web_url": "http://example.com/jsmith/example", "avatar_url": null, "git_ssh_url": "git@example.com:jsmith/example.git", "git_http_url": "http://example.com/jsmith/example.git", "namespace": "Jsmith", "visibility_level": 0, "path_with_namespace": "jsmith/example", "default_branch": "master", "homepage": "http://example.com/jsmith/example", "url": "git@example.com:jsmith/example.git", "ssh_url": "git@example.com:jsmith/example.git", "http_url": "http://example.com/jsmith/example.git" }, "repository": { "name": "Example", "url": "ssh://git@example.com/jsmith/example.git", "description": "", "homepage": "http://example.com/jsmith/example", "git_http_url": "http://example.com/jsmith/example.git", "git_ssh_url": "git@example.com:jsmith/example.git", "visibility_level": 0 }, "commits": [], "total_commits_count": 0 } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/systemhooks/user_add_to_group.json000066400000000000000000000005331475761473200316600ustar00rootroot00000000000000{ "created_at": "2012-07-21T07:30:56Z", "updated_at": "2012-07-21T07:38:22Z", "event_name": "user_add_to_group", "group_access": "Maintainer", "group_id": 78, "group_name": "StoreCloud", "group_path": "storecloud", "user_email": "johnsmith@gmail.com", "user_name": "John Smith", "user_username": "johnsmith", "user_id": 41 } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/systemhooks/user_add_to_team.json000066400000000000000000000007111475761473200314500ustar00rootroot00000000000000{ "created_at": "2012-07-21T07:30:56Z", "updated_at": "2012-07-21T07:38:22Z", "event_name": "user_add_to_team", "access_level": "Maintainer", "project_id": 74, "project_name": "StoreCloud", "project_path": "storecloud", "project_path_with_namespace": "jsmith/storecloud", "user_email": "johnsmith@gmail.com", "user_name": "John Smith", "user_username": "johnsmith", "user_id": 41, "project_visibility": "visibilitylevel|private" } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/systemhooks/user_create.json000066400000000000000000000003151475761473200304530ustar00rootroot00000000000000{ "created_at": "2012-07-21T07:44:07Z", "updated_at": "2012-07-21T07:38:22Z", "email": "js@gitlabhq.com", "event_name": "user_create", "name": "John Smith", "username": "js", "user_id": 41 } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/systemhooks/user_destroy.json000066400000000000000000000003161475761473200307020ustar00rootroot00000000000000{ "created_at": "2012-07-21T07:44:07Z", "updated_at": "2012-07-21T07:38:22Z", "email": "js@gitlabhq.com", "event_name": "user_destroy", "name": "John Smith", "username": "js", "user_id": 41 } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/systemhooks/user_failed_login.json000066400000000000000000000003561475761473200316310ustar00rootroot00000000000000{ "event_name": "user_failed_login", "created_at": "2017-10-03T06:08:48Z", "updated_at": "2018-01-15T04:52:06Z", "name": "John Smith", "email": "user4@example.com", "user_id": 26, "username": "user4", "state": "blocked" } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/systemhooks/user_remove_from_group.json000066400000000000000000000005401475761473200327440ustar00rootroot00000000000000{ "created_at": "2012-07-21T07:30:56Z", "updated_at": "2012-07-21T07:38:22Z", "event_name": "user_remove_from_group", "group_access": "Maintainer", "group_id": 78, "group_name": "StoreCloud", "group_path": "storecloud", "user_email": "johnsmith@gmail.com", "user_name": "John Smith", "user_username": "johnsmith", "user_id": 41 } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/systemhooks/user_remove_from_team.json000066400000000000000000000007161475761473200325430ustar00rootroot00000000000000{ "created_at": "2012-07-21T07:30:56Z", "updated_at": "2012-07-21T07:38:22Z", "event_name": "user_remove_from_team", "access_level": "Maintainer", "project_id": 74, "project_name": "StoreCloud", "project_path": "storecloud", "project_path_with_namespace": "jsmith/storecloud", "user_email": "johnsmith@gmail.com", "user_name": "John Smith", "user_username": "johnsmith", "user_id": 41, "project_visibility": "visibilitylevel|private" } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/systemhooks/user_rename.json000066400000000000000000000004061475761473200304600ustar00rootroot00000000000000{ "event_name": "user_rename", "created_at": "2017-11-01T11:21:04Z", "updated_at": "2017-11-01T14:04:47Z", "name": "new-name", "email": "best-email@example.tld", "user_id": 58, "username": "new-exciting-name", "old_username": "old-boring-name" } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/systemhooks/user_update_for_group.json000066400000000000000000000005371475761473200325620ustar00rootroot00000000000000{ "created_at": "2012-07-21T07:30:56Z", "updated_at": "2012-07-21T07:38:22Z", "event_name": "user_update_for_group", "group_access": "Maintainer", "group_id": 78, "group_name": "StoreCloud", "group_path": "storecloud", "user_email": "johnsmith@gmail.com", "user_name": "John Smith", "user_username": "johnsmith", "user_id": 41 } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/systemhooks/user_update_for_team.json000066400000000000000000000007151475761473200323520ustar00rootroot00000000000000{ "created_at": "2012-07-21T07:30:56Z", "updated_at": "2012-07-21T07:38:22Z", "event_name": "user_update_for_team", "access_level": "Maintainer", "project_id": 74, "project_name": "StoreCloud", "project_path": "storecloud", "project_path_with_namespace": "jsmith/storecloud", "user_email": "johnsmith@gmail.com", "user_name": "John Smith", "user_username": "johnsmith", "user_id": 41, "project_visibility": "visibilitylevel|private" } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/update_draft_note.json000066400000000000000000000003521475761473200272520ustar00rootroot00000000000000{ "id": 37349980, "author_id": 10271899, "merge_request_id": 291473309, "resolve_discussion": false, "discussion_id": null, "note": "Some changed draft note", "commit_id": null, "position": null, "line_code": null } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/update_project_feature_flag.json000066400000000000000000000012421475761473200312760ustar00rootroot00000000000000{ "name": "awesome_feature", "description": null, "active": true, "version": "new_version_flag", "created_at": "2020-05-13T20:10:32Z", "updated_at": "2020-05-13T20:10:32Z", "scopes": [], "strategies": [ { "id": 38, "name": "gradualRolloutUserId", "parameters": { "groupId": "default", "percentage": "25" }, "scopes": [ { "id": 40, "environment_scope": "staging" } ] }, { "id": 37, "name": "default", "parameters": {}, "scopes": [ { "id": 39, "environment_scope": "production" } ] } ] } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/webhooks/000077500000000000000000000000001475761473200245115ustar00rootroot00000000000000golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/webhooks/build.json000066400000000000000000000032221475761473200265020ustar00rootroot00000000000000{ "object_kind": "build", "ref": "gitlab-script-trigger", "tag": false, "before_sha": "2293ada6b400935a1378653304eaf6221e0fdb8f", "sha": "2293ada6b400935a1378653304eaf6221e0fdb8f", "build_id": 1977, "build_name": "test", "build_stage": "test", "build_status": "created", "build_created_at": "2021-02-23T02:41:37.886Z", "build_started_at": null, "build_finished_at": null, "build_duration": null, "build_allow_failure": false, "build_failure_reason": "script_failure", "pipeline_id": 2366, "project_id": 380, "project_name": "gitlab-org/gitlab-test", "user": { "id": 42, "name": "User1", "username": "user1", "email": "user1@example.com", "avatar_url": "http://www.gravatar.com/avatar/e32bd13e2add097461cb96824b7a829c?s=80\u0026d=identicon" }, "commit": { "id": 2366, "sha": "2293ada6b400935a1378653304eaf6221e0fdb8f", "message": "test\n", "author_name": "User", "author_email": "user@gitlab.com", "status": "created", "duration": null, "started_at": null, "finished_at": null }, "repository": { "name": "gitlab_test", "description": "Atque in sunt eos similique dolores voluptatem.", "homepage": "http://192.168.64.1:3005/gitlab-org/gitlab-test", "git_ssh_url": "git@192.168.64.1:gitlab-org/gitlab-test.git", "git_http_url": "http://192.168.64.1:3005/gitlab-org/gitlab-test.git", "visibility_level": 20 }, "runner": { "active": true, "runner_type": "project_type", "is_shared": false, "id": 380987, "description": "shared-runners-manager-6.gitlab.com", "tags": [ "linux", "docker" ] }, "environment": null } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/webhooks/deployment.json000066400000000000000000000031621475761473200275660ustar00rootroot00000000000000{ "object_kind": "deployment", "status": "success", "status_changed_at":"2021-04-28 21:50:00 +0200", "deployment_id": 15, "deployable_id": 796, "deployable_url": "http://10.126.0.2:3000/root/test-deployment-webhooks/-/jobs/796", "environment": "staging", "environment_slug": "staging", "environment_external_url": "https://staging.example.com", "project": { "id": 30, "name": "test-deployment-webhooks", "description": "", "web_url": "http://10.126.0.2:3000/root/test-deployment-webhooks", "avatar_url": null, "git_ssh_url": "ssh://vlad@10.126.0.2:2222/root/test-deployment-webhooks.git", "git_http_url": "http://10.126.0.2:3000/root/test-deployment-webhooks.git", "namespace": "User1", "visibility_level": 0, "path_with_namespace": "root/test-deployment-webhooks", "default_branch": "master", "ci_config_path": "", "homepage": "http://10.126.0.2:3000/root/test-deployment-webhooks", "url": "ssh://vlad@10.126.0.2:2222/root/test-deployment-webhooks.git", "ssh_url": "ssh://vlad@10.126.0.2:2222/root/test-deployment-webhooks.git", "http_url": "http://10.126.0.2:3000/root/test-deployment-webhooks.git" }, "short_sha": "279484c0", "user": { "id": 42, "name": "User1", "username": "user1", "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon", "email": "admin@example.com" }, "user_url": "http://10.126.0.2:3000/root", "commit_url": "http://10.126.0.2:3000/root/test-deployment-webhooks/-/commit/279484c09fbe69ededfced8c1bb6e6d24616b468", "commit_title": "Add new file", "ref": "1.0.0" } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/webhooks/feature_flag.json000066400000000000000000000023071475761473200300320ustar00rootroot00000000000000{ "object_kind": "feature_flag", "project": { "id": 1, "name":"Gitlab Test", "description":"Aut reprehenderit ut est.", "web_url":"http://example.com/gitlabhq/gitlab-test", "avatar_url":null, "git_ssh_url":"git@example.com:gitlabhq/gitlab-test.git", "git_http_url":"http://example.com/gitlabhq/gitlab-test.git", "namespace":"GitlabHQ", "visibility_level":20, "path_with_namespace":"gitlabhq/gitlab-test", "default_branch":"master", "ci_config_path": null, "homepage":"http://example.com/gitlabhq/gitlab-test", "url":"http://example.com/gitlabhq/gitlab-test.git", "ssh_url":"git@example.com:gitlabhq/gitlab-test.git", "http_url":"http://example.com/gitlabhq/gitlab-test.git" }, "user": { "id": 1, "name": "Administrator", "username": "root", "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon", "email": "admin@example.com" }, "user_url": "http://example.com/root", "object_attributes": { "id": 6, "name": "test-feature-flag", "description": "test-feature-flag-description", "active": true } } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/webhooks/group_merge_request.json000066400000000000000000000105031475761473200314660ustar00rootroot00000000000000{ "object_kind": "merge_request", "user": { "id": 42, "name": "User1", "username": "user1", "email": "user1@mail.com", "avatar_url": "http://www.gravatar.com/avatar/d22738dc40839e3d95fca77ca3eac067?s=80\u0026d=identicon" }, "project": { "name": "example-project", "description": "", "web_url": "http://example.com/exm-namespace/example-project", "avatar_url": null, "git_ssh_url": "git@example.com:exm-namespace/example-project.git", "git_http_url": "http://example.com/exm-namespace/example-project.git", "namespace": "exm-namespace", "visibility": "public", "path_with_namespace": "exm-namespace/example-project", "default_branch": "master", "homepage": "http://example.com/exm-namespace/example-project", "url": "git@example.com:exm-namespace/example-project.git", "ssh_url": "git@example.com:exm-namespace/example-project.git", "http_url": "http://example.com/exm-namespace/example-project.git" }, "object_attributes": { "id": 15917, "target_branch ": "master", "source_branch ": "source-branch-test", "source_project_id ": 87, "author_id ": 15, "assignee_id ": 29, "title ": "source-branch-test ", "created_at ": "2016-12-01 13:11:10 UTC", "updated_at ": "2016-12-01 13:21:20 UTC", "milestone_id ": null, "state ": "merged ", "merge_status ": "can_be_merged ", "target_project_id ": 87, "iid ": 1402, "description ": "word doc support for e-ticket", "position ": 0, "locked_at ": null, "updated_by_id ": null, "merge_error ": null, "merge_params": { "force_remove_source_branch": "0" }, "merge_when_build_succeeds": false, "merge_user_id": null, "merge_commit_sha": "ac3ca1559bc39abf963586372eff7f8fdded646e", "deleted_at": null, "approvals_before_merge": null, "rebase_commit_sha": null, "in_progress_merge_commit_sha": null, "lock_version": 0, "time_estimate": 0, "source": { "name": "example-project", "description": "", "web_url": "http://example.com/exm-namespace/example-project", "avatar_url": null, "git_ssh_url": "git@example.com:exm-namespace/example-project.git", "git_http_url": "http://example.com/exm-namespace/example-project.git", "namespace": "exm-namespace", "visibility": "public", "path_with_namespace": "exm-namespace/example-project", "default_branch": "master", "homepage": "http://example.com/exm-namespace/example-project", "url": "git@example.com:exm-namespace/example-project.git", "ssh_url": "git@example.com:exm-namespace/example-project.git", "http_url": "http://example.com/exm-namespace/example-project.git" }, "target": { "name": "example-project", "description": "", "web_url": "http://example.com/exm-namespace/example-project", "avatar_url": null, "git_ssh_url": "git@example.com:exm-namespace/example-project.git", "git_http_url": "http://example.com/exm-namespace/example-project.git", "namespace": "exm-namespace", "visibility": "public", "path_with_namespace": "exm-namespace/example-project", "default_branch": "master", "homepage": "http://example.com/exm-namespace/example-project", "url": "git@example.com:exm-namespace/example-project.git", "ssh_url": "git@example.com:exm-namespace/example-project.git", "http_url": "http://example.com/exm-namespace/example-project.git" }, "last_commit": { "id": "61b6a0d35dbaf915760233b637622e383d3cc9ec", "message": "commit message", "timestamp": "2016-12-01T15:07:53+02:00", "url": "http://example.com/exm-namespace/example-project/commit/61b6a0d35dbaf915760233b637622e383d3cc9ec", "author": { "name": "Test User", "email": "test.user@mail.com" } }, "work_in_progress": false, "url": "http://example.com/exm-namespace/example-project/merge_requests/1402", "action": "merge" }, "repository": { "name": "example-project", "url": "git@example.com:exm-namespace/example-project.git", "description": "", "homepage": "http://example.com/exm-namespace/example-project" }, "assignee": { "name": "User1", "username": "user1", "avatar_url": "http://www.gravatar.com/avatar/d22738dc40839e3d95fca77ca3eac067?s=80\u0026d=identicon" } } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/webhooks/issue.json000066400000000000000000000110241475761473200265320ustar00rootroot00000000000000{ "object_kind": "issue", "event_type": "issue", "user": { "id": 1, "name": "Administrator", "username": "root", "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon", "email": "admin@example.com" }, "project": { "id": 1, "name":"Gitlab Test", "description":"Aut reprehenderit ut est.", "web_url":"http://example.com/gitlabhq/gitlab-test", "avatar_url":null, "git_ssh_url":"git@example.com:gitlabhq/gitlab-test.git", "git_http_url":"http://example.com/gitlabhq/gitlab-test.git", "namespace":"GitlabHQ", "visibility_level":20, "path_with_namespace":"gitlabhq/gitlab-test", "default_branch":"master", "ci_config_path": null, "homepage":"http://example.com/gitlabhq/gitlab-test", "url":"http://example.com/gitlabhq/gitlab-test.git", "ssh_url":"git@example.com:gitlabhq/gitlab-test.git", "http_url":"http://example.com/gitlabhq/gitlab-test.git" }, "object_attributes": { "id": 301, "title": "New API: create/update/delete file", "assignee_ids": [51], "assignee_id": 51, "author_id": 51, "project_id": 14, "created_at": "2013-12-03T17:15:43Z", "updated_at": "2013-12-03T17:15:43Z", "updated_by_id": 1, "last_edited_at": null, "last_edited_by_id": null, "relative_position": 0, "description": "Create new API for manipulations with repository", "milestone_id": null, "state_id": 1, "confidential": false, "discussion_locked": true, "due_date": null, "moved_to_id": null, "duplicated_to_id": null, "time_estimate": 0, "total_time_spent": 0, "time_change": 0, "human_total_time_spent": null, "human_time_estimate": null, "human_time_change": null, "weight": 10, "iid": 23, "url": "http://example.com/diaspora/issues/23", "state": "opened", "action": "open", "severity": "high", "escalation_status": "triggered", "escalation_policy": { "id": 18, "name": "Engineering On-call" }, "labels": [{ "id": 206, "title": "API", "color": "#ffffff", "project_id": 14, "created_at": "2013-12-03T17:15:43Z", "updated_at": "2013-12-03T17:15:43Z", "template": false, "description": "API related issues", "type": "ProjectLabel", "group_id": 41 }] }, "repository": { "name": "Gitlab Test", "url": "http://example.com/gitlabhq/gitlab-test.git", "description": "Aut reprehenderit ut est.", "homepage": "http://example.com/gitlabhq/gitlab-test" }, "assignees": [{ "name": "User1", "username": "user1", "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon" }], "assignee": { "name": "User1", "username": "user1", "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon" }, "labels": [{ "id": 206, "title": "API", "color": "#ffffff", "project_id": 14, "created_at": "2013-12-03T17:15:43Z", "updated_at": "2013-12-03T17:15:43Z", "template": false, "description": "API related issues", "type": "ProjectLabel", "group_id": 41 }], "changes": { "updated_by_id": { "previous": null, "current": 1 }, "updated_at": { "previous": "2017-09-15 16:50:55 UTC", "current": "2017-09-15 16:52:00 UTC" }, "closed_at": { "previous": "2017-09-15 16:54:55 UTC", "current": "2017-09-15 16:56:00 UTC" }, "state_id": { "previous": 0, "current": 1 }, "labels": { "previous": [{ "id": 206, "title": "API", "color": "#ffffff", "project_id": 14, "created_at": "2013-12-03T17:15:43Z", "updated_at": "2013-12-03T17:15:43Z", "template": false, "description": "API related issues", "type": "ProjectLabel", "group_id": 41 }], "current": [{ "id": 205, "title": "Platform", "color": "#123123", "project_id": 14, "created_at": "2013-12-03T17:15:43Z", "updated_at": "2013-12-03T17:15:43Z", "template": false, "description": "Platform related issues", "type": "ProjectLabel", "group_id": 41 }] }, "description": { "previous": null, "current": "New description" }, "title": { "previous": null, "current": "New title" }, "total_time_spent": { "previous": 8100, "current": 9900 } } } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/webhooks/job.json000066400000000000000000000040621475761473200261600ustar00rootroot00000000000000{ "object_kind": "build", "ref": "main", "tag": false, "before_sha": "0000000000000000000000000000000000000000", "sha": "95d49d1efbd941908580e79d65e4b5ecaf4a8305", "build_id": 3580121225, "build_name": "auto_deploy:start", "build_stage": "coordinated:tag", "build_status": "success", "build_created_at": "2023-01-10 13:50:02 UTC", "build_started_at": "2023-01-10 13:50:05 UTC", "build_finished_at": "2023-01-10 13:50:54 UTC", "build_duration": 49.503592, "build_queued_duration": 0.193009, "build_allow_failure": false, "build_failure_reason": "unknown_failure", "retries_count": 1, "pipeline_id": 743121198, "project_id": 31537070, "project_name": "John Smith / release-tools-fake", "runner": { "id": 12270837, "description": "4-blue.shared.runners-manager.gitlab.com/default", "runner_type": "instance_type", "active": true, "is_shared": true, "tags": [ "linux", "docker" ] }, "user": { "id": 2967854, "name": "John Smith", "username": "jsmithy2", "avatar_url": "https://gitlab.com/uploads/-/system/user/avatar/2967852/avatar.png", "email": "john@smith.com" }, "commit": { "id": 743121198, "name": "Build pipeline", "sha": "95d49d1efbd941908580e79d65e4b5ecaf4a8305", "message": "Remove test jobs and add back other jobs", "author_name": "John Smith", "author_email": "john@smith.com", "author_url": "https://gitlab.com/jsmithy2", "status": "running", "duration": 128, "started_at": "2023-01-10 13:50:05 UTC", "finished_at": "2022-10-12 08:09:29 UTC" }, "repository": { "name": "release-tools-fake", "url": "git@gitlab.com:jsmithy2/release-tools-fake.git", "description": "", "homepage": "https://gitlab.com/jsmithy2/release-tools-fake", "git_http_url": "https://gitlab.com/jsmithy2/release-tools-fake.git", "git_ssh_url": "git@gitlab.com:jsmithy2/release-tools-fake.git", "visibility_level": 20 }, "environment": { "name": "production", "action": "start", "deployment_tier": "production" } } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/webhooks/member.json000066400000000000000000000006251475761473200266560ustar00rootroot00000000000000{ "created_at": "2020-12-11T04:57:22Z", "updated_at": "2020-12-11T04:57:22Z", "group_name": "webhook-test", "group_path": "webhook-test", "group_id": 100, "user_username": "user1", "user_name": "User1", "user_email": "testuser@webhooktest.com", "user_id": 64, "group_access": "Guest", "group_plan": null, "expires_at": "2020-12-14T00:00:00Z", "event_name": "user_add_to_group" } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/webhooks/merge_request.json000066400000000000000000000140201475761473200302500ustar00rootroot00000000000000{ "object_kind": "merge_request", "event_type": "merge_request", "user": { "id": 1, "name": "User1", "username": "user1", "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon", "email": "user1@example.com" }, "project": { "id": 1, "name":"Gitlab Test", "description":"Aut reprehenderit ut est.", "web_url":"http://example.com/gitlabhq/gitlab-test", "avatar_url":null, "git_ssh_url":"git@example.com:gitlabhq/gitlab-test.git", "git_http_url":"http://example.com/gitlabhq/gitlab-test.git", "namespace":"GitlabHQ", "visibility_level":20, "path_with_namespace":"gitlabhq/gitlab-test", "default_branch":"master", "homepage":"http://example.com/gitlabhq/gitlab-test", "url":"http://example.com/gitlabhq/gitlab-test.git", "ssh_url":"git@example.com:gitlabhq/gitlab-test.git", "http_url":"http://example.com/gitlabhq/gitlab-test.git" }, "repository": { "name": "Gitlab Test", "url": "http://example.com/gitlabhq/gitlab-test.git", "description": "Aut reprehenderit ut est.", "homepage": "http://example.com/gitlabhq/gitlab-test" }, "object_attributes": { "id": 99, "iid": 1, "target_branch": "master", "source_branch": "ms-viewport", "source_project_id": 14, "author_id": 51, "assignee_ids": [1], "assignee_id": 1, "reviewer_ids": [1], "title": "MS-Viewport", "created_at": "2013-12-03T17:23:34Z", "updated_at": "2013-12-03T17:23:34Z", "milestone_id": null, "state": "opened", "blocking_discussions_resolved": true, "work_in_progress": false, "first_contribution": true, "merge_status": "unchecked", "target_project_id": 14, "description": "", "url": "http://example.com/diaspora/merge_requests/1", "source": { "name":"Awesome Project", "description":"Aut reprehenderit ut est.", "web_url":"http://example.com/awesome_space/awesome_project", "avatar_url":null, "git_ssh_url":"git@example.com:awesome_space/awesome_project.git", "git_http_url":"http://example.com/awesome_space/awesome_project.git", "namespace":"Awesome Space", "visibility_level":20, "path_with_namespace":"awesome_space/awesome_project", "default_branch":"master", "homepage":"http://example.com/awesome_space/awesome_project", "url":"http://example.com/awesome_space/awesome_project.git", "ssh_url":"git@example.com:awesome_space/awesome_project.git", "http_url":"http://example.com/awesome_space/awesome_project.git" }, "target": { "name":"Awesome Project", "description":"Aut reprehenderit ut est.", "web_url":"http://example.com/awesome_space/awesome_project", "avatar_url":null, "git_ssh_url":"git@example.com:awesome_space/awesome_project.git", "git_http_url":"http://example.com/awesome_space/awesome_project.git", "namespace":"Awesome Space", "visibility_level":20, "path_with_namespace":"awesome_space/awesome_project", "default_branch":"master", "homepage":"http://example.com/awesome_space/awesome_project", "url":"http://example.com/awesome_space/awesome_project.git", "ssh_url":"git@example.com:awesome_space/awesome_project.git", "http_url":"http://example.com/awesome_space/awesome_project.git" }, "last_edited_at":"2023-03-27 00:03:05 UTC", "last_edited_by_id": 51, "state_id": 1, "last_commit": { "id": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7", "message": "fixed readme", "title": "MR Title", "timestamp": "2012-01-03T23:36:29+02:00", "url": "http://example.com/awesome_space/awesome_project/commits/da1560886d4f094c3e6c9ef40349f7d38b5d27d7", "author": { "name": "GitLab dev user", "email": "gitlabdev@dv6700.(none)" } }, "total_time_spent": 0, "time_change": 0, "human_total_time_spent": "30m", "human_time_change": "30m", "human_time_estimate": "1h", "labels": [{ "id": 206, "title": "API", "color": "#ffffff", "project_id": 14, "created_at": "2013-12-03T17:15:43Z", "updated_at": "2013-12-03T17:15:43Z", "template": false, "description": "API related issues", "type": "ProjectLabel", "group_id": 41 }], "action": "open", "detailed_merge_status": "mergeable" }, "labels": [{ "id": 206, "title": "API", "color": "#ffffff", "project_id": 14, "created_at": "2013-12-03T17:15:43Z", "updated_at": "2013-12-03T17:15:43Z", "template": false, "description": "API related issues", "type": "ProjectLabel", "group_id": 41 }], "changes": { "updated_by_id": { "previous": null, "current": 1 }, "updated_at": { "previous": "2017-09-15 16:50:55 UTC", "current":"2017-09-15 16:52:00 UTC" }, "state_id": { "previous": 4, "current": 3 }, "labels": { "previous": [{ "id": 206, "title": "API", "color": "#ffffff", "project_id": 14, "created_at": "2013-12-03T17:15:43Z", "updated_at": "2013-12-03T17:15:43Z", "template": false, "description": "API related issues", "type": "ProjectLabel", "group_id": 41 }], "current": [{ "id": 205, "title": "Platform", "color": "#123123", "project_id": 14, "created_at": "2013-12-03T17:15:43Z", "updated_at": "2013-12-03T17:15:43Z", "template": false, "description": "Platform related issues", "type": "ProjectLabel", "group_id": 41 }] } }, "assignees": [ { "id": 1, "name": "User1", "username": "user1", "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon" } ], "reviewers": [ { "id": 1, "name": "User1", "username": "user1", "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon" } ] } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/webhooks/note_commit.json000066400000000000000000000051551475761473200277270ustar00rootroot00000000000000{ "object_kind": "note", "event_type": "note", "user": { "id": 42, "name": "User1", "username": "user1", "email": "user1@example.com", "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon" }, "project_id": 5, "project": { "id": 5, "name": "Gitlab Test", "description": "Aut reprehenderit ut est.", "web_url": "http://example.com/gitlabhq/gitlab-test", "avatar_url": null, "git_ssh_url": "git@example.com:gitlabhq/gitlab-test.git", "git_http_url": "http://example.com/gitlabhq/gitlab-test.git", "namespace": "GitlabHQ", "visibility_level": 20, "path_with_namespace": "gitlabhq/gitlab-test", "default_branch": "master", "homepage": "http://example.com/gitlabhq/gitlab-test", "url": "http://example.com/gitlabhq/gitlab-test.git", "ssh_url": "git@example.com:gitlabhq/gitlab-test.git", "http_url": "http://example.com/gitlabhq/gitlab-test.git" }, "repository": { "name": "Gitlab Test", "url": "http://example.com/gitlab-org/gitlab-test.git", "description": "Aut reprehenderit ut est.", "homepage": "http://example.com/gitlab-org/gitlab-test" }, "object_attributes": { "id": 1243, "note": "This is a commit comment. How does this work?", "noteable_type": "Commit", "author_id": 1, "created_at": "2015-05-17 18:08:09 UTC", "updated_at": "2015-05-17 18:08:09 UTC", "project_id": 5, "attachment": null, "line_code": "bec9703f7a456cd2b4ab5fb3220ae016e3e394e3_0_1", "commit_id": "cfe32cf61b73a0d5e9f13e774abde7ff789b1660", "noteable_id": null, "system": false, "st_diff": { "diff": "--- /dev/null\n+++ b/six\n@@ -0,0 +1 @@\n+Subproject commit 409f37c4f05865e4fb208c771485f211a22c4c2d\n", "new_path": "six", "old_path": "six", "a_mode": "0", "b_mode": "160000", "new_file": true, "renamed_file": false, "deleted_file": false }, "description": "This is a commit comment. How does this work?", "action": "create", "url": "http://example.com/gitlab-org/gitlab-test/commit/cfe32cf61b73a0d5e9f13e774abde7ff789b1660#note_1243" }, "commit": { "id": "cfe32cf61b73a0d5e9f13e774abde7ff789b1660", "title": "Add submodule", "message": "Add submodule\n\nSigned-off-by: Dmitriy Zaporozhets \u003cdmitriy.zaporozhets@gmail.com\u003e\n", "timestamp": "2014-02-27T10:06:20+02:00", "url": "http://example.com/gitlab-org/gitlab-test/commit/cfe32cf61b73a0d5e9f13e774abde7ff789b1660", "author": { "name": "Dmitriy Zaporozhets", "email": "dmitriy.zaporozhets@gmail.com" } } } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/webhooks/note_issue.json000066400000000000000000000054531475761473200275700ustar00rootroot00000000000000{ "object_kind": "note", "event_type": "note", "user": { "id": 42, "name": "User1", "username": "user1", "email": "user1@example.com", "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon" }, "project_id": 5, "project": { "id": 5, "name": "Gitlab Test", "description": "Aut reprehenderit ut est.", "web_url": "http://example.com/gitlab-org/gitlab-test", "avatar_url": null, "git_ssh_url": "git@example.com:gitlab-org/gitlab-test.git", "git_http_url": "http://example.com/gitlab-org/gitlab-test.git", "namespace": "Gitlab Org", "visibility_level": 10, "path_with_namespace": "gitlab-org/gitlab-test", "default_branch": "master", "homepage": "http://example.com/gitlab-org/gitlab-test", "url": "http://example.com/gitlab-org/gitlab-test.git", "ssh_url": "git@example.com:gitlab-org/gitlab-test.git", "http_url": "http://example.com/gitlab-org/gitlab-test.git" }, "repository": { "name": "diaspora", "url": "git@example.com:mike/diaspora.git", "description": "", "homepage": "http://example.com/mike/diaspora" }, "object_attributes": { "id": 1241, "note": "Hello world", "noteable_type": "Issue", "author_id": 1, "created_at": "2015-05-17 17:06:40 UTC", "updated_at": "2015-05-17 17:06:40 UTC", "project_id": 5, "attachment": null, "line_code": null, "commit_id": "", "noteable_id": 92, "system": false, "st_diff": null, "description": "Hello world", "action": "create", "url": "http://example.com/gitlab-org/gitlab-test/issues/17#note_1241" }, "issue": { "id": 92, "title": "test_issue", "assignee_ids": [], "assignee_id": null, "author_id": 1, "project_id": 5, "created_at": "2016-01-04T15:31:46.176Z", "updated_at": "2016-01-04T15:31:46.176Z", "position": 0, "branch_name": null, "description": "test issue", "milestone_id": null, "state": "closed", "iid": 17, "time_estimate": 3600, "total_time_spent": 600, "human_time_estimate": "1h", "human_total_time_spent": "10m", "labels": [ { "id": 25, "title": "Afterpod", "color": "#3e8068", "project_id": null, "created_at": "2019-06-05T14:32:20.211Z", "updated_at": "2019-06-05T14:32:20.211Z", "template": false, "description": null, "type": "GroupLabel", "group_id": 4 }, { "id": 86, "title": "Element", "color": "#231afe", "project_id": 4, "created_at": "2019-06-05T14:32:20.637Z", "updated_at": "2019-06-05T14:32:20.637Z", "template": false, "description": null, "type": "ProjectLabel", "group_id": null } ] } } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/webhooks/note_merge_request.json000066400000000000000000000121461475761473200313040ustar00rootroot00000000000000{ "object_kind": "note", "event_type": "note", "user": { "id": 1, "name": "Administrator", "username": "root", "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon", "email": "admin@example.com" }, "project_id": 5, "project": { "id": 5, "name": "Gitlab Test", "description": "Aut reprehenderit ut est.", "web_url": "http://example.com/gitlab-org/gitlab-test", "avatar_url": null, "git_ssh_url": "git@example.com:gitlab-org/gitlab-test.git", "git_http_url": "http://example.com/gitlab-org/gitlab-test.git", "namespace": "Gitlab Org", "visibility_level": 10, "path_with_namespace": "gitlab-org/gitlab-test", "default_branch": "master", "homepage": "http://example.com/gitlab-org/gitlab-test", "url": "http://example.com/gitlab-org/gitlab-test.git", "ssh_url": "git@example.com:gitlab-org/gitlab-test.git", "http_url": "http://example.com/gitlab-org/gitlab-test.git" }, "repository": { "name": "Gitlab Test", "url": "http://localhost/gitlab-org/gitlab-test.git", "description": "Aut reprehenderit ut est.", "homepage": "http://example.com/gitlab-org/gitlab-test" }, "object_attributes": { "id": 1244, "note": "This MR needs work.", "noteable_type": "MergeRequest", "author_id": 1, "created_at": "2015-05-17 18:21:36 UTC", "updated_at": "2015-05-17 18:21:36 UTC", "project_id": 5, "attachment": null, "line_code": null, "commit_id": "", "noteable_id": 7, "system": false, "st_diff": null, "action": "create", "url": "http://example.com/gitlab-org/gitlab-test/merge_requests/1#note_1244" }, "merge_request": { "id": 7, "target_branch": "markdown", "source_branch": "master", "source_project_id": 5, "author_id": 8, "assignee_id": 28, "title": "Tempora et eos debitis quae laborum et.", "created_at": "2015-03-01 20:12:53 UTC", "updated_at": "2015-03-21 18:27:27 UTC", "milestone_id": 11, "state": "opened", "merge_status": "cannot_be_merged", "target_project_id": 5, "iid": 1, "description": "Et voluptas corrupti assumenda temporibus. Architecto cum animi eveniet amet asperiores. Vitae numquam voluptate est natus sit et ad id.", "position": 0, "labels": [ { "id": 206, "title": "Afterpod", "color": "#3e8068", "project_id": null, "created_at": "2019-06-05T14:32:20.211Z", "updated_at": "2019-06-05T14:32:20.211Z", "template": false, "description": null, "type": "GroupLabel", "group_id": 4 }, { "id": 86, "title": "Element", "color": "#231afe", "project_id": 4, "created_at": "2019-06-05T14:32:20.637Z", "updated_at": "2019-06-05T14:32:20.637Z", "template": false, "description": null, "type": "ProjectLabel", "group_id": null } ], "source": { "name": "Gitlab Test", "description": "Aut reprehenderit ut est.", "web_url": "http://example.com/gitlab-org/gitlab-test", "avatar_url": null, "git_ssh_url": "git@example.com:gitlab-org/gitlab-test.git", "git_http_url": "http://example.com/gitlab-org/gitlab-test.git", "namespace": "Gitlab Org", "visibility_level": 10, "path_with_namespace": "gitlab-org/gitlab-test", "default_branch": "master", "homepage": "http://example.com/gitlab-org/gitlab-test", "url": "http://example.com/gitlab-org/gitlab-test.git", "ssh_url": "git@example.com:gitlab-org/gitlab-test.git", "http_url": "http://example.com/gitlab-org/gitlab-test.git" }, "target": { "name": "Gitlab Test", "description": "Aut reprehenderit ut est.", "web_url": "http://example.com/gitlab-org/gitlab-test", "avatar_url": null, "git_ssh_url": "git@example.com:gitlab-org/gitlab-test.git", "git_http_url": "http://example.com/gitlab-org/gitlab-test.git", "namespace": "Gitlab Org", "visibility_level": 10, "path_with_namespace": "gitlab-org/gitlab-test", "default_branch": "master", "homepage": "http://example.com/gitlab-org/gitlab-test", "url": "http://example.com/gitlab-org/gitlab-test.git", "ssh_url": "git@example.com:gitlab-org/gitlab-test.git", "http_url": "http://example.com/gitlab-org/gitlab-test.git" }, "last_commit": { "id": "562e173be03b8ff2efb05345d12df18815438a4b", "message": "Merge branch 'another-branch' into 'master'\n\nCheck in this test\n", "title": "Merge branch 'another-branch' into 'master'", "timestamp": "2015-04-08T21:00:25-07:00", "url": "http://example.com/gitlab-org/gitlab-test/commit/562e173be03b8ff2efb05345d12df18815438a4b", "author": { "name": "John Smith", "email": "john@example.com" } }, "work_in_progress": false, "assignee": { "name": "User1", "username": "user1", "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon" }, "detailed_merge_status": "checking" } } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/webhooks/note_snippet.json000066400000000000000000000050721475761473200301170ustar00rootroot00000000000000{ "object_kind": "note", "event_type": "note", "user": { "id": 42, "name": "User1", "username": "user1", "email": "user1@example.com", "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon" }, "project_id": 5, "project": { "id": 5, "name": "Gitlab Test", "description": "Aut reprehenderit ut est.", "web_url": "http://example.com/gitlab-org/gitlab-test", "avatar_url": null, "git_ssh_url": "git@example.com:gitlab-org/gitlab-test.git", "git_http_url": "http://example.com/gitlab-org/gitlab-test.git", "namespace": "Gitlab Org", "visibility_level": 10, "path_with_namespace": "gitlab-org/gitlab-test", "default_branch": "master", "homepage": "http://example.com/gitlab-org/gitlab-test", "url": "http://example.com/gitlab-org/gitlab-test.git", "ssh_url": "git@example.com:gitlab-org/gitlab-test.git", "http_url": "http://example.com/gitlab-org/gitlab-test.git" }, "repository": { "name": "Gitlab Test", "url": "http://example.com/gitlab-org/gitlab-test.git", "description": "Aut reprehenderit ut est.", "homepage": "http://example.com/gitlab-org/gitlab-test" }, "object_attributes": { "id": 1245, "note": "Is this snippet doing what it's supposed to be doing?", "noteable_type": "Snippet", "author_id": 1, "created_at": "2015-05-17 18:35:50 UTC", "updated_at": "2015-05-17 18:35:50 UTC", "project_id": 5, "attachment": null, "change_position": null, "discussion_id": "e1c5835f5f99414806f6fe45b28s48cfebb89ee1", "line_code": null, "commit_id": null, "noteable_id": 53, "system": false, "original_position": null, "position": null, "resolved_at": null, "resolved_by_id": null, "resolved_by_push": null, "st_diff": null, "type": null, "updated_by_id": null, "description": "Is this snippet doing what it's supposed to be doing?", "action": "create", "url": "http://example.com/gitlab-org/gitlab-test/snippets/53#note_1245" }, "snippet": { "id": 53, "title": "test", "content": "puts 'Hello world'", "author_id": 1, "project_id": 5, "created_at": "2016-01-04 15:31:46 UTC", "updated_at": "2016-01-04 15:32:46 UTC", "file_name": "test.rb", "expires_at": null, "type": "ProjectSnippet", "visibility_level": 0, "description": "Prints 'Hello world'", "encrypted_secret_token": null, "encrypted_secret_token_iv": null, "secret": false, "repository_read_only": false, "secret_token": null } } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/webhooks/pipeline.json000066400000000000000000000151111475761473200272100ustar00rootroot00000000000000{ "object_kind": "pipeline", "object_attributes": { "id": 31, "iid": 123, "ref": "master", "tag": false, "sha": "bcbb5ec396a2c0f828686f14fac9b80b780504f2", "before_sha": "bcbb5ec396a2c0f828686f14fac9b80b780504f2", "source": "merge_request_event", "status": "success", "detailed_status": "passed", "stages": [ "build", "test", "deploy" ], "created_at": "2016-08-12 15:23:28 UTC", "finished_at": "2016-08-12 15:26:29 UTC", "duration": 63, "queued_duration": 12, "variables": [ { "key": "NESTOR_PROD_ENVIRONMENT", "value": "us-west-1" } ] }, "merge_request": { "id": 1, "iid": 1, "title": "Test", "source_branch": "test", "source_project_id": 1, "target_branch": "master", "target_project_id": 1, "state": "opened", "merge_status": "can_be_merged", "detailed_merge_status": "mergeable", "url": "http://192.168.64.1:3005/gitlab-org/gitlab-test/merge_requests/1" }, "user": { "id": 42, "name": "User1", "username": "user1", "email": "user1@example.com", "avatar_url": "http://www.gravatar.com/avatar/e32bd13e2add097461cb96824b7a829c?s=80\u0026d=identicon" }, "project": { "id": 1, "name": "Gitlab Test", "description": "Atque in sunt eos similique dolores voluptatem.", "web_url": "http://192.168.64.1:3005/gitlab-org/gitlab-test", "avatar_url": null, "git_ssh_url": "git@192.168.64.1:gitlab-org/gitlab-test.git", "git_http_url": "http://192.168.64.1:3005/gitlab-org/gitlab-test.git", "namespace": "Gitlab Org", "visibility_level": 20, "path_with_namespace": "gitlab-org/gitlab-test", "default_branch": "master" }, "commit": { "id": "bcbb5ec396a2c0f828686f14fac9b80b780504f2", "message": "test\n", "timestamp": "2016-08-12T17:23:21+02:00", "url": "http://example.com/gitlab-org/gitlab-test/commit/bcbb5ec396a2c0f828686f14fac9b80b780504f2", "author": { "name": "User", "email": "user@gitlab.com" } }, "source_pipeline":{ "project":{ "id": 41, "web_url": "https://gitlab.example.com/gitlab-org/upstream-project", "path_with_namespace": "gitlab-org/upstream-project" }, "pipeline_id": 30, "job_id": 3401 }, "builds": [ { "id": 380, "stage": "deploy", "name": "production", "status": "skipped", "created_at": "2016-08-12 15:23:28 UTC", "started_at": null, "finished_at": null, "duration": 17.1, "queued_duration": 3.5, "when": "manual", "manual": true, "allow_failure": true, "failure_reason": "script_failure", "user": { "id": 42, "name": "User1", "username": "user1", "email": "user1@example.com", "avatar_url": "http://www.gravatar.com/avatar/e32bd13e2add097461cb96824b7a829c?s=80\u0026d=identicon" }, "runner": { "id": 42, "description": "shared-runners-manager-1.gitlab.com", "runner_type": "instance_type", "active": true, "is_shared": true, "tags": [ "docker", "gce" ] }, "artifacts_file": { "filename": null, "size": null }, "environment": { "name": "production", "action": "start", "deployment_tier": "production" } }, { "id": 377, "stage": "test", "name": "test-image", "status": "success", "created_at": "2016-08-12 15:23:28 UTC", "started_at": "2016-08-12 15:26:12 UTC", "finished_at": null, "duration": 17.0, "queued_duration": 196.0, "when": "on_success", "manual": false, "allow_failure": false, "user": { "id": 42, "name": "User1", "username": "user1", "email": "user1@example.com", "avatar_url": "http://www.gravatar.com/avatar/e32bd13e2add097461cb96824b7a829c?s=80\u0026d=identicon" }, "runner": { "id": 380987, "description": "shared-runners-manager-6.gitlab.com", "active": true, "is_shared": true }, "artifacts_file": { "filename": null, "size": null } }, { "id": 378, "stage": "test", "name": "test-build", "status": "success", "created_at": "2016-08-12 15:23:28 UTC", "started_at": "2016-08-12 15:26:12 UTC", "finished_at": "2016-08-12 15:26:29 UTC", "duration": 17.0, "queued_duration": 196.0, "when": "on_success", "manual": false, "allow_failure": false, "user": { "id": 42, "name": "User1", "username": "user1", "email": "user1@example.com", "avatar_url": "http://www.gravatar.com/avatar/e32bd13e2add097461cb96824b7a829c?s=80\u0026d=identicon" }, "runner": { "id": 380987, "description": "shared-runners-manager-6.gitlab.com", "active": true, "is_shared": true }, "artifacts_file": { "filename": null, "size": null } }, { "id": 376, "stage": "build", "name": "build-image", "status": "success", "created_at": "2016-08-12 15:23:28 UTC", "started_at": "2016-08-12 15:24:56 UTC", "finished_at": "2016-08-12 15:25:26 UTC", "duration": 17.0, "queued_duration": 196.0, "when": "on_success", "manual": false, "allow_failure": false, "user": { "id": 42, "name": "User1", "username": "user1", "email": "user1@example.com", "avatar_url": "http://www.gravatar.com/avatar/e32bd13e2add097461cb96824b7a829c?s=80\u0026d=identicon" }, "runner": { "id": 380987, "description": "shared-runners-manager-6.gitlab.com", "active": true, "is_shared": true }, "artifacts_file": { "filename": null, "size": null } }, { "id": 379, "stage": "deploy", "name": "staging", "status": "created", "created_at": "2016-08-12 15:23:28 UTC", "started_at": null, "finished_at": null, "duration": 17.0, "queued_duration": 196.0, "when": "on_success", "manual": false, "allow_failure": false, "user": { "id": 42, "name": "User1", "username": "user1", "email": "user1@example.com", "avatar_url": "http://www.gravatar.com/avatar/e32bd13e2add097461cb96824b7a829c?s=80\u0026d=identicon" }, "runner": null, "artifacts_file": { "filename": null, "size": null } } ] } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/webhooks/push.json000066400000000000000000000050271475761473200263670ustar00rootroot00000000000000{ "object_kind": "push", "event_name": "push", "before": "95790bf891e76fee5e1747ab589903a6a1f80f22", "after": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7", "ref": "refs/heads/master", "checkout_sha": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7", "user_id": 4, "user_name": "John Smith", "user_username": "jsmith", "user_email": "john@example.com", "user_avatar": "https://s.gravatar.com/avatar/d4c74594d841139328695756648b6bd6?s=8://s.gravatar.com/avatar/d4c74594d841139328695756648b6bd6?s=80", "project_id": 15, "project": { "id": 15, "name": "Diaspora", "description": "", "web_url": "http://example.com/mike/diaspora", "avatar_url": null, "git_ssh_url": "git@example.com:mike/diaspora.git", "git_http_url": "http://example.com/mike/diaspora.git", "namespace": "Mike", "visibility_level": 0, "path_with_namespace": "mike/diaspora", "default_branch": "master", "homepage": "http://example.com/mike/diaspora", "url": "git@example.com:mike/diaspora.git", "ssh_url": "git@example.com:mike/diaspora.git", "http_url": "http://example.com/mike/diaspora.git" }, "repository": { "name": "Diaspora", "url": "git@example.com:mike/diaspora.git", "description": "", "homepage": "http://example.com/mike/diaspora", "git_http_url": "http://example.com/mike/diaspora.git", "git_ssh_url": "git@example.com:mike/diaspora.git", "visibility_level": 0 }, "commits": [ { "id": "b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327", "message": "Merge branch 'some-feature' into 'master'\n\nRelease v1.0.0\n\nSee merge request jsmith/example!1", "title": "Merge branch 'some-feature' into 'master'", "timestamp": "2011-12-12T14:27:31+02:00", "url": "http://example.com/mike/diaspora/commit/b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327", "author": { "name": "Jordi Mallach", "email": "jordi@softcatala.org" }, "added": ["CHANGELOG"], "modified": ["app/controller/application.rb"], "removed": [] }, { "id": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7", "message": "fixed readme\n", "title": "fixed readme", "timestamp": "2012-01-03T23:36:29+02:00", "url": "http://example.com/mike/diaspora/commit/da1560886d4f094c3e6c9ef40349f7d38b5d27d7", "author": { "name": "GitLab dev user", "email": "gitlabdev@dv6700.(none)" }, "added": ["CHANGELOG"], "modified": ["app/controller/application.rb"], "removed": [] } ], "total_commits_count": 4 } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/webhooks/release.json000066400000000000000000000046421475761473200270320ustar00rootroot00000000000000{ "id": 8273642, "created_at": "2021-02-25 21:23:34 UTC", "description": "Release!", "name": "1.0.0", "released_at": "2021-02-25 21:23:34 UTC", "tag": "1.0.0", "object_kind": "release", "project": { "id": 327622, "name": "Project Name", "description": "", "web_url": "http://example.com/exm-namespace/example-project", "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon", "git_ssh_url": "git@gitlab.com:exm-namespace/example-project.git", "git_http_url": "http://example.com/exm-namespace/example-project.git", "namespace": "exm-namespace", "visibility_level": 0, "path_with_namespace": "exm-namespace/example-project", "default_branch": "master", "ci_config_path": "", "homepage": "http://example.com/exm-namespace/example-project", "url": "git@gitlab.com:exm-namespace/example-project.git", "ssh_url": "git@gitlab.com:exm-namespace/example-project.git", "http_url": "http://example.com/exm-namespace/example-project.git" }, "url": "http://example.com/exm-namespace/example-project/-/releases/1.0.0", "action": "create", "assets": { "count": 4, "links": [ { "id": 1, "external": true, "link_type": "other", "name": "Changelog", "url": "https://example.net/changelog" } ], "sources": [ { "format": "zip", "url": "http://example.com/exm-namespace/example-project/-/archive/1.0.0/example-project-1.0.0.zip" }, { "format": "tar.gz", "url": "http://example.com/exm-namespace/example-project/-/archive/1.0.0/example-project-1.0.0.tar.gz" }, { "format": "tar.bz2", "url": "http://example.com/exm-namespace/example-project/-/archive/1.0.0/example-project-1.0.0.tar.bz2" }, { "format": "tar", "url": "http://example.com/exm-namespace/example-project/-/archive/1.0.0/example-project-1.0.0.tar" } ] }, "commit": { "id": "2626dbdb936782b5c54816b1c6d45b1279303c6d", "message": "Merge branch 'example-branch' into 'master'\n\nCheck in this test", "title": "Merge branch 'example-branch' into 'master'", "timestamp": "2021-02-25T21:21:58+00:00", "url": "http://example.com/exm-namespace/example-project/-/commit/2626dbdb936782b5c54816b1c6d45b1279303c6d", "author": { "name": "User", "email": "user@gitlab.com" } } } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/webhooks/resource_access_token_group.json000066400000000000000000000005121475761473200331660ustar00rootroot00000000000000{ "object_kind": "access_token", "group": { "group_name": "Twitter", "group_path": "twitter", "group_id": 35 }, "object_attributes": { "user_id": 90, "created_at": "2024-01-24 16:27:40 UTC", "id": 25, "name": "acd", "expires_at": "2024-01-26" }, "event_name": "expiring_access_token" } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/webhooks/resource_access_token_project.json000066400000000000000000000016761475761473200335140ustar00rootroot00000000000000{ "object_kind": "access_token", "project": { "id": 7, "name": "Flight", "description": "Eum dolore maxime atque reprehenderit voluptatem.", "web_url": "https://example.com/flightjs/Flight", "avatar_url": null, "git_ssh_url": "ssh://git@example.com/flightjs/Flight.git", "git_http_url": "https://example.com/flightjs/Flight.git", "namespace": "Flightjs", "visibility_level": 0, "path_with_namespace": "flightjs/Flight", "default_branch": "master", "ci_config_path": null, "homepage": "https://example.com/flightjs/Flight", "url": "ssh://git@example.com/flightjs/Flight.git", "ssh_url": "ssh://git@example.com/flightjs/Flight.git", "http_url": "https://example.com/flightjs/Flight.git" }, "object_attributes": { "user_id": 90, "created_at": "2024-01-24 16:27:40 UTC", "id": 25, "name": "acd", "expires_at": "2024-01-26" }, "event_name": "expiring_access_token" } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/webhooks/service_merge_request.json000066400000000000000000000103731475761473200317770ustar00rootroot00000000000000{ "object_kind": "merge_request", "event_type": "merge_request", "user": { "id": 2, "name": "the test", "username": "test", "avatar_url": "https://www.gravatar.com/avatar/dd46a756faad4727fb679320751f6dea?s=80&d=identicon", "email": "test@test.test" }, "project": { "id": 2, "name": "Woodpecker", "description": "", "web_url": "http://10.40.8.5:3200/test/woodpecker", "avatar_url": null, "git_ssh_url": "git@10.40.8.5:test/woodpecker.git", "git_http_url": "http://10.40.8.5:3200/test/woodpecker.git", "namespace": "the test", "visibility_level": 20, "path_with_namespace": "test/woodpecker", "default_branch": "master", "ci_config_path": null, "homepage": "http://10.40.8.5:3200/test/woodpecker", "url": "git@10.40.8.5:test/woodpecker.git", "ssh_url": "git@10.40.8.5:test/woodpecker.git", "http_url": "http://10.40.8.5:3200/test/woodpecker.git" }, "object_attributes": { "assignee_id": null, "author_id": 2, "created_at": "2021-09-27 05:00:01 UTC", "description": "", "head_pipeline_id": 5, "id": 2, "iid": 2, "last_edited_at": null, "last_edited_by_id": null, "merge_commit_sha": null, "merge_error": null, "merge_params": { "force_remove_source_branch": "1" }, "merge_status": "unchecked", "merge_user_id": null, "merge_when_pipeline_succeeds": false, "milestone_id": null, "source_branch": "next-feature", "source_project_id": 2, "state_id": 1, "target_branch": "master", "target_project_id": 2, "time_estimate": 0, "title": "Update client.go 🎉", "updated_at": "2021-09-27 05:01:21 UTC", "updated_by_id": null, "url": "http://10.40.8.5:3200/test/woodpecker/-/merge_requests/2", "source": { "id": 2, "name": "Woodpecker", "description": "", "web_url": "http://10.40.8.5:3200/test/woodpecker", "avatar_url": "http://example.com/uploads/project/avatar/555/Outh-20-Logo.jpg", "git_ssh_url": "git@10.40.8.5:test/woodpecker.git", "git_http_url": "http://10.40.8.5:3200/test/woodpecker.git", "namespace": "the test", "visibility_level": 20, "path_with_namespace": "test/woodpecker", "default_branch": "develop", "ci_config_path": null, "homepage": "http://10.40.8.5:3200/test/woodpecker", "url": "git@10.40.8.5:test/woodpecker.git", "ssh_url": "git@10.40.8.5:test/woodpecker.git", "http_url": "http://10.40.8.5:3200/test/woodpecker.git" }, "target": { "id": 2, "name": "Woodpecker", "description": "", "web_url": "http://10.40.8.5:3200/test/woodpecker", "avatar_url": "http://example.com/uploads/project/avatar/555/Outh-20-Logo.jpg", "git_ssh_url": "git@10.40.8.5:test/woodpecker.git", "git_http_url": "http://10.40.8.5:3200/test/woodpecker.git", "namespace": "the test", "visibility_level": 20, "path_with_namespace": "test/woodpecker", "default_branch": "develop", "ci_config_path": null, "homepage": "http://10.40.8.5:3200/test/woodpecker", "url": "git@10.40.8.5:test/woodpecker.git", "ssh_url": "git@10.40.8.5:test/woodpecker.git", "http_url": "http://10.40.8.5:3200/test/woodpecker.git" }, "last_commit": { "id": "0ab96a10266b95b4b533dcfd98738015fbe70889", "message": "Update state.go", "title": "Update state.go", "timestamp": "2021-09-27T05:01:20+00:00", "url": "http://10.40.8.5:3200/test/woodpecker/-/commit/0ab96a10266b95b4b533dcfd98738015fbe70889", "author": { "name": "the test", "email": "test@test.test" } }, "work_in_progress": false, "total_time_spent": 0, "time_change": 0, "human_total_time_spent": null, "human_time_change": null, "human_time_estimate": null, "assignee_ids": [], "state": "opened", "action": "update", "oldrev": "6ef047571374c96a2bf13c361efd1fb008b0063e" }, "labels": [], "changes": { "updated_at": { "previous": "2021-09-27 05:00:01 UTC", "current": "2021-09-27 05:01:21 UTC" } }, "repository": { "name": "Woodpecker", "url": "git@10.40.8.5:test/woodpecker.git", "description": "", "homepage": "http://10.40.8.5:3200/test/woodpecker" } } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/webhooks/subgroup.json000066400000000000000000000005151475761473200272530ustar00rootroot00000000000000{ "created_at": "2022-01-24T14:23:59Z", "updated_at": "2022-01-24T14:23:59Z", "event_name": "subgroup_create", "name": "SubGroup 1", "path": "subgroup-1", "full_path": "group-1/subgroup-1", "group_id": 2, "parent_group_id": 1, "parent_name": "Group 1", "parent_path": "group-1", "parent_full_path": "group-1" } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/webhooks/tag_push.json000066400000000000000000000037731475761473200272300ustar00rootroot00000000000000{ "object_kind": "tag_push", "event_name": "tag_push", "before": "0000000000000000000000000000000000000000", "after": "82b3d5ae55f7080f1e6022629cdb57bfae7cccc7", "ref": "refs/tags/v1.0.0", "checkout_sha": "82b3d5ae55f7080f1e6022629cdb57bfae7cccc7", "user_id": 1, "user_username": "jsmith", "user_name": "John Smith", "user_avatar": "https://s.gravatar.com/avatar/d4c74594d841139328695756648b6bd6?s=8://s.gravatar.com/avatar/d4c74594d841139328695756648b6bd6?s=80", "project_id": 1, "project":{ "id": 1, "name":"Example", "description":"", "web_url":"http://example.com/jsmith/example", "avatar_url":null, "git_ssh_url":"git@example.com:jsmith/example.git", "git_http_url":"http://example.com/jsmith/example.git", "namespace":"Jsmith", "visibility_level":0, "path_with_namespace":"jsmith/example", "default_branch":"master", "homepage":"http://example.com/jsmith/example", "url":"git@example.com:jsmith/example.git", "ssh_url":"git@example.com:jsmith/example.git", "http_url":"http://example.com/jsmith/example.git" }, "repository":{ "name": "Example", "url": "ssh://git@example.com/jsmith/example.git", "description": "", "homepage": "http://example.com/jsmith/example", "git_http_url":"http://example.com/jsmith/example.git", "git_ssh_url":"git@example.com:jsmith/example.git", "visibility_level":0 }, "commits": [ { "id": "82b3d5ae55f7080f1e6022629cdb57bfae7cccc7", "message": "Merge branch 'some-feature' into 'master'\n\nRelease v1.0.0\n\nSee merge request jsmith/example!1", "title": "Merge branch 'some-feature' into 'master'", "timestamp": "2012-01-03T23:36:29+02:00", "url": "http://example.com/jsmith/example/commit/82b3d5ae55f7080f1e6022629cdb57bfae7cccc7", "author": { "name": "John Smith", "email": "johnsmith@example.com" }, "added": ["CHANGELOG"], "modified": ["UPGRADE.md"], "removed": [] } ], "total_commits_count": 1 } golang-gitlab-gitlab-org-api-client-go-0.123.0/testdata/webhooks/wiki_page.json000066400000000000000000000030411475761473200273410ustar00rootroot00000000000000{ "object_kind": "wiki_page", "user": { "name": "User1", "username": "user1", "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon" }, "project": { "id": 1, "name": "awesome-project", "description": "This is awesome", "web_url": "http://example.com/root/awesome-project", "avatar_url": null, "git_ssh_url": "git@example.com:root/awesome-project.git", "git_http_url": "http://example.com/root/awesome-project.git", "namespace": "root", "visibility_level": 0, "path_with_namespace": "root/awesome-project", "default_branch": "master", "homepage": "http://example.com/root/awesome-project", "url": "git@example.com:root/awesome-project.git", "ssh_url": "git@example.com:root/awesome-project.git", "http_url": "http://example.com/root/awesome-project.git" }, "wiki": { "web_url": "http://example.com/root/awesome-project/wikis/home", "git_ssh_url": "git@example.com:root/awesome-project.wiki.git", "git_http_url": "http://example.com/root/awesome-project.wiki.git", "path_with_namespace": "root/awesome-project.wiki", "default_branch": "master" }, "object_attributes": { "title": "Awesome", "content": "awesome content goes here", "format": "markdown", "message": "adding an awesome page to the wiki", "slug": "awesome", "url": "http://example.com/root/awesome-project/wikis/awesome", "action": "create", "diff_url": "http://example.com/root/awesome-project/wikis/awesome/diff" } } golang-gitlab-gitlab-org-api-client-go-0.123.0/time_stats.go000066400000000000000000000124531475761473200235670ustar00rootroot00000000000000// // Copyright 2021, Sander van Harmelen // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "fmt" "net/http" ) // timeStatsService handles communication with the time tracking related // methods of the GitLab API. // // GitLab docs: https://docs.gitlab.com/ee/workflow/time_tracking.html type timeStatsService struct { client *Client } // TimeStats represents the time estimates and time spent for an issue. // // GitLab docs: https://docs.gitlab.com/ee/workflow/time_tracking.html type TimeStats struct { HumanTimeEstimate string `json:"human_time_estimate"` HumanTotalTimeSpent string `json:"human_total_time_spent"` TimeEstimate int `json:"time_estimate"` TotalTimeSpent int `json:"total_time_spent"` } func (t TimeStats) String() string { return Stringify(t) } // SetTimeEstimateOptions represents the available SetTimeEstimate() // options. // // GitLab docs: https://docs.gitlab.com/ee/workflow/time_tracking.html type SetTimeEstimateOptions struct { Duration *string `url:"duration,omitempty" json:"duration,omitempty"` } // setTimeEstimate sets the time estimate for a single project issue. // // GitLab docs: https://docs.gitlab.com/ee/workflow/time_tracking.html func (s *timeStatsService) setTimeEstimate(pid interface{}, entity string, issue int, opt *SetTimeEstimateOptions, options ...RequestOptionFunc) (*TimeStats, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/%s/%d/time_estimate", PathEscape(project), entity, issue) req, err := s.client.NewRequest(http.MethodPost, u, opt, options) if err != nil { return nil, nil, err } t := new(TimeStats) resp, err := s.client.Do(req, t) if err != nil { return nil, resp, err } return t, resp, nil } // resetTimeEstimate resets the time estimate for a single project issue. // // GitLab docs: https://docs.gitlab.com/ee/workflow/time_tracking.html func (s *timeStatsService) resetTimeEstimate(pid interface{}, entity string, issue int, options ...RequestOptionFunc) (*TimeStats, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/%s/%d/reset_time_estimate", PathEscape(project), entity, issue) req, err := s.client.NewRequest(http.MethodPost, u, nil, options) if err != nil { return nil, nil, err } t := new(TimeStats) resp, err := s.client.Do(req, t) if err != nil { return nil, resp, err } return t, resp, nil } // AddSpentTimeOptions represents the available AddSpentTime() options. // // GitLab docs: https://docs.gitlab.com/ee/workflow/time_tracking.html type AddSpentTimeOptions struct { Duration *string `url:"duration,omitempty" json:"duration,omitempty"` Summary *string `url:"summary,omitempty" json:"summary,omitempty"` } // addSpentTime adds spent time for a single project issue. // // GitLab docs: https://docs.gitlab.com/ee/workflow/time_tracking.html func (s *timeStatsService) addSpentTime(pid interface{}, entity string, issue int, opt *AddSpentTimeOptions, options ...RequestOptionFunc) (*TimeStats, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/%s/%d/add_spent_time", PathEscape(project), entity, issue) req, err := s.client.NewRequest(http.MethodPost, u, opt, options) if err != nil { return nil, nil, err } t := new(TimeStats) resp, err := s.client.Do(req, t) if err != nil { return nil, resp, err } return t, resp, nil } // resetSpentTime resets the spent time for a single project issue. // // GitLab docs: https://docs.gitlab.com/ee/workflow/time_tracking.html func (s *timeStatsService) resetSpentTime(pid interface{}, entity string, issue int, options ...RequestOptionFunc) (*TimeStats, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/%s/%d/reset_spent_time", PathEscape(project), entity, issue) req, err := s.client.NewRequest(http.MethodPost, u, nil, options) if err != nil { return nil, nil, err } t := new(TimeStats) resp, err := s.client.Do(req, t) if err != nil { return nil, resp, err } return t, resp, nil } // getTimeSpent gets the spent time for a single project issue. // // GitLab docs: https://docs.gitlab.com/ee/workflow/time_tracking.html func (s *timeStatsService) getTimeSpent(pid interface{}, entity string, issue int, options ...RequestOptionFunc) (*TimeStats, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/%s/%d/time_stats", PathEscape(project), entity, issue) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } t := new(TimeStats) resp, err := s.client.Do(req, t) if err != nil { return nil, resp, err } return t, resp, nil } golang-gitlab-gitlab-org-api-client-go-0.123.0/todos.go000066400000000000000000000143671475761473200225510ustar00rootroot00000000000000// // Copyright 2021, Sander van Harmelen // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "fmt" "net/http" "time" ) // TodosService handles communication with the todos related methods of // the Gitlab API. // // GitLab API docs: https://docs.gitlab.com/ee/api/todos.html type TodosService struct { client *Client } // Todo represents a GitLab todo. // // GitLab API docs: https://docs.gitlab.com/ee/api/todos.html type Todo struct { ID int `json:"id"` Project *BasicProject `json:"project"` Author *BasicUser `json:"author"` ActionName TodoAction `json:"action_name"` TargetType TodoTargetType `json:"target_type"` Target *TodoTarget `json:"target"` TargetURL string `json:"target_url"` Body string `json:"body"` State string `json:"state"` CreatedAt *time.Time `json:"created_at"` } func (t Todo) String() string { return Stringify(t) } // TodoTarget represents a todo target of type Issue or MergeRequest type TodoTarget struct { Assignees []*BasicUser `json:"assignees"` Assignee *BasicUser `json:"assignee"` Author *BasicUser `json:"author"` CreatedAt *time.Time `json:"created_at"` Description string `json:"description"` Downvotes int `json:"downvotes"` ID interface{} `json:"id"` IID int `json:"iid"` Labels []string `json:"labels"` Milestone *Milestone `json:"milestone"` ProjectID int `json:"project_id"` State string `json:"state"` Subscribed bool `json:"subscribed"` TaskCompletionStatus *TasksCompletionStatus `json:"task_completion_status"` Title string `json:"title"` UpdatedAt *time.Time `json:"updated_at"` Upvotes int `json:"upvotes"` UserNotesCount int `json:"user_notes_count"` WebURL string `json:"web_url"` // Only available for type Issue Confidential bool `json:"confidential"` DueDate string `json:"due_date"` HasTasks bool `json:"has_tasks"` Links *IssueLinks `json:"_links"` MovedToID int `json:"moved_to_id"` TimeStats *TimeStats `json:"time_stats"` Weight int `json:"weight"` // Only available for type MergeRequest MergedAt *time.Time `json:"merged_at"` ApprovalsBeforeMerge int `json:"approvals_before_merge"` ForceRemoveSourceBranch bool `json:"force_remove_source_branch"` MergeCommitSHA string `json:"merge_commit_sha"` MergeWhenPipelineSucceeds bool `json:"merge_when_pipeline_succeeds"` MergeStatus string `json:"merge_status"` Reference string `json:"reference"` Reviewers []*BasicUser `json:"reviewers"` SHA string `json:"sha"` ShouldRemoveSourceBranch bool `json:"should_remove_source_branch"` SourceBranch string `json:"source_branch"` SourceProjectID int `json:"source_project_id"` Squash bool `json:"squash"` TargetBranch string `json:"target_branch"` TargetProjectID int `json:"target_project_id"` WorkInProgress bool `json:"work_in_progress"` // Only available for type DesignManagement::Design FileName string `json:"filename"` ImageURL string `json:"image_url"` } // ListTodosOptions represents the available ListTodos() options. // // GitLab API docs: https://docs.gitlab.com/ee/api/todos.html#get-a-list-of-to-do-items type ListTodosOptions struct { ListOptions Action *TodoAction `url:"action,omitempty" json:"action,omitempty"` AuthorID *int `url:"author_id,omitempty" json:"author_id,omitempty"` ProjectID *int `url:"project_id,omitempty" json:"project_id,omitempty"` State *string `url:"state,omitempty" json:"state,omitempty"` Type *string `url:"type,omitempty" json:"type,omitempty"` } // ListTodos lists all todos created by authenticated user. // When no filter is applied, it returns all pending todos for the current user. // // GitLab API docs: // https://docs.gitlab.com/ee/api/todos.html#get-a-list-of-to-do-items func (s *TodosService) ListTodos(opt *ListTodosOptions, options ...RequestOptionFunc) ([]*Todo, *Response, error) { req, err := s.client.NewRequest(http.MethodGet, "todos", opt, options) if err != nil { return nil, nil, err } var t []*Todo resp, err := s.client.Do(req, &t) if err != nil { return nil, resp, err } return t, resp, nil } // MarkTodoAsDone marks a single pending todo given by its ID for the current user as done. // // GitLab API docs: https://docs.gitlab.com/ee/api/todos.html#mark-a-to-do-item-as-done func (s *TodosService) MarkTodoAsDone(id int, options ...RequestOptionFunc) (*Response, error) { u := fmt.Sprintf("todos/%d/mark_as_done", id) req, err := s.client.NewRequest(http.MethodPost, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } // MarkAllTodosAsDone marks all pending todos for the current user as done. // // GitLab API docs: https://docs.gitlab.com/ee/api/todos.html#mark-all-to-do-items-as-done func (s *TodosService) MarkAllTodosAsDone(options ...RequestOptionFunc) (*Response, error) { req, err := s.client.NewRequest(http.MethodPost, "todos/mark_as_done", nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } golang-gitlab-gitlab-org-api-client-go-0.123.0/todos_test.go000066400000000000000000000035571475761473200236070ustar00rootroot00000000000000// // Copyright 2021, Sander van Harmelen // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "net/http" "testing" "github.com/stretchr/testify/require" ) func TestListTodos(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/todos", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) mustWriteHTTPResponse(t, w, "testdata/list_todos.json") }) opts := &ListTodosOptions{ListOptions: ListOptions{PerPage: 2}} todos, _, err := client.Todos.ListTodos(opts) require.NoError(t, err) want := []*Todo{ {ID: 1, State: "pending", Target: &TodoTarget{ID: float64(1), ApprovalsBeforeMerge: 2}}, {ID: 2, State: "pending", Target: &TodoTarget{ID: "1d76d1b2e3e886108f662765c97f4687f4134d8c"}}, } require.Equal(t, want, todos) } func TestMarkAllTodosAsDone(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/todos/mark_as_done", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) w.WriteHeader(http.StatusNoContent) }) _, err := client.Todos.MarkAllTodosAsDone() require.NoError(t, err) } func TestMarkTodoAsDone(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/todos/1/mark_as_done", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) }) _, err := client.Todos.MarkTodoAsDone(1) require.NoError(t, err) } golang-gitlab-gitlab-org-api-client-go-0.123.0/topics.go000066400000000000000000000140261475761473200227120ustar00rootroot00000000000000// // Copyright 2021, Sander van Harmelen // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "encoding/json" "fmt" "io" "net/http" retryablehttp "github.com/hashicorp/go-retryablehttp" ) // TopicsService handles communication with the topics related methods // of the GitLab API. // // GitLab API docs: https://docs.gitlab.com/ee/api/topics.html type TopicsService struct { client *Client } // Topic represents a GitLab project topic. // // GitLab API docs: https://docs.gitlab.com/ee/api/topics.html type Topic struct { ID int `json:"id"` Name string `json:"name"` Title string `json:"title"` Description string `json:"description"` TotalProjectsCount uint64 `json:"total_projects_count"` AvatarURL string `json:"avatar_url"` } func (t Topic) String() string { return Stringify(t) } // ListTopicsOptions represents the available ListTopics() options. // // GitLab API docs: https://docs.gitlab.com/ee/api/topics.html#list-topics type ListTopicsOptions struct { ListOptions Search *string `url:"search,omitempty" json:"search,omitempty"` } // ListTopics returns a list of project topics in the GitLab instance ordered // by number of associated projects. // // GitLab API docs: https://docs.gitlab.com/ee/api/topics.html#list-topics func (s *TopicsService) ListTopics(opt *ListTopicsOptions, options ...RequestOptionFunc) ([]*Topic, *Response, error) { req, err := s.client.NewRequest(http.MethodGet, "topics", opt, options) if err != nil { return nil, nil, err } var t []*Topic resp, err := s.client.Do(req, &t) if err != nil { return nil, resp, err } return t, resp, nil } // GetTopic gets a project topic by ID. // // GitLab API docs: https://docs.gitlab.com/ee/api/topics.html#get-a-topic func (s *TopicsService) GetTopic(topic int, options ...RequestOptionFunc) (*Topic, *Response, error) { u := fmt.Sprintf("topics/%d", topic) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } t := new(Topic) resp, err := s.client.Do(req, t) if err != nil { return nil, resp, err } return t, resp, nil } // CreateTopicOptions represents the available CreateTopic() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/topics.html#create-a-project-topic type CreateTopicOptions struct { Name *string `url:"name,omitempty" json:"name,omitempty"` Title *string `url:"title,omitempty" json:"title,omitempty"` Description *string `url:"description,omitempty" json:"description,omitempty"` Avatar *TopicAvatar `url:"-" json:"-"` } // TopicAvatar represents a GitLab topic avatar. type TopicAvatar struct { Filename string Image io.Reader } // MarshalJSON implements the json.Marshaler interface. func (a *TopicAvatar) MarshalJSON() ([]byte, error) { if a.Filename == "" && a.Image == nil { return []byte(`""`), nil } type alias TopicAvatar return json.Marshal((*alias)(a)) } // CreateTopic creates a new project topic. // // GitLab API docs: // https://docs.gitlab.com/ee/api/topics.html#create-a-project-topic func (s *TopicsService) CreateTopic(opt *CreateTopicOptions, options ...RequestOptionFunc) (*Topic, *Response, error) { var err error var req *retryablehttp.Request if opt.Avatar == nil { req, err = s.client.NewRequest(http.MethodPost, "topics", opt, options) } else { req, err = s.client.UploadRequest( http.MethodPost, "topics", opt.Avatar.Image, opt.Avatar.Filename, UploadAvatar, opt, options, ) } if err != nil { return nil, nil, err } t := new(Topic) resp, err := s.client.Do(req, t) if err != nil { return nil, resp, err } return t, resp, nil } // UpdateTopicOptions represents the available UpdateTopic() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/topics.html#update-a-project-topic type UpdateTopicOptions struct { Name *string `url:"name,omitempty" json:"name,omitempty"` Title *string `url:"title,omitempty" json:"title,omitempty"` Description *string `url:"description,omitempty" json:"description,omitempty"` Avatar *TopicAvatar `url:"-" json:"avatar,omitempty"` } // UpdateTopic updates a project topic. Only available to administrators. // // To remove a topic avatar set the TopicAvatar.Filename to an empty string // and set TopicAvatar.Image to nil. // // GitLab API docs: // https://docs.gitlab.com/ee/api/topics.html#update-a-project-topic func (s *TopicsService) UpdateTopic(topic int, opt *UpdateTopicOptions, options ...RequestOptionFunc) (*Topic, *Response, error) { u := fmt.Sprintf("topics/%d", topic) var err error var req *retryablehttp.Request if opt.Avatar == nil || (opt.Avatar.Filename == "" && opt.Avatar.Image == nil) { req, err = s.client.NewRequest(http.MethodPut, u, opt, options) } else { req, err = s.client.UploadRequest( http.MethodPut, u, opt.Avatar.Image, opt.Avatar.Filename, UploadAvatar, opt, options, ) } if err != nil { return nil, nil, err } t := new(Topic) resp, err := s.client.Do(req, t) if err != nil { return nil, resp, err } return t, resp, nil } // DeleteTopic deletes a project topic. Only available to administrators. // // GitLab API docs: // https://docs.gitlab.com/ee/api/topics.html#delete-a-project-topic func (s *TopicsService) DeleteTopic(topic int, options ...RequestOptionFunc) (*Response, error) { u := fmt.Sprintf("topics/%d", topic) req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } golang-gitlab-gitlab-org-api-client-go-0.123.0/topics_test.go000066400000000000000000000123461475761473200237540ustar00rootroot00000000000000// // Copyright 2021, Sander van Harmelen // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "fmt" "net/http" "reflect" "testing" ) func TestTopicsService_ListTopics(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/topics", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, `[ { "id": 1, "name": "gitlab", "title": "GitLab", "description": "GitLab is a version control system", "total_projects_count": 1000, "avatar_url": "http://www.gravatar.com/avatar/a0d477b3ea21970ce6ffcbb817b0b435?s=80&d=identicon" }, { "id": 3, "name": "git", "title": "Git", "description": "Git is free and open source", "total_projects_count": 900, "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon" }, { "id": 2, "name": "git-lfs", "title": "Git LFS", "description": null, "total_projects_count": 300, "avatar_url": null } ]`) }) opt := &ListTopicsOptions{Search: Ptr("git")} topics, _, err := client.Topics.ListTopics(opt) if err != nil { t.Errorf("Tags.ListTags returned error: %v", err) } want := []*Topic{{ ID: 1, Name: "gitlab", Title: "GitLab", Description: "GitLab is a version control system", TotalProjectsCount: 1000, AvatarURL: "http://www.gravatar.com/avatar/a0d477b3ea21970ce6ffcbb817b0b435?s=80&d=identicon", }, { ID: 3, Name: "git", Title: "Git", Description: "Git is free and open source", TotalProjectsCount: 900, AvatarURL: "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon", }, { ID: 2, Name: "git-lfs", Title: "Git LFS", TotalProjectsCount: 300, }} if !reflect.DeepEqual(want, topics) { t.Errorf("Topics.ListTopics returned %+v, want %+v", topics, want) } } func TestTopicsService_GetTopic(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/topics/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, `{ "id": 1, "name": "gitlab", "title": "GitLab", "Description": "GitLab is a version control system", "total_projects_count": 1000, "avatar_url": "http://www.gravatar.com/avatar/a0d477b3ea21970ce6ffcbb817b0b435?s=80&d=identicon" }`) }) release, _, err := client.Topics.GetTopic(1) if err != nil { t.Errorf("Topics.GetTopic returned error: %v", err) } want := &Topic{ ID: 1, Name: "gitlab", Title: "GitLab", Description: "GitLab is a version control system", TotalProjectsCount: 1000, AvatarURL: "http://www.gravatar.com/avatar/a0d477b3ea21970ce6ffcbb817b0b435?s=80&d=identicon", } if !reflect.DeepEqual(want, release) { t.Errorf("Topics.GetTopic returned %+v, want %+v", release, want) } } func TestTopicsService_CreateTopic(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/topics", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) fmt.Fprint(w, `{ "id": 1, "name": "topic1", "title": "Topic 1", "description": "description", "total_projects_count": 0, "avatar_url": null }`) }) opt := &CreateTopicOptions{Name: Ptr("topic1"), Title: Ptr("Topic 1"), Description: Ptr("description")} release, _, err := client.Topics.CreateTopic(opt) if err != nil { t.Errorf("Topics.CreateTopic returned error: %v", err) } want := &Topic{ID: 1, Name: "topic1", Title: "Topic 1", Description: "description", TotalProjectsCount: 0} if !reflect.DeepEqual(want, release) { t.Errorf("Topics.CreateTopic returned %+v, want %+v", release, want) } } func TestTopicsService_UpdateTopic(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/topics/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPut) fmt.Fprint(w, `{ "id": 1, "name": "topic1", "title": "Topic 1", "description": "description", "total_projects_count": 0, "avatar_url": null }`) }) opt := &UpdateTopicOptions{Name: Ptr("topic1"), Title: Ptr("Topic 1"), Description: Ptr("description")} release, _, err := client.Topics.UpdateTopic(1, opt) if err != nil { t.Errorf("Topics.UpdateTopic returned error: %v", err) } want := &Topic{ID: 1, Name: "topic1", Title: "Topic 1", Description: "description", TotalProjectsCount: 0} if !reflect.DeepEqual(want, release) { t.Errorf("Topics.UpdateTopic returned %+v, want %+v", release, want) } } golang-gitlab-gitlab-org-api-client-go-0.123.0/types.go000066400000000000000000000773211475761473200225640ustar00rootroot00000000000000// // Copyright 2021, Sander van Harmelen // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "bytes" "encoding/json" "errors" "fmt" "net/url" "reflect" "strconv" "strings" "time" ) // Ptr is a helper that returns a pointer to v. func Ptr[T any](v T) *T { return &v } // AccessControlValue represents an access control value within GitLab, // used for managing access to certain project features. // // GitLab API docs: https://docs.gitlab.com/ee/api/projects.html type AccessControlValue string // List of available access control values. // // GitLab API docs: https://docs.gitlab.com/ee/api/projects.html const ( DisabledAccessControl AccessControlValue = "disabled" EnabledAccessControl AccessControlValue = "enabled" PrivateAccessControl AccessControlValue = "private" PublicAccessControl AccessControlValue = "public" ) // AccessControl is a helper routine that allocates a new AccessControlValue // to store v and returns a pointer to it. // // Deprecated: Please use Ptr instead. func AccessControl(v AccessControlValue) *AccessControlValue { return Ptr(v) } // AccessLevelValue represents a permission level within GitLab. // // GitLab API docs: https://docs.gitlab.com/ee/user/permissions.html type AccessLevelValue int // List of available access levels. // // GitLab API docs: https://docs.gitlab.com/ee/user/permissions.html const ( NoPermissions AccessLevelValue = 0 MinimalAccessPermissions AccessLevelValue = 5 GuestPermissions AccessLevelValue = 10 ReporterPermissions AccessLevelValue = 20 DeveloperPermissions AccessLevelValue = 30 MaintainerPermissions AccessLevelValue = 40 OwnerPermissions AccessLevelValue = 50 AdminPermissions AccessLevelValue = 60 // Deprecated: Renamed to MaintainerPermissions in GitLab 11.0. MasterPermissions AccessLevelValue = 40 // Deprecated: Renamed to OwnerPermissions. OwnerPermission AccessLevelValue = 50 ) // AccessLevel is a helper routine that allocates a new AccessLevelValue // to store v and returns a pointer to it. // // Deprecated: Please use Ptr instead. func AccessLevel(v AccessLevelValue) *AccessLevelValue { return Ptr(v) } type AccessLevelDetails struct { IntegerValue AccessLevelValue `json:"integer_value"` StringValue string `json:"string_value"` } // UserIDValue represents a user ID value within GitLab. type UserIDValue string // List of available user ID values. const ( UserIDAny UserIDValue = "Any" UserIDNone UserIDValue = "None" ) // ApproverIDsValue represents an approver ID value within GitLab. type ApproverIDsValue struct { value interface{} } // ApproverIDs is a helper routine that creates a new ApproverIDsValue. func ApproverIDs(v interface{}) *ApproverIDsValue { switch v.(type) { case UserIDValue, []int: return &ApproverIDsValue{value: v} default: panic("Unsupported value passed as approver ID") } } // EncodeValues implements the query.Encoder interface. func (a *ApproverIDsValue) EncodeValues(key string, v *url.Values) error { switch value := a.value.(type) { case UserIDValue: v.Set(key, string(value)) case []int: v.Del(key) v.Del(key + "[]") for _, id := range value { v.Add(key+"[]", strconv.Itoa(id)) } } return nil } // MarshalJSON implements the json.Marshaler interface. func (a ApproverIDsValue) MarshalJSON() ([]byte, error) { return json.Marshal(a.value) } // UnmarshalJSON implements the json.Unmarshaler interface. func (a *ApproverIDsValue) UnmarshalJSON(bytes []byte) error { return json.Unmarshal(bytes, a.value) } // AssigneeIDValue represents an assignee ID value within GitLab. type AssigneeIDValue struct { value interface{} } // AssigneeID is a helper routine that creates a new AssigneeIDValue. func AssigneeID(v interface{}) *AssigneeIDValue { switch v.(type) { case UserIDValue, int: return &AssigneeIDValue{value: v} default: panic("Unsupported value passed as assignee ID") } } // EncodeValues implements the query.Encoder interface. func (a *AssigneeIDValue) EncodeValues(key string, v *url.Values) error { switch value := a.value.(type) { case UserIDValue: v.Set(key, string(value)) case int: v.Set(key, strconv.Itoa(value)) } return nil } // MarshalJSON implements the json.Marshaler interface. func (a AssigneeIDValue) MarshalJSON() ([]byte, error) { return json.Marshal(a.value) } // UnmarshalJSON implements the json.Unmarshaler interface. func (a *AssigneeIDValue) UnmarshalJSON(bytes []byte) error { return json.Unmarshal(bytes, a.value) } // ReviewerIDValue represents a reviewer ID value within GitLab. type ReviewerIDValue struct { value interface{} } // ReviewerID is a helper routine that creates a new ReviewerIDValue. func ReviewerID(v interface{}) *ReviewerIDValue { switch v.(type) { case UserIDValue, int: return &ReviewerIDValue{value: v} default: panic("Unsupported value passed as reviewer ID") } } // EncodeValues implements the query.Encoder interface. func (a *ReviewerIDValue) EncodeValues(key string, v *url.Values) error { switch value := a.value.(type) { case UserIDValue: v.Set(key, string(value)) case int: v.Set(key, strconv.Itoa(value)) } return nil } // MarshalJSON implements the json.Marshaler interface. func (a ReviewerIDValue) MarshalJSON() ([]byte, error) { return json.Marshal(a.value) } // UnmarshalJSON implements the json.Unmarshaler interface. func (a *ReviewerIDValue) UnmarshalJSON(bytes []byte) error { return json.Unmarshal(bytes, a.value) } // AvailabilityValue represents an availability value within GitLab. type AvailabilityValue string // List of available availability values. // // Undocummented, see code at: // https://gitlab.com/gitlab-org/gitlab-foss/-/blob/master/app/models/user_status.rb#L22 const ( NotSet AvailabilityValue = "not_set" Busy AvailabilityValue = "busy" ) // Availability is a helper routine that allocates a new AvailabilityValue // to store v and returns a pointer to it. // // Deprecated: Please use Ptr instead. func Availability(v AvailabilityValue) *AvailabilityValue { return Ptr(v) } // BuildStateValue represents a GitLab build state. type BuildStateValue string // These constants represent all valid build states. const ( Created BuildStateValue = "created" WaitingForResource BuildStateValue = "waiting_for_resource" Preparing BuildStateValue = "preparing" Pending BuildStateValue = "pending" Running BuildStateValue = "running" Success BuildStateValue = "success" Failed BuildStateValue = "failed" Canceled BuildStateValue = "canceled" Skipped BuildStateValue = "skipped" Manual BuildStateValue = "manual" Scheduled BuildStateValue = "scheduled" ) // BuildState is a helper routine that allocates a new BuildStateValue // to store v and returns a pointer to it. // // Deprecated: Please use Ptr instead. func BuildState(v BuildStateValue) *BuildStateValue { return Ptr(v) } // CommentEventAction identifies if a comment has been newly created or updated. // // GitLab API docs: // https://docs.gitlab.com/ee/user/project/integrations/webhook_events.html#comment-events type CommentEventAction string const ( CommentEventActionCreate CommentEventAction = "create" CommentEventActionUpdate CommentEventAction = "update" ) // ContainerRegistryStatus represents the status of a Container Registry. // // GitLab API docs: // https://docs.gitlab.com/ee/api/container_registry.html#list-registry-repositories type ContainerRegistryStatus string // ContainerRegistryStatus represents all valid statuses of a Container Registry. // // Undocumented, see code at: // https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/models/container_repository.rb?ref_type=heads#L35 const ( ContainerRegistryStatusDeleteScheduled ContainerRegistryStatus = "delete_scheduled" ContainerRegistryStatusDeleteFailed ContainerRegistryStatus = "delete_failed" ContainerRegistryStatusDeleteOngoing ContainerRegistryStatus = "delete_ongoing" ) // DeploymentApprovalStatus represents a Gitlab deployment approval status. type DeploymentApprovalStatus string // These constants represent all valid deployment approval statuses. const ( DeploymentApprovalStatusApproved DeploymentApprovalStatus = "approved" DeploymentApprovalStatusRejected DeploymentApprovalStatus = "rejected" ) // DeploymentStatusValue represents a Gitlab deployment status. type DeploymentStatusValue string // These constants represent all valid deployment statuses. const ( DeploymentStatusCreated DeploymentStatusValue = "created" DeploymentStatusRunning DeploymentStatusValue = "running" DeploymentStatusSuccess DeploymentStatusValue = "success" DeploymentStatusFailed DeploymentStatusValue = "failed" DeploymentStatusCanceled DeploymentStatusValue = "canceled" ) // DeploymentStatus is a helper routine that allocates a new // DeploymentStatusValue to store v and returns a pointer to it. // // Deprecated: Please use Ptr instead. func DeploymentStatus(v DeploymentStatusValue) *DeploymentStatusValue { return Ptr(v) } // DORAMetricType represents all valid DORA metrics types. // // GitLab API docs: https://docs.gitlab.com/ee/api/dora/metrics.html type DORAMetricType string // List of available DORA metric type names. // // GitLab API docs: https://docs.gitlab.com/ee/api/dora/metrics.html const ( DORAMetricDeploymentFrequency DORAMetricType = "deployment_frequency" DORAMetricLeadTimeForChanges DORAMetricType = "lead_time_for_changes" DORAMetricTimeToRestoreService DORAMetricType = "time_to_restore_service" DORAMetricChangeFailureRate DORAMetricType = "change_failure_rate" ) // DORAMetricInterval represents the time period over which the // metrics are aggregated. // // GitLab API docs: https://docs.gitlab.com/ee/api/dora/metrics.html type DORAMetricInterval string // List of available DORA metric interval types. // // GitLab API docs: https://docs.gitlab.com/ee/api/dora/metrics.html const ( DORAMetricIntervalDaily DORAMetricInterval = "daily" DORAMetricIntervalMonthly DORAMetricInterval = "monthly" DORAMetricIntervalAll DORAMetricInterval = "all" ) // EventTypeValue represents actions type for contribution events. type EventTypeValue string // List of available action type. // // GitLab API docs: // https://docs.gitlab.com/ee/user/profile/contributions_calendar.html#user-contribution-events const ( CreatedEventType EventTypeValue = "created" UpdatedEventType EventTypeValue = "updated" ClosedEventType EventTypeValue = "closed" ReopenedEventType EventTypeValue = "reopened" PushedEventType EventTypeValue = "pushed" CommentedEventType EventTypeValue = "commented" MergedEventType EventTypeValue = "merged" JoinedEventType EventTypeValue = "joined" LeftEventType EventTypeValue = "left" DestroyedEventType EventTypeValue = "destroyed" ExpiredEventType EventTypeValue = "expired" ) // EventTargetTypeValue represents actions type value for contribution events. type EventTargetTypeValue string // List of available action type. // // GitLab API docs: https://docs.gitlab.com/ee/api/events.html#target-types const ( IssueEventTargetType EventTargetTypeValue = "issue" MilestoneEventTargetType EventTargetTypeValue = "milestone" MergeRequestEventTargetType EventTargetTypeValue = "merge_request" NoteEventTargetType EventTargetTypeValue = "note" ProjectEventTargetType EventTargetTypeValue = "project" SnippetEventTargetType EventTargetTypeValue = "snippet" UserEventTargetType EventTargetTypeValue = "user" ) // FileActionValue represents the available actions that can be performed on a file. // // GitLab API docs: // https://docs.gitlab.com/ee/api/commits.html#create-a-commit-with-multiple-files-and-actions type FileActionValue string // The available file actions. const ( FileCreate FileActionValue = "create" FileDelete FileActionValue = "delete" FileMove FileActionValue = "move" FileUpdate FileActionValue = "update" FileChmod FileActionValue = "chmod" ) // FileAction is a helper routine that allocates a new FileActionValue value // to store v and returns a pointer to it. // // Deprecated: Please use Ptr instead. func FileAction(v FileActionValue) *FileActionValue { return Ptr(v) } // GenericPackageSelectValue represents a generic package select value. type GenericPackageSelectValue string // The available generic package select values. const ( SelectPackageFile GenericPackageSelectValue = "package_file" ) // GenericPackageSelect is a helper routine that allocates a new // GenericPackageSelectValue value to store v and returns a pointer to it. // // Deprecated: Please use Ptr instead. func GenericPackageSelect(v GenericPackageSelectValue) *GenericPackageSelectValue { return Ptr(v) } // GenericPackageStatusValue represents a generic package status. type GenericPackageStatusValue string // The available generic package statuses. const ( PackageDefault GenericPackageStatusValue = "default" PackageHidden GenericPackageStatusValue = "hidden" ) // GenericPackageStatus is a helper routine that allocates a new // GenericPackageStatusValue value to store v and returns a pointer to it. // // Deprecated: Please use Ptr instead. func GenericPackageStatus(v GenericPackageStatusValue) *GenericPackageStatusValue { return Ptr(v) } // GroupHookTrigger represents the type of event to trigger for a group // hook test. type GroupHookTrigger string // List of available group hook trigger types. const ( GroupHookTriggerPush GroupHookTrigger = "push_events" GroupHookTriggerTagPush GroupHookTrigger = "tag_push_events" GroupHookTriggerIssue GroupHookTrigger = "issues_events" GroupHookTriggerConfidentialIssue GroupHookTrigger = "confidential_issues_events" GroupHookTriggerNote GroupHookTrigger = "note_events" GroupHookTriggerMergeRequest GroupHookTrigger = "merge_requests_events" GroupHookTriggerJob GroupHookTrigger = "job_events" GroupHookTriggerPipeline GroupHookTrigger = "pipeline_events" GroupHookTriggerWikiPage GroupHookTrigger = "wiki_page_events" GroupHookTriggerRelease GroupHookTrigger = "releases_events" GroupHookTriggerEmoji GroupHookTrigger = "emoji_events" GroupHookTriggerResourceAccessToken GroupHookTrigger = "resource_access_token_events" ) // ISOTime represents an ISO 8601 formatted date. type ISOTime time.Time // ISO 8601 date format. const iso8601 = "2006-01-02" // ParseISOTime parses an ISO 8601 formatted date. func ParseISOTime(s string) (ISOTime, error) { t, err := time.Parse(iso8601, s) return ISOTime(t), err } // MarshalJSON implements the json.Marshaler interface. func (t ISOTime) MarshalJSON() ([]byte, error) { if reflect.ValueOf(t).IsZero() { return []byte(`null`), nil } if y := time.Time(t).Year(); y < 0 || y >= 10000 { // ISO 8901 uses 4 digits for the years. return nil, errors.New("json: ISOTime year outside of range [0,9999]") } b := make([]byte, 0, len(iso8601)+2) b = append(b, '"') b = time.Time(t).AppendFormat(b, iso8601) b = append(b, '"') return b, nil } // UnmarshalJSON implements the json.Unmarshaler interface. func (t *ISOTime) UnmarshalJSON(data []byte) error { // Ignore null, like in the main JSON package. if string(data) == "null" { return nil } isotime, err := time.Parse(`"`+iso8601+`"`, string(data)) *t = ISOTime(isotime) return err } // EncodeValues implements the query.Encoder interface. func (t *ISOTime) EncodeValues(key string, v *url.Values) error { if t == nil || (time.Time(*t)).IsZero() { return nil } v.Add(key, t.String()) return nil } // String implements the Stringer interface. func (t ISOTime) String() string { return time.Time(t).Format(iso8601) } // Labels represents a list of labels. type Labels []string // LabelOptions is a custom type with specific marshaling characteristics. type LabelOptions []string // MarshalJSON implements the json.Marshaler interface. func (l *LabelOptions) MarshalJSON() ([]byte, error) { if *l == nil { return []byte(`null`), nil } return json.Marshal(strings.Join(*l, ",")) } // UnmarshalJSON implements the json.Unmarshaler interface. func (l *LabelOptions) UnmarshalJSON(data []byte) error { type alias LabelOptions if !bytes.HasPrefix(data, []byte("[")) { data = []byte(fmt.Sprintf("[%s]", string(data))) } return json.Unmarshal(data, (*alias)(l)) } // EncodeValues implements the query.EncodeValues interface. func (l *LabelOptions) EncodeValues(key string, v *url.Values) error { v.Set(key, strings.Join(*l, ",")) return nil } // LinkTypeValue represents a release link type. type LinkTypeValue string // List of available release link types. // // GitLab API docs: // https://docs.gitlab.com/ee/api/releases/links.html#create-a-release-link const ( ImageLinkType LinkTypeValue = "image" OtherLinkType LinkTypeValue = "other" PackageLinkType LinkTypeValue = "package" RunbookLinkType LinkTypeValue = "runbook" ) // LinkType is a helper routine that allocates a new LinkType value // to store v and returns a pointer to it. // // Deprecated: Please use Ptr instead. func LinkType(v LinkTypeValue) *LinkTypeValue { return Ptr(v) } // LicenseApprovalStatusValue describe the approval statuses of a license. // // GitLab API docs: https://docs.gitlab.com/ee/api/managed_licenses.html type LicenseApprovalStatusValue string // List of available license approval statuses. const ( LicenseApproved LicenseApprovalStatusValue = "approved" LicenseBlacklisted LicenseApprovalStatusValue = "blacklisted" LicenseAllowed LicenseApprovalStatusValue = "allowed" LicenseDenied LicenseApprovalStatusValue = "denied" ) // LicenseApprovalStatus is a helper routine that allocates a new license // approval status value to store v and returns a pointer to it. // // Deprecated: Please use Ptr instead. func LicenseApprovalStatus(v LicenseApprovalStatusValue) *LicenseApprovalStatusValue { return Ptr(v) } // MergeMethodValue represents a project merge type within GitLab. // // GitLab API docs: https://docs.gitlab.com/ee/api/projects.html#project-merge-method type MergeMethodValue string // List of available merge type // // GitLab API docs: https://docs.gitlab.com/ee/api/projects.html#project-merge-method const ( NoFastForwardMerge MergeMethodValue = "merge" FastForwardMerge MergeMethodValue = "ff" RebaseMerge MergeMethodValue = "rebase_merge" ) // MergeMethod is a helper routine that allocates a new MergeMethod // to store v and returns a pointer to it. // // Deprecated: Please use Ptr instead. func MergeMethod(v MergeMethodValue) *MergeMethodValue { return Ptr(v) } // NoteTypeValue represents the type of a Note. type NoteTypeValue string // List of available note types. const ( DiffNote NoteTypeValue = "DiffNote" DiscussionNote NoteTypeValue = "DiscussionNote" GenericNote NoteTypeValue = "Note" LegacyDiffNote NoteTypeValue = "LegacyDiffNote" ) // NoteType is a helper routine that allocates a new NoteTypeValue to // store v and returns a pointer to it. // // Deprecated: Please use Ptr instead. func NoteType(v NoteTypeValue) *NoteTypeValue { return Ptr(v) } // NotificationLevelValue represents a notification level. type NotificationLevelValue int // String implements the fmt.Stringer interface. func (l NotificationLevelValue) String() string { return notificationLevelNames[l] } // MarshalJSON implements the json.Marshaler interface. func (l NotificationLevelValue) MarshalJSON() ([]byte, error) { return json.Marshal(l.String()) } // UnmarshalJSON implements the json.Unmarshaler interface. func (l *NotificationLevelValue) UnmarshalJSON(data []byte) error { var raw interface{} if err := json.Unmarshal(data, &raw); err != nil { return err } switch raw := raw.(type) { case float64: *l = NotificationLevelValue(raw) case string: *l = notificationLevelTypes[raw] case nil: // No action needed. default: return fmt.Errorf("json: cannot unmarshal %T into Go value of type %T", raw, *l) } return nil } // List of valid notification levels. const ( DisabledNotificationLevel NotificationLevelValue = iota ParticipatingNotificationLevel WatchNotificationLevel GlobalNotificationLevel MentionNotificationLevel CustomNotificationLevel ) var notificationLevelNames = [...]string{ "disabled", "participating", "watch", "global", "mention", "custom", } var notificationLevelTypes = map[string]NotificationLevelValue{ "disabled": DisabledNotificationLevel, "participating": ParticipatingNotificationLevel, "watch": WatchNotificationLevel, "global": GlobalNotificationLevel, "mention": MentionNotificationLevel, "custom": CustomNotificationLevel, } // NotificationLevel is a helper routine that allocates a new NotificationLevelValue // to store v and returns a pointer to it. // // Deprecated: Please use Ptr instead. func NotificationLevel(v NotificationLevelValue) *NotificationLevelValue { return Ptr(v) } // ProjectCreationLevelValue represents a project creation level within GitLab. // // GitLab API docs: https://docs.gitlab.com/ee/api/ type ProjectCreationLevelValue string // List of available project creation levels. // // GitLab API docs: https://docs.gitlab.com/ee/api/ const ( NoOneProjectCreation ProjectCreationLevelValue = "noone" MaintainerProjectCreation ProjectCreationLevelValue = "maintainer" DeveloperProjectCreation ProjectCreationLevelValue = "developer" OwnerProjectCreation ProjectCreationLevelValue = "owner" ) // ProjectCreationLevel is a helper routine that allocates a new ProjectCreationLevelValue // to store v and returns a pointer to it. // Please use Ptr instead. func ProjectCreationLevel(v ProjectCreationLevelValue) *ProjectCreationLevelValue { return Ptr(v) } // ProjectHookEvent represents a project hook event. // // GitLab API docs: https://docs.gitlab.com/ee/api/projects.html#hook-events type ProjectHookEvent string // List of available project hook events. // // GitLab API docs: https://docs.gitlab.com/ee/api/projects.html#hook-events const ( ProjectHookEventPush ProjectHookEvent = "push_events" ProjectHookEventTagPush ProjectHookEvent = "tag_push_events" ProjectHookEventIssues ProjectHookEvent = "issues_events" ProjectHookEventConfidentialIssues ProjectHookEvent = "confidential_issues_events" ProjectHookEventNote ProjectHookEvent = "note_events" ProjectHookEventMergeRequests ProjectHookEvent = "merge_requests_events" ProjectHookEventJob ProjectHookEvent = "job_events" ProjectHookEventPipeline ProjectHookEvent = "pipeline_events" ProjectHookEventWiki ProjectHookEvent = "wiki_page_events" ProjectHookEventReleases ProjectHookEvent = "releases_events" ProjectHookEventEmoji ProjectHookEvent = "emoji_events" ProjectHookEventResourceAccessToken ProjectHookEvent = "resource_access_token_events" ) // ResourceGroupProcessMode represents a process mode for a resource group // within a GitLab project. // // GitLab API docs: // https://docs.gitlab.com/ee/ci/resource_groups/index.html#process-modes type ResourceGroupProcessMode string // List of available resource group process modes. // // GitLab API docs: // https://docs.gitlab.com/ee/ci/resource_groups/index.html#process-modes const ( Unordered ResourceGroupProcessMode = "unordered" OldestFirst ResourceGroupProcessMode = "oldest_first" NewestFirst ResourceGroupProcessMode = "newest_first" ) // SharedRunnersSettingValue determines whether shared runners are enabled for a // group’s subgroups and projects. // // GitLab API docs: // https://docs.gitlab.com/ee/api/groups.html#options-for-shared_runners_setting type SharedRunnersSettingValue string // List of available shared runner setting levels. // // GitLab API docs: // https://docs.gitlab.com/ee/api/groups.html#options-for-shared_runners_setting const ( EnabledSharedRunnersSettingValue SharedRunnersSettingValue = "enabled" DisabledAndOverridableSharedRunnersSettingValue SharedRunnersSettingValue = "disabled_and_overridable" DisabledAndUnoverridableSharedRunnersSettingValue SharedRunnersSettingValue = "disabled_and_unoverridable" // Deprecated: DisabledWithOverrideSharedRunnersSettingValue is deprecated // in favor of DisabledAndOverridableSharedRunnersSettingValue. DisabledWithOverrideSharedRunnersSettingValue SharedRunnersSettingValue = "disabled_with_override" ) // SharedRunnersSetting is a helper routine that allocates a new SharedRunnersSettingValue // to store v and returns a pointer to it. // // Deprecated: Please use Ptr instead. func SharedRunnersSetting(v SharedRunnersSettingValue) *SharedRunnersSettingValue { return Ptr(v) } // SubGroupCreationLevelValue represents a sub group creation level within GitLab. // // GitLab API docs: https://docs.gitlab.com/ee/api/ type SubGroupCreationLevelValue string // List of available sub group creation levels. // // GitLab API docs: https://docs.gitlab.com/ee/api/ const ( OwnerSubGroupCreationLevelValue SubGroupCreationLevelValue = "owner" MaintainerSubGroupCreationLevelValue SubGroupCreationLevelValue = "maintainer" ) // SubGroupCreationLevel is a helper routine that allocates a new SubGroupCreationLevelValue // to store v and returns a pointer to it. // // Deprecated: Please use Ptr instead. func SubGroupCreationLevel(v SubGroupCreationLevelValue) *SubGroupCreationLevelValue { return Ptr(v) } // SquashOptionValue represents a squash optional level within GitLab. // // GitLab API docs: https://docs.gitlab.com/ee/api/projects.html#create-project type SquashOptionValue string // List of available squash options. // // GitLab API docs: https://docs.gitlab.com/ee/api/projects.html#create-project const ( SquashOptionNever SquashOptionValue = "never" SquashOptionAlways SquashOptionValue = "always" SquashOptionDefaultOff SquashOptionValue = "default_off" SquashOptionDefaultOn SquashOptionValue = "default_on" ) // SquashOption is a helper routine that allocates a new SquashOptionValue // to store s and returns a pointer to it. // // Deprecated: Please use Ptr instead. func SquashOption(s SquashOptionValue) *SquashOptionValue { return Ptr(s) } // TasksCompletionStatus represents tasks of the issue/merge request. type TasksCompletionStatus struct { Count int `json:"count"` CompletedCount int `json:"completed_count"` } // TodoAction represents the available actions that can be performed on a todo. // // GitLab API docs: https://docs.gitlab.com/ee/api/todos.html type TodoAction string // The available todo actions. const ( TodoAssigned TodoAction = "assigned" TodoMentioned TodoAction = "mentioned" TodoBuildFailed TodoAction = "build_failed" TodoMarked TodoAction = "marked" TodoApprovalRequired TodoAction = "approval_required" TodoDirectlyAddressed TodoAction = "directly_addressed" ) // TodoTargetType represents the available target that can be linked to a todo. // // GitLab API docs: https://docs.gitlab.com/ee/api/todos.html type TodoTargetType string const ( TodoTargetAlertManagement TodoTargetType = "AlertManagement::Alert" TodoTargetDesignManagement TodoTargetType = "DesignManagement::Design" TodoTargetIssue TodoTargetType = "Issue" TodoTargetMergeRequest TodoTargetType = "MergeRequest" ) // UploadType represents the available upload types. type UploadType string // The available upload types. const ( UploadAvatar UploadType = "avatar" UploadFile UploadType = "file" ) // VariableTypeValue represents a variable type within GitLab. // // GitLab API docs: https://docs.gitlab.com/ee/api/ type VariableTypeValue string // List of available variable types. // // GitLab API docs: https://docs.gitlab.com/ee/api/ const ( EnvVariableType VariableTypeValue = "env_var" FileVariableType VariableTypeValue = "file" ) // VariableType is a helper routine that allocates a new VariableTypeValue // to store v and returns a pointer to it. // // Deprecated: Please use Ptr instead. func VariableType(v VariableTypeValue) *VariableTypeValue { return Ptr(v) } // VisibilityValue represents a visibility level within GitLab. // // GitLab API docs: https://docs.gitlab.com/ee/api/ type VisibilityValue string // List of available visibility levels. // // GitLab API docs: https://docs.gitlab.com/ee/api/ const ( PrivateVisibility VisibilityValue = "private" InternalVisibility VisibilityValue = "internal" PublicVisibility VisibilityValue = "public" ) // Visibility is a helper routine that allocates a new VisibilityValue // to store v and returns a pointer to it. // // Deprecated: Please use Ptr instead. func Visibility(v VisibilityValue) *VisibilityValue { return Ptr(v) } // WikiFormatValue represents the available wiki formats. // // GitLab API docs: https://docs.gitlab.com/ee/api/wikis.html type WikiFormatValue string // The available wiki formats. const ( WikiFormatMarkdown WikiFormatValue = "markdown" WikiFormatRDoc WikiFormatValue = "rdoc" WikiFormatASCIIDoc WikiFormatValue = "asciidoc" WikiFormatOrg WikiFormatValue = "org" ) // WikiFormat is a helper routine that allocates a new WikiFormatValue // to store v and returns a pointer to it. // // Deprecated: Please use Ptr instead. func WikiFormat(v WikiFormatValue) *WikiFormatValue { return Ptr(v) } // Bool is a helper routine that allocates a new bool value // to store v and returns a pointer to it. // // Deprecated: Please use Ptr instead. 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. // // Deprecated: Please use Ptr instead. func Int(v int) *int { return Ptr(v) } // String is a helper routine that allocates a new string value // to store v and returns a pointer to it. // // Deprecated: Please use Ptr instead. func String(v string) *string { return Ptr(v) } // Time is a helper routine that allocates a new time.Time value // to store v and returns a pointer to it. // // Deprecated: Please use Ptr instead. func Time(v time.Time) *time.Time { return Ptr(v) } // BoolValue is a boolean value with advanced json unmarshaling features. type BoolValue bool // UnmarshalJSON allows 1, 0, "true", and "false" to be considered as boolean values // Needed for: // https://gitlab.com/gitlab-org/gitlab-ce/issues/50122 // https://gitlab.com/gitlab-org/gitlab/-/issues/233941 // https://github.com/gitlabhq/terraform-provider-gitlab/issues/348 func (t *BoolValue) UnmarshalJSON(b []byte) error { switch string(b) { case `"1"`: *t = true return nil case `"0"`: *t = false return nil case `"true"`: *t = true return nil case `"false"`: *t = false return nil default: var v bool err := json.Unmarshal(b, &v) *t = BoolValue(v) return err } } // CIPipelineVariablesMinimumOverrideRoleValue represents an access control // value used for managing access to the CI Pipeline Variable Override feature. // // GitLab API docs: https://docs.gitlab.com/ee/api/projects.html type CIPipelineVariablesMinimumOverrideRoleValue = string // List of available CIPipelineVariablesMinimumOverrideRoleValue values. // // GitLab API docs: https://docs.gitlab.com/ee/api/projects.html const ( CIPipelineVariablesNoOneAllowedRole CIPipelineVariablesMinimumOverrideRoleValue = "no_one_allowed" CiPipelineVariablesOwnerRole CIPipelineVariablesMinimumOverrideRoleValue = "owner" CiPipelineVariablesMaintainerRole CIPipelineVariablesMinimumOverrideRoleValue = "maintainer" CIPipelineVariablesDeveloperRole CIPipelineVariablesMinimumOverrideRoleValue = "developer" ) golang-gitlab-gitlab-org-api-client-go-0.123.0/types_test.go000066400000000000000000000033071475761473200236140ustar00rootroot00000000000000// // Copyright 2021, Sander van Harmelen // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "encoding/json" "testing" ) func TestBoolValue(t *testing.T) { testCases := []struct { name string data []byte expected bool }{ { name: "should unmarshal true as true", data: []byte("true"), expected: true, }, { name: "should unmarshal false as false", data: []byte("false"), expected: false, }, { name: "should unmarshal true as true", data: []byte(`"true"`), expected: true, }, { name: "should unmarshal false as false", data: []byte(`"false"`), expected: false, }, { name: "should unmarshal \"1\" as true", data: []byte(`"1"`), expected: true, }, { name: "should unmarshal \"0\" as false", data: []byte(`"0"`), expected: false, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { var b BoolValue if err := json.Unmarshal(testCase.data, &b); err != nil { t.Fatalf("Unexpected error: %v", err) } if bool(b) != testCase.expected { t.Fatalf("Expected %v but got %v", testCase.expected, b) } }) } } golang-gitlab-gitlab-org-api-client-go-0.123.0/users.go000066400000000000000000001516731475761473200225640ustar00rootroot00000000000000// // Copyright 2021, Sander van Harmelen // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "encoding/json" "errors" "fmt" "io" "net" "net/http" "time" "github.com/hashicorp/go-retryablehttp" ) // List a couple of standard errors. var ( ErrUserActivatePrevented = errors.New("Cannot activate a user that is blocked by admin or by LDAP synchronization") ErrUserApprovePrevented = errors.New("Cannot approve a user that is blocked by admin or by LDAP synchronization") ErrUserBlockPrevented = errors.New("Cannot block a user that is already blocked by LDAP synchronization") ErrUserConflict = errors.New("User does not have a pending request") ErrUserDeactivatePrevented = errors.New("Cannot deactivate a user that is blocked by admin or by LDAP synchronization") ErrUserDisableTwoFactorPrevented = errors.New("Cannot disable two factor authentication if not authenticated as administrator") ErrUserNotFound = errors.New("User does not exist") ErrUserRejectPrevented = errors.New("Cannot reject a user if not authenticated as administrator") ErrUserTwoFactorNotEnabled = errors.New("Cannot disable two factor authentication if not enabled") ErrUserUnblockPrevented = errors.New("Cannot unblock a user that is blocked by LDAP synchronization") ) // UsersService handles communication with the user related methods of // the GitLab API. // // GitLab API docs: https://docs.gitlab.com/ee/api/users.html type UsersService struct { client *Client } // BasicUser included in other service responses (such as merge requests, pipelines, etc). type BasicUser struct { ID int `json:"id"` Username string `json:"username"` Name string `json:"name"` State string `json:"state"` Locked bool `json:"locked"` CreatedAt *time.Time `json:"created_at"` AvatarURL string `json:"avatar_url"` WebURL string `json:"web_url"` } // ServiceAccount represents a GitLab service account. // // GitLab API docs: // https://docs.gitlab.com/ee/api/user_service_accounts.html type ServiceAccount struct { ID int `json:"id"` Username string `json:"username"` Name string `json:"name"` } // User represents a GitLab user. // // GitLab API docs: https://docs.gitlab.com/ee/api/users.html type User struct { ID int `json:"id"` Username string `json:"username"` Email string `json:"email"` Name string `json:"name"` State string `json:"state"` WebURL string `json:"web_url"` CreatedAt *time.Time `json:"created_at"` Bio string `json:"bio"` Bot bool `json:"bot"` Location string `json:"location"` PublicEmail string `json:"public_email"` Skype string `json:"skype"` Linkedin string `json:"linkedin"` Twitter string `json:"twitter"` WebsiteURL string `json:"website_url"` Organization string `json:"organization"` JobTitle string `json:"job_title"` ExternUID string `json:"extern_uid"` Provider string `json:"provider"` ThemeID int `json:"theme_id"` LastActivityOn *ISOTime `json:"last_activity_on"` ColorSchemeID int `json:"color_scheme_id"` IsAdmin bool `json:"is_admin"` IsAuditor bool `json:"is_auditor"` AvatarURL string `json:"avatar_url"` CanCreateGroup bool `json:"can_create_group"` CanCreateProject bool `json:"can_create_project"` ProjectsLimit int `json:"projects_limit"` CurrentSignInAt *time.Time `json:"current_sign_in_at"` CurrentSignInIP *net.IP `json:"current_sign_in_ip"` LastSignInAt *time.Time `json:"last_sign_in_at"` LastSignInIP *net.IP `json:"last_sign_in_ip"` ConfirmedAt *time.Time `json:"confirmed_at"` TwoFactorEnabled bool `json:"two_factor_enabled"` Note string `json:"note"` Identities []*UserIdentity `json:"identities"` External bool `json:"external"` PrivateProfile bool `json:"private_profile"` SharedRunnersMinutesLimit int `json:"shared_runners_minutes_limit"` ExtraSharedRunnersMinutesLimit int `json:"extra_shared_runners_minutes_limit"` UsingLicenseSeat bool `json:"using_license_seat"` CustomAttributes []*CustomAttribute `json:"custom_attributes"` NamespaceID int `json:"namespace_id"` Locked bool `json:"locked"` CreatedBy *BasicUser `json:"created_by"` } // UserIdentity represents a user identity. type UserIdentity struct { Provider string `json:"provider"` ExternUID string `json:"extern_uid"` } // UserAvatar represents a GitLab user avatar. // // GitLab API docs: https://docs.gitlab.com/ee/api/users.html type UserAvatar struct { Filename string Image io.Reader } // MarshalJSON implements the json.Marshaler interface. func (a *UserAvatar) MarshalJSON() ([]byte, error) { if a.Filename == "" && a.Image == nil { return []byte(`""`), nil } type alias UserAvatar return json.Marshal((*alias)(a)) } // ListUsersOptions represents the available ListUsers() options. // // GitLab API docs: https://docs.gitlab.com/ee/api/users.html#list-users type ListUsersOptions struct { ListOptions Active *bool `url:"active,omitempty" json:"active,omitempty"` Blocked *bool `url:"blocked,omitempty" json:"blocked,omitempty"` ExcludeInternal *bool `url:"exclude_internal,omitempty" json:"exclude_internal,omitempty"` ExcludeExternal *bool `url:"exclude_external,omitempty" json:"exclude_external,omitempty"` // The options below are only available for admins. Search *string `url:"search,omitempty" json:"search,omitempty"` Username *string `url:"username,omitempty" json:"username,omitempty"` ExternalUID *string `url:"extern_uid,omitempty" json:"extern_uid,omitempty"` Provider *string `url:"provider,omitempty" json:"provider,omitempty"` CreatedBefore *time.Time `url:"created_before,omitempty" json:"created_before,omitempty"` CreatedAfter *time.Time `url:"created_after,omitempty" json:"created_after,omitempty"` OrderBy *string `url:"order_by,omitempty" json:"order_by,omitempty"` Sort *string `url:"sort,omitempty" json:"sort,omitempty"` TwoFactor *string `url:"two_factor,omitempty" json:"two_factor,omitempty"` Admins *bool `url:"admins,omitempty" json:"admins,omitempty"` External *bool `url:"external,omitempty" json:"external,omitempty"` WithoutProjects *bool `url:"without_projects,omitempty" json:"without_projects,omitempty"` WithCustomAttributes *bool `url:"with_custom_attributes,omitempty" json:"with_custom_attributes,omitempty"` WithoutProjectBots *bool `url:"without_project_bots,omitempty" json:"without_project_bots,omitempty"` } // ListUsers gets a list of users. // // GitLab API docs: https://docs.gitlab.com/ee/api/users.html#list-users func (s *UsersService) ListUsers(opt *ListUsersOptions, options ...RequestOptionFunc) ([]*User, *Response, error) { req, err := s.client.NewRequest(http.MethodGet, "users", opt, options) if err != nil { return nil, nil, err } var usr []*User resp, err := s.client.Do(req, &usr) if err != nil { return nil, resp, err } return usr, resp, nil } // GetUsersOptions represents the available GetUser() options. // // GitLab API docs: https://docs.gitlab.com/ee/api/users.html#single-user type GetUsersOptions struct { WithCustomAttributes *bool `url:"with_custom_attributes,omitempty" json:"with_custom_attributes,omitempty"` } // GetUser gets a single user. // // GitLab API docs: https://docs.gitlab.com/ee/api/users.html#single-user func (s *UsersService) GetUser(user int, opt GetUsersOptions, options ...RequestOptionFunc) (*User, *Response, error) { u := fmt.Sprintf("users/%d", user) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } usr := new(User) resp, err := s.client.Do(req, usr) if err != nil { return nil, resp, err } return usr, resp, nil } // CreateUserOptions represents the available CreateUser() options. // // GitLab API docs: https://docs.gitlab.com/ee/api/users.html#user-creation type CreateUserOptions struct { Admin *bool `url:"admin,omitempty" json:"admin,omitempty"` Avatar *UserAvatar `url:"-" json:"-"` Bio *string `url:"bio,omitempty" json:"bio,omitempty"` CanCreateGroup *bool `url:"can_create_group,omitempty" json:"can_create_group,omitempty"` Email *string `url:"email,omitempty" json:"email,omitempty"` External *bool `url:"external,omitempty" json:"external,omitempty"` ExternUID *string `url:"extern_uid,omitempty" json:"extern_uid,omitempty"` ForceRandomPassword *bool `url:"force_random_password,omitempty" json:"force_random_password,omitempty"` JobTitle *string `url:"job_title,omitempty" json:"job_title,omitempty"` Linkedin *string `url:"linkedin,omitempty" json:"linkedin,omitempty"` Location *string `url:"location,omitempty" json:"location,omitempty"` Name *string `url:"name,omitempty" json:"name,omitempty"` Note *string `url:"note,omitempty" json:"note,omitempty"` Organization *string `url:"organization,omitempty" json:"organization,omitempty"` Password *string `url:"password,omitempty" json:"password,omitempty"` PrivateProfile *bool `url:"private_profile,omitempty" json:"private_profile,omitempty"` ProjectsLimit *int `url:"projects_limit,omitempty" json:"projects_limit,omitempty"` Provider *string `url:"provider,omitempty" json:"provider,omitempty"` ResetPassword *bool `url:"reset_password,omitempty" json:"reset_password,omitempty"` SkipConfirmation *bool `url:"skip_confirmation,omitempty" json:"skip_confirmation,omitempty"` Skype *string `url:"skype,omitempty" json:"skype,omitempty"` ThemeID *int `url:"theme_id,omitempty" json:"theme_id,omitempty"` Twitter *string `url:"twitter,omitempty" json:"twitter,omitempty"` Username *string `url:"username,omitempty" json:"username,omitempty"` WebsiteURL *string `url:"website_url,omitempty" json:"website_url,omitempty"` } // CreateUser creates a new user. Note only administrators can create new users. // // GitLab API docs: https://docs.gitlab.com/ee/api/users.html#user-creation func (s *UsersService) CreateUser(opt *CreateUserOptions, options ...RequestOptionFunc) (*User, *Response, error) { var err error var req *retryablehttp.Request if opt.Avatar == nil { req, err = s.client.NewRequest(http.MethodPost, "users", opt, options) } else { req, err = s.client.UploadRequest( http.MethodPost, "users", opt.Avatar.Image, opt.Avatar.Filename, UploadAvatar, opt, options, ) } if err != nil { return nil, nil, err } usr := new(User) resp, err := s.client.Do(req, usr) if err != nil { return nil, resp, err } return usr, resp, nil } // ModifyUserOptions represents the available ModifyUser() options. // // GitLab API docs: https://docs.gitlab.com/ee/api/users.html#user-modification type ModifyUserOptions struct { Admin *bool `url:"admin,omitempty" json:"admin,omitempty"` Avatar *UserAvatar `url:"-" json:"avatar,omitempty"` Bio *string `url:"bio,omitempty" json:"bio,omitempty"` CanCreateGroup *bool `url:"can_create_group,omitempty" json:"can_create_group,omitempty"` CommitEmail *string `url:"commit_email,omitempty" json:"commit_email,omitempty"` Email *string `url:"email,omitempty" json:"email,omitempty"` External *bool `url:"external,omitempty" json:"external,omitempty"` ExternUID *string `url:"extern_uid,omitempty" json:"extern_uid,omitempty"` JobTitle *string `url:"job_title,omitempty" json:"job_title,omitempty"` Linkedin *string `url:"linkedin,omitempty" json:"linkedin,omitempty"` Location *string `url:"location,omitempty" json:"location,omitempty"` Name *string `url:"name,omitempty" json:"name,omitempty"` Note *string `url:"note,omitempty" json:"note,omitempty"` Organization *string `url:"organization,omitempty" json:"organization,omitempty"` Password *string `url:"password,omitempty" json:"password,omitempty"` PrivateProfile *bool `url:"private_profile,omitempty" json:"private_profile,omitempty"` ProjectsLimit *int `url:"projects_limit,omitempty" json:"projects_limit,omitempty"` Provider *string `url:"provider,omitempty" json:"provider,omitempty"` PublicEmail *string `url:"public_email,omitempty" json:"public_email,omitempty"` SkipReconfirmation *bool `url:"skip_reconfirmation,omitempty" json:"skip_reconfirmation,omitempty"` Skype *string `url:"skype,omitempty" json:"skype,omitempty"` ThemeID *int `url:"theme_id,omitempty" json:"theme_id,omitempty"` Twitter *string `url:"twitter,omitempty" json:"twitter,omitempty"` Username *string `url:"username,omitempty" json:"username,omitempty"` WebsiteURL *string `url:"website_url,omitempty" json:"website_url,omitempty"` } // ModifyUser modifies an existing user. Only administrators can change attributes // of a user. // // GitLab API docs: https://docs.gitlab.com/ee/api/users.html#user-modification func (s *UsersService) ModifyUser(user int, opt *ModifyUserOptions, options ...RequestOptionFunc) (*User, *Response, error) { var err error var req *retryablehttp.Request u := fmt.Sprintf("users/%d", user) if opt.Avatar == nil || (opt.Avatar.Filename == "" && opt.Avatar.Image == nil) { req, err = s.client.NewRequest(http.MethodPut, u, opt, options) } else { req, err = s.client.UploadRequest( http.MethodPut, u, opt.Avatar.Image, opt.Avatar.Filename, UploadAvatar, opt, options, ) } if err != nil { return nil, nil, err } usr := new(User) resp, err := s.client.Do(req, usr) if err != nil { return nil, resp, err } return usr, resp, nil } // DeleteUser deletes a user. Available only for administrators. This is an // idempotent function, calling this function for a non-existent user id still // returns a status code 200 OK. The JSON response differs if the user was // actually deleted or not. In the former the user is returned and in the // latter not. // // GitLab API docs: https://docs.gitlab.com/ee/api/users.html#user-deletion func (s *UsersService) DeleteUser(user int, options ...RequestOptionFunc) (*Response, error) { u := fmt.Sprintf("users/%d", user) req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } // CurrentUser gets currently authenticated user. // // GitLab API docs: https://docs.gitlab.com/ee/api/users.html#list-current-user func (s *UsersService) CurrentUser(options ...RequestOptionFunc) (*User, *Response, error) { req, err := s.client.NewRequest(http.MethodGet, "user", nil, options) if err != nil { return nil, nil, err } usr := new(User) resp, err := s.client.Do(req, usr) if err != nil { return nil, resp, err } return usr, resp, nil } // UserStatus represents the current status of a user // // GitLab API docs: // https://docs.gitlab.com/ee/api/users.html#user-status type UserStatus struct { Emoji string `json:"emoji"` Availability AvailabilityValue `json:"availability"` Message string `json:"message"` MessageHTML string `json:"message_html"` } // CurrentUserStatus retrieves the user status // // GitLab API docs: // https://docs.gitlab.com/ee/api/users.html#user-status func (s *UsersService) CurrentUserStatus(options ...RequestOptionFunc) (*UserStatus, *Response, error) { req, err := s.client.NewRequest(http.MethodGet, "user/status", nil, options) if err != nil { return nil, nil, err } status := new(UserStatus) resp, err := s.client.Do(req, status) if err != nil { return nil, resp, err } return status, resp, nil } // GetUserStatus retrieves a user's status // // GitLab API docs: // https://docs.gitlab.com/ee/api/users.html#get-the-status-of-a-user func (s *UsersService) GetUserStatus(user int, options ...RequestOptionFunc) (*UserStatus, *Response, error) { u := fmt.Sprintf("users/%d/status", user) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } status := new(UserStatus) resp, err := s.client.Do(req, status) if err != nil { return nil, resp, err } return status, resp, nil } // UserStatusOptions represents the options required to set the status // // GitLab API docs: // https://docs.gitlab.com/ee/api/users.html#set-user-status type UserStatusOptions struct { Emoji *string `url:"emoji,omitempty" json:"emoji,omitempty"` Availability *AvailabilityValue `url:"availability,omitempty" json:"availability,omitempty"` Message *string `url:"message,omitempty" json:"message,omitempty"` } // SetUserStatus sets the user's status // // GitLab API docs: // https://docs.gitlab.com/ee/api/users.html#set-user-status func (s *UsersService) SetUserStatus(opt *UserStatusOptions, options ...RequestOptionFunc) (*UserStatus, *Response, error) { req, err := s.client.NewRequest(http.MethodPut, "user/status", opt, options) if err != nil { return nil, nil, err } status := new(UserStatus) resp, err := s.client.Do(req, status) if err != nil { return nil, resp, err } return status, resp, nil } // UserAssociationsCount represents the user associations count. // // Gitlab API docs: https://docs.gitlab.com/ee/api/users.html#list-associations-count-for-user type UserAssociationsCount struct { GroupsCount int `json:"groups_count"` ProjectsCount int `json:"projects_count"` IssuesCount int `json:"issues_count"` MergeRequestsCount int `json:"merge_requests_count"` } // GetUserAssociationsCount gets a list of a specified user associations. // // Gitlab API docs: https://docs.gitlab.com/ee/api/users.html#list-associations-count-for-user func (s *UsersService) GetUserAssociationsCount(user int, options ...RequestOptionFunc) (*UserAssociationsCount, *Response, error) { u := fmt.Sprintf("users/%d/associations_count", user) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } uac := new(UserAssociationsCount) resp, err := s.client.Do(req, uac) if err != nil { return nil, resp, err } return uac, resp, nil } // SSHKey represents a SSH key. // // GitLab API docs: https://docs.gitlab.com/ee/api/user_keys.html#list-all-ssh-keys type SSHKey struct { ID int `json:"id"` Title string `json:"title"` Key string `json:"key"` CreatedAt *time.Time `json:"created_at"` ExpiresAt *time.Time `json:"expires_at"` UsageType string `json:"usage_type"` } // ListSSHKeysOptions represents the available ListSSHKeys options. // // GitLab API docs: https://docs.gitlab.com/ee/api/user_keys.html#list-all-ssh-keys type ListSSHKeysOptions ListOptions // ListSSHKeys gets a list of currently authenticated user's SSH keys. // // GitLab API docs: https://docs.gitlab.com/ee/api/user_keys.html#list-all-ssh-keys func (s *UsersService) ListSSHKeys(opt *ListSSHKeysOptions, options ...RequestOptionFunc) ([]*SSHKey, *Response, error) { req, err := s.client.NewRequest(http.MethodGet, "user/keys", opt, options) if err != nil { return nil, nil, err } var k []*SSHKey resp, err := s.client.Do(req, &k) if err != nil { return nil, resp, err } return k, resp, nil } // ListSSHKeysForUserOptions represents the available ListSSHKeysForUser() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/users.html#list-ssh-keys-for-user type ListSSHKeysForUserOptions ListOptions // ListSSHKeysForUser gets a list of a specified user's SSH keys. // // GitLab API docs: // https://docs.gitlab.com/ee/api/users.html#list-ssh-keys-for-user func (s *UsersService) ListSSHKeysForUser(uid interface{}, opt *ListSSHKeysForUserOptions, options ...RequestOptionFunc) ([]*SSHKey, *Response, error) { user, err := parseID(uid) if err != nil { return nil, nil, err } u := fmt.Sprintf("users/%s/keys", user) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } var k []*SSHKey resp, err := s.client.Do(req, &k) if err != nil { return nil, resp, err } return k, resp, nil } // GetSSHKey gets a single key. // // GitLab API docs: https://docs.gitlab.com/ee/api/users.html#single-ssh-key func (s *UsersService) GetSSHKey(key int, options ...RequestOptionFunc) (*SSHKey, *Response, error) { u := fmt.Sprintf("user/keys/%d", key) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } k := new(SSHKey) resp, err := s.client.Do(req, k) if err != nil { return nil, resp, err } return k, resp, nil } // GetSSHKeyForUser gets a single key for a given user. // // GitLab API docs: https://docs.gitlab.com/ee/api/users.html#single-ssh-key-for-given-user func (s *UsersService) GetSSHKeyForUser(user int, key int, options ...RequestOptionFunc) (*SSHKey, *Response, error) { u := fmt.Sprintf("users/%d/keys/%d", user, key) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } k := new(SSHKey) resp, err := s.client.Do(req, k) if err != nil { return nil, resp, err } return k, resp, nil } // AddSSHKeyOptions represents the available AddSSHKey() options. // // GitLab API docs: https://docs.gitlab.com/ee/api/users.html#add-ssh-key type AddSSHKeyOptions struct { Title *string `url:"title,omitempty" json:"title,omitempty"` Key *string `url:"key,omitempty" json:"key,omitempty"` ExpiresAt *ISOTime `url:"expires_at,omitempty" json:"expires_at,omitempty"` } // AddSSHKey creates a new key owned by the currently authenticated user. // // GitLab API docs: https://docs.gitlab.com/ee/api/users.html#add-ssh-key func (s *UsersService) AddSSHKey(opt *AddSSHKeyOptions, options ...RequestOptionFunc) (*SSHKey, *Response, error) { req, err := s.client.NewRequest(http.MethodPost, "user/keys", opt, options) if err != nil { return nil, nil, err } k := new(SSHKey) resp, err := s.client.Do(req, k) if err != nil { return nil, resp, err } return k, resp, nil } // AddSSHKeyForUser creates new key owned by specified user. Available only for // admin. // // GitLab API docs: https://docs.gitlab.com/ee/api/users.html#add-ssh-key-for-user func (s *UsersService) AddSSHKeyForUser(user int, opt *AddSSHKeyOptions, options ...RequestOptionFunc) (*SSHKey, *Response, error) { u := fmt.Sprintf("users/%d/keys", user) req, err := s.client.NewRequest(http.MethodPost, u, opt, options) if err != nil { return nil, nil, err } k := new(SSHKey) resp, err := s.client.Do(req, k) if err != nil { return nil, resp, err } return k, resp, nil } // DeleteSSHKey deletes key owned by currently authenticated user. This is an // idempotent function and calling it on a key that is already deleted or not // available results in 200 OK. // // GitLab API docs: // https://docs.gitlab.com/ee/api/users.html#delete-ssh-key-for-current-user func (s *UsersService) DeleteSSHKey(key int, options ...RequestOptionFunc) (*Response, error) { u := fmt.Sprintf("user/keys/%d", key) req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } // DeleteSSHKeyForUser deletes key owned by a specified user. Available only // for admin. // // GitLab API docs: // https://docs.gitlab.com/ee/api/users.html#delete-ssh-key-for-given-user func (s *UsersService) DeleteSSHKeyForUser(user, key int, options ...RequestOptionFunc) (*Response, error) { u := fmt.Sprintf("users/%d/keys/%d", user, key) req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } // GPGKey represents a GPG key. // // GitLab API docs: https://docs.gitlab.com/ee/api/users.html#list-all-gpg-keys type GPGKey struct { ID int `json:"id"` Key string `json:"key"` CreatedAt *time.Time `json:"created_at"` } // ListGPGKeys gets a list of currently authenticated user’s GPG keys. // // GitLab API docs: https://docs.gitlab.com/ee/api/users.html#list-all-gpg-keys func (s *UsersService) ListGPGKeys(options ...RequestOptionFunc) ([]*GPGKey, *Response, error) { req, err := s.client.NewRequest(http.MethodGet, "user/gpg_keys", nil, options) if err != nil { return nil, nil, err } var ks []*GPGKey resp, err := s.client.Do(req, &ks) if err != nil { return nil, resp, err } return ks, resp, nil } // GetGPGKey gets a specific GPG key of currently authenticated user. // // GitLab API docs: https://docs.gitlab.com/ee/api/users.html#get-a-specific-gpg-key func (s *UsersService) GetGPGKey(key int, options ...RequestOptionFunc) (*GPGKey, *Response, error) { u := fmt.Sprintf("user/gpg_keys/%d", key) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } k := new(GPGKey) resp, err := s.client.Do(req, k) if err != nil { return nil, resp, err } return k, resp, nil } // AddGPGKeyOptions represents the available AddGPGKey() options. // // GitLab API docs: https://docs.gitlab.com/ee/api/users.html#add-a-gpg-key type AddGPGKeyOptions struct { Key *string `url:"key,omitempty" json:"key,omitempty"` } // AddGPGKey creates a new GPG key owned by the currently authenticated user. // // GitLab API docs: https://docs.gitlab.com/ee/api/users.html#add-a-gpg-key func (s *UsersService) AddGPGKey(opt *AddGPGKeyOptions, options ...RequestOptionFunc) (*GPGKey, *Response, error) { req, err := s.client.NewRequest(http.MethodPost, "user/gpg_keys", opt, options) if err != nil { return nil, nil, err } k := new(GPGKey) resp, err := s.client.Do(req, k) if err != nil { return nil, resp, err } return k, resp, nil } // DeleteGPGKey deletes a GPG key owned by currently authenticated user. // // GitLab API docs: https://docs.gitlab.com/ee/api/users.html#delete-a-gpg-key func (s *UsersService) DeleteGPGKey(key int, options ...RequestOptionFunc) (*Response, error) { u := fmt.Sprintf("user/gpg_keys/%d", key) req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } // ListGPGKeysForUser gets a list of a specified user’s GPG keys. // // GitLab API docs: // https://docs.gitlab.com/ee/api/users.html#list-all-gpg-keys-for-given-user func (s *UsersService) ListGPGKeysForUser(user int, options ...RequestOptionFunc) ([]*GPGKey, *Response, error) { u := fmt.Sprintf("users/%d/gpg_keys", user) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } var ks []*GPGKey resp, err := s.client.Do(req, &ks) if err != nil { return nil, resp, err } return ks, resp, nil } // GetGPGKeyForUser gets a specific GPG key for a given user. // // GitLab API docs: https://docs.gitlab.com/ee/api/users.html#get-a-specific-gpg-key-for-a-given-user func (s *UsersService) GetGPGKeyForUser(user, key int, options ...RequestOptionFunc) (*GPGKey, *Response, error) { u := fmt.Sprintf("users/%d/gpg_keys/%d", user, key) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } k := new(GPGKey) resp, err := s.client.Do(req, k) if err != nil { return nil, resp, err } return k, resp, nil } // AddGPGKeyForUser creates new GPG key owned by the specified user. // // GitLab API docs: // https://docs.gitlab.com/ee/api/users.html#add-a-gpg-key-for-a-given-user func (s *UsersService) AddGPGKeyForUser(user int, opt *AddGPGKeyOptions, options ...RequestOptionFunc) (*GPGKey, *Response, error) { u := fmt.Sprintf("users/%d/gpg_keys", user) req, err := s.client.NewRequest(http.MethodPost, u, opt, options) if err != nil { return nil, nil, err } k := new(GPGKey) resp, err := s.client.Do(req, k) if err != nil { return nil, resp, err } return k, resp, nil } // DeleteGPGKeyForUser deletes a GPG key owned by a specified user. // // GitLab API docs: // https://docs.gitlab.com/ee/api/users.html#delete-a-gpg-key-for-a-given-user func (s *UsersService) DeleteGPGKeyForUser(user, key int, options ...RequestOptionFunc) (*Response, error) { u := fmt.Sprintf("users/%d/gpg_keys/%d", user, key) req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } // Email represents an Email. // // GitLab API docs: https://docs.gitlab.com/ee/api/users.html#list-emails type Email struct { ID int `json:"id"` Email string `json:"email"` ConfirmedAt *time.Time `json:"confirmed_at"` } // ListEmails gets a list of currently authenticated user's Emails. // // GitLab API docs: https://docs.gitlab.com/ee/api/users.html#list-emails func (s *UsersService) ListEmails(options ...RequestOptionFunc) ([]*Email, *Response, error) { req, err := s.client.NewRequest(http.MethodGet, "user/emails", nil, options) if err != nil { return nil, nil, err } var e []*Email resp, err := s.client.Do(req, &e) if err != nil { return nil, resp, err } return e, resp, nil } // ListEmailsForUserOptions represents the available ListEmailsForUser() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/users.html#list-emails-for-user type ListEmailsForUserOptions ListOptions // ListEmailsForUser gets a list of a specified user's Emails. Available // only for admin // // GitLab API docs: // https://docs.gitlab.com/ee/api/users.html#list-emails-for-user func (s *UsersService) ListEmailsForUser(user int, opt *ListEmailsForUserOptions, options ...RequestOptionFunc) ([]*Email, *Response, error) { u := fmt.Sprintf("users/%d/emails", user) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } var e []*Email resp, err := s.client.Do(req, &e) if err != nil { return nil, resp, err } return e, resp, nil } // GetEmail gets a single email. // // GitLab API docs: https://docs.gitlab.com/ee/api/users.html#single-email func (s *UsersService) GetEmail(email int, options ...RequestOptionFunc) (*Email, *Response, error) { u := fmt.Sprintf("user/emails/%d", email) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } e := new(Email) resp, err := s.client.Do(req, e) if err != nil { return nil, resp, err } return e, resp, nil } // AddEmailOptions represents the available AddEmail() options. // // GitLab API docs: https://docs.gitlab.com/ee/api/users.html#add-email type AddEmailOptions struct { Email *string `url:"email,omitempty" json:"email,omitempty"` SkipConfirmation *bool `url:"skip_confirmation,omitempty" json:"skip_confirmation,omitempty"` } // AddEmail creates a new email owned by the currently authenticated user. // // GitLab API docs: https://docs.gitlab.com/ee/api/users.html#add-email func (s *UsersService) AddEmail(opt *AddEmailOptions, options ...RequestOptionFunc) (*Email, *Response, error) { req, err := s.client.NewRequest(http.MethodPost, "user/emails", opt, options) if err != nil { return nil, nil, err } e := new(Email) resp, err := s.client.Do(req, e) if err != nil { return nil, resp, err } return e, resp, nil } // AddEmailForUser creates new email owned by specified user. Available only for // admin. // // GitLab API docs: https://docs.gitlab.com/ee/api/users.html#add-email-for-user func (s *UsersService) AddEmailForUser(user int, opt *AddEmailOptions, options ...RequestOptionFunc) (*Email, *Response, error) { u := fmt.Sprintf("users/%d/emails", user) req, err := s.client.NewRequest(http.MethodPost, u, opt, options) if err != nil { return nil, nil, err } e := new(Email) resp, err := s.client.Do(req, e) if err != nil { return nil, resp, err } return e, resp, nil } // DeleteEmail deletes email owned by currently authenticated user. This is an // idempotent function and calling it on a key that is already deleted or not // available results in 200 OK. // // GitLab API docs: // https://docs.gitlab.com/ee/api/users.html#delete-email-for-current-user func (s *UsersService) DeleteEmail(email int, options ...RequestOptionFunc) (*Response, error) { u := fmt.Sprintf("user/emails/%d", email) req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } // DeleteEmailForUser deletes email owned by a specified user. Available only // for admin. // // GitLab API docs: // https://docs.gitlab.com/ee/api/users.html#delete-email-for-given-user func (s *UsersService) DeleteEmailForUser(user, email int, options ...RequestOptionFunc) (*Response, error) { u := fmt.Sprintf("users/%d/emails/%d", user, email) req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } // BlockUser blocks the specified user. Available only for admin. // // GitLab API docs: https://docs.gitlab.com/ee/api/users.html#block-user func (s *UsersService) BlockUser(user int, options ...RequestOptionFunc) error { u := fmt.Sprintf("users/%d/block", user) req, err := s.client.NewRequest(http.MethodPost, u, nil, options) if err != nil { return err } resp, err := s.client.Do(req, nil) if err != nil && resp == nil { return err } switch resp.StatusCode { case 201: return nil case 403: return ErrUserBlockPrevented case 404: return ErrUserNotFound default: return fmt.Errorf("Received unexpected result code: %d", resp.StatusCode) } } // UnblockUser unblocks the specified user. Available only for admin. // // GitLab API docs: https://docs.gitlab.com/ee/api/users.html#unblock-user func (s *UsersService) UnblockUser(user int, options ...RequestOptionFunc) error { u := fmt.Sprintf("users/%d/unblock", user) req, err := s.client.NewRequest(http.MethodPost, u, nil, options) if err != nil { return err } resp, err := s.client.Do(req, nil) if err != nil && resp == nil { return err } switch resp.StatusCode { case 201: return nil case 403: return ErrUserUnblockPrevented case 404: return ErrUserNotFound default: return fmt.Errorf("Received unexpected result code: %d", resp.StatusCode) } } // BanUser bans the specified user. Available only for admin. // // GitLab API docs: https://docs.gitlab.com/ee/api/users.html#ban-user func (s *UsersService) BanUser(user int, options ...RequestOptionFunc) error { u := fmt.Sprintf("users/%d/ban", user) req, err := s.client.NewRequest(http.MethodPost, u, nil, options) if err != nil { return err } resp, err := s.client.Do(req, nil) if err != nil && resp == nil { return err } switch resp.StatusCode { case 201: return nil case 404: return ErrUserNotFound default: return fmt.Errorf("Received unexpected result code: %d", resp.StatusCode) } } // UnbanUser unbans the specified user. Available only for admin. // // GitLab API docs: https://docs.gitlab.com/ee/api/users.html#unban-user func (s *UsersService) UnbanUser(user int, options ...RequestOptionFunc) error { u := fmt.Sprintf("users/%d/unban", user) req, err := s.client.NewRequest(http.MethodPost, u, nil, options) if err != nil { return err } resp, err := s.client.Do(req, nil) if err != nil && resp == nil { return err } switch resp.StatusCode { case 201: return nil case 404: return ErrUserNotFound default: return fmt.Errorf("Received unexpected result code: %d", resp.StatusCode) } } // DeactivateUser deactivate the specified user. Available only for admin. // // GitLab API docs: https://docs.gitlab.com/ee/api/users.html#deactivate-user func (s *UsersService) DeactivateUser(user int, options ...RequestOptionFunc) error { u := fmt.Sprintf("users/%d/deactivate", user) req, err := s.client.NewRequest(http.MethodPost, u, nil, options) if err != nil { return err } resp, err := s.client.Do(req, nil) if err != nil && resp == nil { return err } switch resp.StatusCode { case 201: return nil case 403: return ErrUserDeactivatePrevented case 404: return ErrUserNotFound default: return fmt.Errorf("Received unexpected result code: %d", resp.StatusCode) } } // ActivateUser activate the specified user. Available only for admin. // // GitLab API docs: https://docs.gitlab.com/ee/api/users.html#activate-user func (s *UsersService) ActivateUser(user int, options ...RequestOptionFunc) error { u := fmt.Sprintf("users/%d/activate", user) req, err := s.client.NewRequest(http.MethodPost, u, nil, options) if err != nil { return err } resp, err := s.client.Do(req, nil) if err != nil && resp == nil { return err } switch resp.StatusCode { case 201: return nil case 403: return ErrUserActivatePrevented case 404: return ErrUserNotFound default: return fmt.Errorf("Received unexpected result code: %d", resp.StatusCode) } } // ApproveUser approve the specified user. Available only for admin. // // GitLab API docs: https://docs.gitlab.com/ee/api/users.html#approve-user func (s *UsersService) ApproveUser(user int, options ...RequestOptionFunc) error { u := fmt.Sprintf("users/%d/approve", user) req, err := s.client.NewRequest(http.MethodPost, u, nil, options) if err != nil { return err } resp, err := s.client.Do(req, nil) if err != nil && resp == nil { return err } switch resp.StatusCode { case 201: return nil case 403: return ErrUserApprovePrevented case 404: return ErrUserNotFound default: return fmt.Errorf("Received unexpected result code: %d", resp.StatusCode) } } // RejectUser reject the specified user. Available only for admin. // // GitLab API docs: https://docs.gitlab.com/ee/api/users.html#reject-user func (s *UsersService) RejectUser(user int, options ...RequestOptionFunc) error { u := fmt.Sprintf("users/%d/reject", user) req, err := s.client.NewRequest(http.MethodPost, u, nil, options) if err != nil { return err } resp, err := s.client.Do(req, nil) if err != nil && resp == nil { return err } switch resp.StatusCode { case 200: return nil case 403: return ErrUserRejectPrevented case 404: return ErrUserNotFound case 409: return ErrUserConflict default: return fmt.Errorf("Received unexpected result code: %d", resp.StatusCode) } } // ImpersonationToken represents an impersonation token. // // GitLab API docs: // https://docs.gitlab.com/ee/api/users.html#get-all-impersonation-tokens-of-a-user type ImpersonationToken struct { ID int `json:"id"` Name string `json:"name"` Active bool `json:"active"` Token string `json:"token"` Scopes []string `json:"scopes"` Revoked bool `json:"revoked"` CreatedAt *time.Time `json:"created_at"` ExpiresAt *ISOTime `json:"expires_at"` LastUsedAt *time.Time `json:"last_used_at"` } // GetAllImpersonationTokensOptions represents the available // GetAllImpersonationTokens() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/users.html#get-all-impersonation-tokens-of-a-user type GetAllImpersonationTokensOptions struct { ListOptions State *string `url:"state,omitempty" json:"state,omitempty"` } // GetAllImpersonationTokens retrieves all impersonation tokens of a user. // // GitLab API docs: // https://docs.gitlab.com/ee/api/users.html#get-all-impersonation-tokens-of-a-user func (s *UsersService) GetAllImpersonationTokens(user int, opt *GetAllImpersonationTokensOptions, options ...RequestOptionFunc) ([]*ImpersonationToken, *Response, error) { u := fmt.Sprintf("users/%d/impersonation_tokens", user) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } var ts []*ImpersonationToken resp, err := s.client.Do(req, &ts) if err != nil { return nil, resp, err } return ts, resp, nil } // GetImpersonationToken retrieves an impersonation token of a user. // // GitLab API docs: // https://docs.gitlab.com/ee/api/users.html#get-an-impersonation-token-of-a-user func (s *UsersService) GetImpersonationToken(user, token int, options ...RequestOptionFunc) (*ImpersonationToken, *Response, error) { u := fmt.Sprintf("users/%d/impersonation_tokens/%d", user, token) req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { return nil, nil, err } t := new(ImpersonationToken) resp, err := s.client.Do(req, &t) if err != nil { return nil, resp, err } return t, resp, nil } // CreateImpersonationTokenOptions represents the available // CreateImpersonationToken() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/users.html#create-an-impersonation-token type CreateImpersonationTokenOptions struct { Name *string `url:"name,omitempty" json:"name,omitempty"` Scopes *[]string `url:"scopes,omitempty" json:"scopes,omitempty"` ExpiresAt *time.Time `url:"expires_at,omitempty" json:"expires_at,omitempty"` } // CreateImpersonationToken creates an impersonation token. // // GitLab API docs: // https://docs.gitlab.com/ee/api/users.html#create-an-impersonation-token func (s *UsersService) CreateImpersonationToken(user int, opt *CreateImpersonationTokenOptions, options ...RequestOptionFunc) (*ImpersonationToken, *Response, error) { u := fmt.Sprintf("users/%d/impersonation_tokens", user) req, err := s.client.NewRequest(http.MethodPost, u, opt, options) if err != nil { return nil, nil, err } t := new(ImpersonationToken) resp, err := s.client.Do(req, &t) if err != nil { return nil, resp, err } return t, resp, nil } // RevokeImpersonationToken revokes an impersonation token. // // GitLab API docs: // https://docs.gitlab.com/ee/api/users.html#revoke-an-impersonation-token func (s *UsersService) RevokeImpersonationToken(user, token int, options ...RequestOptionFunc) (*Response, error) { u := fmt.Sprintf("users/%d/impersonation_tokens/%d", user, token) req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } // CreatePersonalAccessTokenOptions represents the available // CreatePersonalAccessToken() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/users.html#create-a-personal-access-token type CreatePersonalAccessTokenOptions struct { Name *string `url:"name,omitempty" json:"name,omitempty"` ExpiresAt *ISOTime `url:"expires_at,omitempty" json:"expires_at,omitempty"` Scopes *[]string `url:"scopes,omitempty" json:"scopes,omitempty"` } // CreatePersonalAccessToken creates a personal access token. // // GitLab API docs: // https://docs.gitlab.com/ee/api/users.html#create-a-personal-access-token func (s *UsersService) CreatePersonalAccessToken(user int, opt *CreatePersonalAccessTokenOptions, options ...RequestOptionFunc) (*PersonalAccessToken, *Response, error) { u := fmt.Sprintf("users/%d/personal_access_tokens", user) req, err := s.client.NewRequest(http.MethodPost, u, opt, options) if err != nil { return nil, nil, err } t := new(PersonalAccessToken) resp, err := s.client.Do(req, &t) if err != nil { return nil, resp, err } return t, resp, nil } // CreatePersonalAccessTokenForCurrentUserOptions represents the available // CreatePersonalAccessTokenForCurrentUser() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/users.html#create-a-personal-access-token-with-limited-scopes-for-the-currently-authenticated-user type CreatePersonalAccessTokenForCurrentUserOptions struct { Name *string `url:"name,omitempty" json:"name,omitempty"` Scopes *[]string `url:"scopes,omitempty" json:"scopes,omitempty"` ExpiresAt *ISOTime `url:"expires_at,omitempty" json:"expires_at,omitempty"` } // CreatePersonalAccessTokenForCurrentUser creates a personal access token with limited scopes for the currently authenticated user. // // GitLab API docs: // https://docs.gitlab.com/ee/api/users.html#create-a-personal-access-token-with-limited-scopes-for-the-currently-authenticated-user func (s *UsersService) CreatePersonalAccessTokenForCurrentUser(opt *CreatePersonalAccessTokenForCurrentUserOptions, options ...RequestOptionFunc) (*PersonalAccessToken, *Response, error) { u := "user/personal_access_tokens" req, err := s.client.NewRequest(http.MethodPost, u, opt, options) if err != nil { return nil, nil, err } t := new(PersonalAccessToken) resp, err := s.client.Do(req, &t) if err != nil { return nil, resp, err } return t, resp, nil } // UserActivity represents an entry in the user/activities response // // GitLab API docs: // https://docs.gitlab.com/ee/api/users.html#get-user-activities type UserActivity struct { Username string `json:"username"` LastActivityOn *ISOTime `json:"last_activity_on"` } // GetUserActivitiesOptions represents the options for GetUserActivities // // GitLap API docs: // https://docs.gitlab.com/ee/api/users.html#get-user-activities type GetUserActivitiesOptions struct { ListOptions From *ISOTime `url:"from,omitempty" json:"from,omitempty"` } // GetUserActivities retrieves user activities (admin only) // // GitLab API docs: // https://docs.gitlab.com/ee/api/users.html#get-user-activities func (s *UsersService) GetUserActivities(opt *GetUserActivitiesOptions, options ...RequestOptionFunc) ([]*UserActivity, *Response, error) { req, err := s.client.NewRequest(http.MethodGet, "user/activities", opt, options) if err != nil { return nil, nil, err } var t []*UserActivity resp, err := s.client.Do(req, &t) if err != nil { return nil, resp, err } return t, resp, nil } // UserMembership represents a membership of the user in a namespace or project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/users.html#user-memberships type UserMembership struct { SourceID int `json:"source_id"` SourceName string `json:"source_name"` SourceType string `json:"source_type"` AccessLevel AccessLevelValue `json:"access_level"` } // GetUserMembershipOptions represents the options available to query user memberships. // // GitLab API docs: // ohttps://docs.gitlab.com/ee/api/users.html#user-memberships type GetUserMembershipOptions struct { ListOptions Type *string `url:"type,omitempty" json:"type,omitempty"` } // GetUserMemberships retrieves a list of the user's memberships. // // GitLab API docs: // https://docs.gitlab.com/ee/api/users.html#user-memberships func (s *UsersService) GetUserMemberships(user int, opt *GetUserMembershipOptions, options ...RequestOptionFunc) ([]*UserMembership, *Response, error) { u := fmt.Sprintf("users/%d/memberships", user) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } var m []*UserMembership resp, err := s.client.Do(req, &m) if err != nil { return nil, resp, err } return m, resp, nil } // DisableTwoFactor disables two factor authentication for the specified user. // // GitLab API docs: // https://docs.gitlab.com/ee/api/users.html#disable-two-factor-authentication func (s *UsersService) DisableTwoFactor(user int, options ...RequestOptionFunc) error { u := fmt.Sprintf("users/%d/disable_two_factor", user) req, err := s.client.NewRequest(http.MethodPatch, u, nil, options) if err != nil { return err } resp, err := s.client.Do(req, nil) if err != nil && resp == nil { return err } switch resp.StatusCode { case 204: return nil case 400: return ErrUserTwoFactorNotEnabled case 403: return ErrUserDisableTwoFactorPrevented case 404: return ErrUserNotFound default: return fmt.Errorf("Received unexpected result code: %d", resp.StatusCode) } } // UserRunner represents a GitLab runner linked to the current user. // // GitLab API docs: // https://docs.gitlab.com/ee/api/users.html#create-a-runner type UserRunner struct { ID int `json:"id"` Token string `json:"token"` TokenExpiresAt *time.Time `json:"token_expires_at"` } // CreateUserRunnerOptions represents the available CreateUserRunner() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/users.html#create-a-runner type CreateUserRunnerOptions struct { RunnerType *string `url:"runner_type,omitempty" json:"runner_type,omitempty"` GroupID *int `url:"group_id,omitempty" json:"group_id,omitempty"` ProjectID *int `url:"project_id,omitempty" json:"project_id,omitempty"` Description *string `url:"description,omitempty" json:"description,omitempty"` Paused *bool `url:"paused,omitempty" json:"paused,omitempty"` Locked *bool `url:"locked,omitempty" json:"locked,omitempty"` RunUntagged *bool `url:"run_untagged,omitempty" json:"run_untagged,omitempty"` TagList *[]string `url:"tag_list,omitempty" json:"tag_list,omitempty"` AccessLevel *string `url:"access_level,omitempty" json:"access_level,omitempty"` MaximumTimeout *int `url:"maximum_timeout,omitempty" json:"maximum_timeout,omitempty"` MaintenanceNote *string `url:"maintenance_note,omitempty" json:"maintenance_note,omitempty"` } // CreateUserRunner creates a runner linked to the current user. // // GitLab API docs: // https://docs.gitlab.com/ee/api/users.html#create-a-runner func (s *UsersService) CreateUserRunner(opts *CreateUserRunnerOptions, options ...RequestOptionFunc) (*UserRunner, *Response, error) { req, err := s.client.NewRequest(http.MethodPost, "user/runners", opts, options) if err != nil { return nil, nil, err } r := new(UserRunner) resp, err := s.client.Do(req, r) if err != nil { return nil, resp, err } return r, resp, nil } // CreateServiceAccountUserOptions represents the available CreateServiceAccountUser() options. // // GitLab API docs: https://docs.gitlab.com/ee/api/user_service_accounts.html#create-a-service-account-user type CreateServiceAccountUserOptions struct { Name *string `url:"name,omitempty" json:"name,omitempty"` Username *string `url:"username,omitempty" json:"username,omitempty"` } // CreateServiceAccountUser creates a new service account user. // // GitLab API docs: // https://docs.gitlab.com/ee/api/users.html#create-service-account-user func (s *UsersService) CreateServiceAccountUser(opts *CreateServiceAccountUserOptions, options ...RequestOptionFunc) (*User, *Response, error) { req, err := s.client.NewRequest(http.MethodPost, "service_accounts", opts, options) if err != nil { return nil, nil, err } usr := new(User) resp, err := s.client.Do(req, usr) if err != nil { return nil, resp, err } return usr, resp, nil } // ListServiceAccounts lists all service accounts. // // GitLab API docs: // https://docs.gitlab.com/ee/api/users.html#create-service-account-user func (s *UsersService) ListServiceAccounts(opt *ListServiceAccountsOptions, options ...RequestOptionFunc) ([]*ServiceAccount, *Response, error) { req, err := s.client.NewRequest(http.MethodGet, "service_accounts", opt, options) if err != nil { return nil, nil, err } var sas []*ServiceAccount resp, err := s.client.Do(req, &sas) if err != nil { return nil, resp, err } return sas, resp, nil } // UploadAvatar uploads an avatar to the current user. // // GitLab API docs: // https://docs.gitlab.com/ee/api/users.html#upload-a-current-user-avatar func (s *UsersService) UploadAvatar(avatar io.Reader, filename string, options ...RequestOptionFunc) (*User, *Response, error) { u := "user/avatar" req, err := s.client.UploadRequest( http.MethodPut, u, avatar, filename, UploadAvatar, nil, options, ) if err != nil { return nil, nil, err } usr := new(User) resp, err := s.client.Do(req, usr) if err != nil { return nil, resp, err } return usr, resp, nil } golang-gitlab-gitlab-org-api-client-go-0.123.0/users_test.go000066400000000000000000000704601475761473200236150ustar00rootroot00000000000000// // Copyright 2021, Sander van Harmelen // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "bytes" "errors" "fmt" "net" "net/http" "reflect" "strings" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestGetUser(t *testing.T) { mux, client := setup(t) path := "/api/v4/users/1" mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) mustWriteHTTPResponse(t, w, "testdata/get_user.json") }) user, _, err := client.Users.GetUser(1, GetUsersOptions{}) require.NoError(t, err) want := &User{ ID: 1, Username: "john_smith", Name: "John Smith", State: "active", WebURL: "http://localhost:3000/john_smith", CreatedAt: Ptr(time.Date(2012, time.May, 23, 8, 0o0, 58, 0, time.UTC)), Bio: "Bio of John Smith", Location: "USA", PublicEmail: "john@example.com", Skype: "john_smith", Linkedin: "john_smith", Twitter: "john_smith", WebsiteURL: "john_smith.example.com", Organization: "Smith Inc", JobTitle: "Operations Specialist", AvatarURL: "http://localhost:3000/uploads/user/avatar/1/cd8.jpeg", } require.Equal(t, want, user) } func TestGetUserAdmin(t *testing.T) { mux, client := setup(t) path := "/api/v4/users/1" mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) mustWriteHTTPResponse(t, w, "testdata/get_user_admin.json") }) user, _, err := client.Users.GetUser(1, GetUsersOptions{}) require.NoError(t, err) lastActivityOn := ISOTime(time.Date(2012, time.May, 23, 0, 0, 0, 0, time.UTC)) currentSignInIP := net.ParseIP("8.8.8.8") lastSignInIP := net.ParseIP("2001:db8::68") want := &User{ ID: 1, Username: "john_smith", Email: "john@example.com", Name: "John Smith", State: "active", WebURL: "http://localhost:3000/john_smith", CreatedAt: Ptr(time.Date(2012, time.May, 23, 8, 0, 58, 0, time.UTC)), Bio: "Bio of John Smith", Location: "USA", PublicEmail: "john@example.com", Skype: "john_smith", Linkedin: "john_smith", Twitter: "john_smith", WebsiteURL: "john_smith.example.com", Organization: "Smith Inc", JobTitle: "Operations Specialist", ThemeID: 1, LastActivityOn: &lastActivityOn, ColorSchemeID: 2, IsAdmin: true, IsAuditor: true, AvatarURL: "http://localhost:3000/uploads/user/avatar/1/index.jpg", CanCreateGroup: true, CanCreateProject: true, ProjectsLimit: 100, CurrentSignInAt: Ptr(time.Date(2012, time.June, 2, 6, 36, 55, 0, time.UTC)), CurrentSignInIP: ¤tSignInIP, LastSignInAt: Ptr(time.Date(2012, time.June, 1, 11, 41, 1, 0, time.UTC)), LastSignInIP: &lastSignInIP, ConfirmedAt: Ptr(time.Date(2012, time.May, 23, 9, 0o5, 22, 0, time.UTC)), TwoFactorEnabled: true, Note: "DMCA Request: 2018-11-05 | DMCA Violation | Abuse | https://gitlab.zendesk.com/agent/tickets/123", Identities: []*UserIdentity{{Provider: "github", ExternUID: "2435223452345"}}, NamespaceID: 42, } require.Equal(t, want, user) } func TestCreatedBy(t *testing.T) { mux, client := setup(t) path := "/api/v4/users/2" mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) mustWriteHTTPResponse(t, w, "testdata/get_user_bot.json") }) user, _, err := client.Users.GetUser(2, GetUsersOptions{}) require.NoError(t, err) lastActivityOn := ISOTime(time.Date(2012, time.May, 23, 0, 0, 0, 0, time.UTC)) want := &User{ ID: 2, Username: "project_1_bot_3cca1d029554e372cf8f39df76bf507d", Email: "project_1_bot_3cca1d029554e372cf8f39df76bf507d@example.com", Name: "John Bot", State: "active", WebURL: "http://localhost:3000/project_1_bot_3cca1d029554e372cf8f39df76bf507d", CreatedAt: Ptr(time.Date(2012, time.May, 23, 8, 0o0, 58, 0, time.UTC)), Bot: true, // Bio: "Bio of John Smith", // Location: "USA", // PublicEmail: "john@example.com", // Skype: "john_smith", // Linkedin: "john_smith", // Twitter: "john_smith", // WebsiteURL: "john_smith.example.com", // Organization: "Smith Inc", // JobTitle: "Operations Specialist", ThemeID: 3, LastActivityOn: &lastActivityOn, ColorSchemeID: 1, IsAdmin: false, AvatarURL: "http://localhost:3000/uploads/user/avatar/2/index.jpg", ConfirmedAt: Ptr(time.Date(2012, time.May, 23, 8, 0o0, 58, 0, time.UTC)), Identities: []*UserIdentity{}, NamespaceID: 4, Locked: false, CreatedBy: &BasicUser{ ID: 1, Username: "john_smith", Name: "John Smith", State: "active", Locked: false, WebURL: "http://localhost:3000/john_smith", AvatarURL: "http://localhost:3000/uploads/user/avatar/1/cd8.jpeg", }, } require.Equal(t, want, user) } func TestBlockUser(t *testing.T) { mux, client := setup(t) path := fmt.Sprintf("/%susers/1/block", apiVersionPath) mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) w.WriteHeader(http.StatusCreated) }) err := client.Users.BlockUser(1) if err != nil { t.Errorf("Users.BlockUser returned error: %v", err) } } func TestBlockUser_UserNotFound(t *testing.T) { mux, client := setup(t) path := fmt.Sprintf("/%susers/1/block", apiVersionPath) mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) w.WriteHeader(http.StatusNotFound) }) err := client.Users.BlockUser(1) if !errors.Is(err, ErrUserNotFound) { t.Errorf("Users.BlockUser error.\nExpected: %+v\nGot: %+v", ErrUserNotFound, err) } } func TestBlockUser_BlockPrevented(t *testing.T) { mux, client := setup(t) path := fmt.Sprintf("/%susers/1/block", apiVersionPath) mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) w.WriteHeader(http.StatusForbidden) }) err := client.Users.BlockUser(1) if !errors.Is(err, ErrUserBlockPrevented) { t.Errorf("Users.BlockUser error.\nExpected: %+v\nGot: %+v", ErrUserBlockPrevented, err) } } func TestBlockUser_UnknownError(t *testing.T) { mux, client := setup(t) path := fmt.Sprintf("/%susers/1/block", apiVersionPath) mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) w.WriteHeader(http.StatusTeapot) }) want := fmt.Sprintf("Received unexpected result code: %d", http.StatusTeapot) err := client.Users.BlockUser(1) if err.Error() != want { t.Errorf("Users.BlockUser error.\nExpected: %s\nGot: %v", want, err) } } func TestUnblockUser(t *testing.T) { mux, client := setup(t) path := fmt.Sprintf("/%susers/1/unblock", apiVersionPath) mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) w.WriteHeader(http.StatusCreated) }) err := client.Users.UnblockUser(1) if err != nil { t.Errorf("Users.UnblockUser returned error: %v", err) } } func TestUnblockUser_UserNotFound(t *testing.T) { mux, client := setup(t) path := fmt.Sprintf("/%susers/1/unblock", apiVersionPath) mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) w.WriteHeader(http.StatusNotFound) }) err := client.Users.UnblockUser(1) if !errors.Is(err, ErrUserNotFound) { t.Errorf("Users.UnblockUser error.\nExpected: %v\nGot: %v", ErrUserNotFound, err) } } func TestUnblockUser_UnblockPrevented(t *testing.T) { mux, client := setup(t) path := fmt.Sprintf("/%susers/1/unblock", apiVersionPath) mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) w.WriteHeader(http.StatusForbidden) }) err := client.Users.UnblockUser(1) if !errors.Is(err, ErrUserUnblockPrevented) { t.Errorf("Users.UnblockUser error.\nExpected: %v\nGot: %v", ErrUserUnblockPrevented, err) } } func TestUnblockUser_UnknownError(t *testing.T) { mux, client := setup(t) path := fmt.Sprintf("/%susers/1/unblock", apiVersionPath) mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) w.WriteHeader(http.StatusTeapot) }) want := fmt.Sprintf("Received unexpected result code: %d", http.StatusTeapot) err := client.Users.UnblockUser(1) if err.Error() != want { t.Errorf("Users.UnblockUser error.\nExpected: %s\n\tGot: %v", want, err) } } func TestBanUser(t *testing.T) { mux, client := setup(t) path := fmt.Sprintf("/%susers/1/block", apiVersionPath) mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) w.WriteHeader(http.StatusCreated) }) err := client.Users.BlockUser(1) if err != nil { t.Errorf("Users.BlockUser returned error: %v", err) } } func TestBanUser_UserNotFound(t *testing.T) { mux, client := setup(t) path := fmt.Sprintf("/%susers/1/ban", apiVersionPath) mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) w.WriteHeader(http.StatusNotFound) }) err := client.Users.BanUser(1) if !errors.Is(err, ErrUserNotFound) { t.Errorf("Users.BanUser error.\nExpected: %+v\nGot: %+v", ErrUserNotFound, err) } } func TestBanUser_UnknownError(t *testing.T) { mux, client := setup(t) path := fmt.Sprintf("/%susers/1/ban", apiVersionPath) mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) w.WriteHeader(http.StatusTeapot) }) want := fmt.Sprintf("Received unexpected result code: %d", http.StatusTeapot) err := client.Users.BanUser(1) if err.Error() != want { t.Errorf("Users.BanUSer error.\nExpected: %s\nGot: %v", want, err) } } func TestUnbanUser(t *testing.T) { mux, client := setup(t) path := fmt.Sprintf("/%susers/1/unban", apiVersionPath) mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) w.WriteHeader(http.StatusCreated) }) err := client.Users.UnbanUser(1) if err != nil { t.Errorf("Users.UnbanUser returned error: %v", err) } } func TestUnbanUser_UserNotFound(t *testing.T) { mux, client := setup(t) path := fmt.Sprintf("/%susers/1/unban", apiVersionPath) mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) w.WriteHeader(http.StatusNotFound) }) err := client.Users.UnbanUser(1) if !errors.Is(err, ErrUserNotFound) { t.Errorf("Users.UnbanUser error.\nExpected: %v\nGot: %v", ErrUserNotFound, err) } } func TestUnbanUser_UnknownError(t *testing.T) { mux, client := setup(t) path := fmt.Sprintf("/%susers/1/unban", apiVersionPath) mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) w.WriteHeader(http.StatusTeapot) }) want := fmt.Sprintf("Received unexpected result code: %d", http.StatusTeapot) err := client.Users.UnbanUser(1) if err.Error() != want { t.Errorf("Users.UnbanUser error.\nExpected: %s\n\tGot: %v", want, err) } } func TestDeactivateUser(t *testing.T) { mux, client := setup(t) path := fmt.Sprintf("/%susers/1/deactivate", apiVersionPath) mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) w.WriteHeader(http.StatusCreated) }) err := client.Users.DeactivateUser(1) if err != nil { t.Errorf("Users.DeactivateUser returned error: %v", err) } } func TestDeactivateUser_UserNotFound(t *testing.T) { mux, client := setup(t) path := fmt.Sprintf("/%susers/1/deactivate", apiVersionPath) mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) w.WriteHeader(http.StatusNotFound) }) err := client.Users.DeactivateUser(1) if !errors.Is(err, ErrUserNotFound) { t.Errorf("Users.DeactivateUser error.\nExpected: %+v\n\tGot: %+v", ErrUserNotFound, err) } } func TestDeactivateUser_DeactivatePrevented(t *testing.T) { mux, client := setup(t) path := fmt.Sprintf("/%susers/1/deactivate", apiVersionPath) mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) w.WriteHeader(http.StatusForbidden) }) err := client.Users.DeactivateUser(1) if !errors.Is(err, ErrUserDeactivatePrevented) { t.Errorf("Users.DeactivateUser error.\nExpected: %+v\n\tGot: %+v", ErrUserDeactivatePrevented, err) } } func TestActivateUser(t *testing.T) { mux, client := setup(t) path := fmt.Sprintf("/%susers/1/activate", apiVersionPath) mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) w.WriteHeader(http.StatusCreated) }) err := client.Users.ActivateUser(1) if err != nil { t.Errorf("Users.ActivateUser returned error: %v", err) } } func TestActivateUser_ActivatePrevented(t *testing.T) { mux, client := setup(t) path := fmt.Sprintf("/%susers/1/activate", apiVersionPath) mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) w.WriteHeader(http.StatusForbidden) }) err := client.Users.ActivateUser(1) if !errors.Is(err, ErrUserActivatePrevented) { t.Errorf("Users.ActivateUser error.\nExpected: %+v\n\tGot: %+v", ErrUserActivatePrevented, err) } } func TestActivateUser_UserNotFound(t *testing.T) { mux, client := setup(t) path := fmt.Sprintf("/%susers/1/activate", apiVersionPath) mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) w.WriteHeader(http.StatusNotFound) }) err := client.Users.ActivateUser(1) if !errors.Is(err, ErrUserNotFound) { t.Errorf("Users.ActivateUser error.\nExpected: %+v\n\tGot: %+v", ErrUserNotFound, err) } } func TestApproveUser(t *testing.T) { mux, client := setup(t) path := fmt.Sprintf("/%susers/1/approve", apiVersionPath) mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) w.WriteHeader(http.StatusCreated) }) err := client.Users.ApproveUser(1) if err != nil { t.Errorf("Users.ApproveUser returned error: %v", err) } } func TestApproveUser_UserNotFound(t *testing.T) { mux, client := setup(t) path := fmt.Sprintf("/%susers/1/approve", apiVersionPath) mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) w.WriteHeader(http.StatusNotFound) }) err := client.Users.ApproveUser(1) if !errors.Is(err, ErrUserNotFound) { t.Errorf("Users.ApproveUser error.\nExpected: %v\nGot: %v", ErrUserNotFound, err) } } func TestApproveUser_ApprovePrevented(t *testing.T) { mux, client := setup(t) path := fmt.Sprintf("/%susers/1/approve", apiVersionPath) mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) w.WriteHeader(http.StatusForbidden) }) err := client.Users.ApproveUser(1) if !errors.Is(err, ErrUserApprovePrevented) { t.Errorf("Users.ApproveUser error.\nExpected: %v\nGot: %v", ErrUserApprovePrevented, err) } } func TestApproveUser_UnknownError(t *testing.T) { mux, client := setup(t) path := fmt.Sprintf("/%susers/1/approve", apiVersionPath) mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) w.WriteHeader(http.StatusTeapot) }) want := fmt.Sprintf("Received unexpected result code: %d", http.StatusTeapot) err := client.Users.ApproveUser(1) if err.Error() != want { t.Errorf("Users.ApproveUser error.\nExpected: %s\n\tGot: %v", want, err) } } func TestRejectUser(t *testing.T) { mux, client := setup(t) path := fmt.Sprintf("/%susers/1/reject", apiVersionPath) mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) w.WriteHeader(http.StatusOK) }) err := client.Users.RejectUser(1) if err != nil { t.Errorf("Users.RejectUser returned error: %v", err) } } func TestRejectUser_UserNotFound(t *testing.T) { mux, client := setup(t) path := fmt.Sprintf("/%susers/1/reject", apiVersionPath) mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) w.WriteHeader(http.StatusNotFound) }) err := client.Users.RejectUser(1) if !errors.Is(err, ErrUserNotFound) { t.Errorf("Users.RejectUser error.\nExpected: %v\nGot: %v", ErrUserNotFound, err) } } func TestRejectUser_RejectPrevented(t *testing.T) { mux, client := setup(t) path := fmt.Sprintf("/%susers/1/reject", apiVersionPath) mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) w.WriteHeader(http.StatusForbidden) }) err := client.Users.RejectUser(1) if !errors.Is(err, ErrUserRejectPrevented) { t.Errorf("Users.RejectUser error.\nExpected: %v\nGot: %v", ErrUserRejectPrevented, err) } } func TestRejectUser_Conflict(t *testing.T) { mux, client := setup(t) path := fmt.Sprintf("/%susers/1/reject", apiVersionPath) mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) w.WriteHeader(http.StatusConflict) }) err := client.Users.RejectUser(1) if !errors.Is(err, ErrUserConflict) { t.Errorf("Users.RejectUser error.\nExpected: %v\nGot: %v", ErrUserConflict, err) } } func TestRejectUser_UnknownError(t *testing.T) { mux, client := setup(t) path := fmt.Sprintf("/%susers/1/reject", apiVersionPath) mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) w.WriteHeader(http.StatusTeapot) }) want := fmt.Sprintf("Received unexpected result code: %d", http.StatusTeapot) err := client.Users.RejectUser(1) if err.Error() != want { t.Errorf("Users.RejectUser error.\nExpected: %s\n\tGot: %v", want, err) } } func TestGetMemberships(t *testing.T) { mux, client := setup(t) path := fmt.Sprintf("/%susers/1/memberships", apiVersionPath) mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) mustWriteHTTPResponse(t, w, "testdata/get_user_memberships.json") }) opt := new(GetUserMembershipOptions) memberships, _, err := client.Users.GetUserMemberships(1, opt) require.NoError(t, err) want := []*UserMembership{{SourceID: 1, SourceName: "Project one", SourceType: "Project", AccessLevel: 20}, {SourceID: 3, SourceName: "Group three", SourceType: "Namespace", AccessLevel: 20}} assert.Equal(t, want, memberships) } func TestGetUserAssociationsCount(t *testing.T) { mux, client := setup(t) path := "/api/v4/users/1/associations_count" mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) mustWriteHTTPResponse(t, w, "testdata/get_user_associations_count.json") }) userAssociationsCount, _, err := client.Users.GetUserAssociationsCount(1) require.NoError(t, err) want := &UserAssociationsCount{ GroupsCount: 1, ProjectsCount: 2, IssuesCount: 3, MergeRequestsCount: 4, } require.Equal(t, want, userAssociationsCount) } func TestGetSingleSSHKeyForUser(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/users/1/keys/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, ` { "id": 1, "title": "Public key", "key": "ssh-rsa AAAA...", "created_at": "2014-08-01T14:47:39.080Z", "usage_type": "auth" }`) }) sshKey, _, err := client.Users.GetSSHKeyForUser(1, 1) if err != nil { t.Errorf("Users.GetSSHKeyForUser returned an error: %v", err) } wantCreatedAt := time.Date(2014, 8, 1, 14, 47, 39, 80000000, time.UTC) want := &SSHKey{ ID: 1, Title: "Public key", Key: "ssh-rsa AAAA...", UsageType: "auth", CreatedAt: &wantCreatedAt, } if !reflect.DeepEqual(want, sshKey) { t.Errorf("Users.GetSSHKeyForUser returned %+v, want %+v", sshKey, want) } } func TestDisableUser2FA(t *testing.T) { mux, client := setup(t) path := fmt.Sprintf("/%susers/1/disable_two_factor", apiVersionPath) mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPatch) w.WriteHeader(http.StatusNoContent) }) err := client.Users.DisableTwoFactor(1) if err != nil { t.Errorf("Users.DisableTwoFactor returned error: %v", err) } } func TestCreateUserRunner(t *testing.T) { mux, client := setup(t) path := fmt.Sprintf("/%suser/runners", apiVersionPath) mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) w.WriteHeader(http.StatusCreated) w.Write([]byte(` { "id": 1234, "token": "glrt-1234567890ABCD", "token_expires_at":null }`)) }) createRunnerOpts := &CreateUserRunnerOptions{ ProjectID: Ptr(1), RunnerType: Ptr("project_type"), } response, _, err := client.Users.CreateUserRunner(createRunnerOpts) if err != nil { t.Errorf("Users.CreateUserRunner returned an error: %v", err) } require.Equal(t, 1234, response.ID) require.Equal(t, "glrt-1234567890ABCD", response.Token) require.Equal(t, (*time.Time)(nil), response.TokenExpiresAt) } func TestCreatePersonalAccessTokenForCurrentUser(t *testing.T) { mux, client := setup(t) path := "/api/v4/user/personal_access_tokens" mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) mustWriteHTTPResponse(t, w, "testdata/post_user_personal_access_tokens.json") }) scopes := []string{"k8s_proxy"} expiresAt := ISOTime(time.Date(2020, time.October, 15, 0, 0, 0, 0, time.UTC)) user, _, err := client.Users.CreatePersonalAccessTokenForCurrentUser(&CreatePersonalAccessTokenForCurrentUserOptions{ Name: String("mytoken"), Scopes: &scopes, ExpiresAt: &expiresAt, }) require.NoError(t, err) createdAt := time.Date(2020, time.October, 14, 11, 58, 53, 526000000, time.UTC) want := &PersonalAccessToken{ ID: 3, Name: "mytoken", Revoked: false, CreatedAt: &createdAt, Scopes: scopes, UserID: 42, Active: true, ExpiresAt: &expiresAt, Token: "glpat-aaaaaaaa-bbbbbbbbb", } require.Equal(t, want, user) } func TestCreateServiceAccountUser(t *testing.T) { mux, client := setup(t) path := "/api/v4/service_accounts" mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) if !strings.Contains(r.Header.Get("Content-Type"), "application/json") { t.Fatalf("Users.CreateServiceAccountUser request content-type %+v want application/json;", r.Header.Get("Content-Type")) } if r.ContentLength == -1 { t.Fatalf("Users.CreateServiceAccountUser request content-length is -1") } mustWriteHTTPResponse(t, w, "testdata/create_service_account_user.json") }) user, _, err := client.Users.CreateServiceAccountUser(&CreateServiceAccountUserOptions{ Name: Ptr("Test Service Account"), Username: Ptr("serviceaccount"), }) require.NoError(t, err) want := &User{ ID: 999, Username: "serviceaccount", Name: "Test Service Account", State: "active", Locked: false, AvatarURL: "http://localhost:3000/uploads/user/avatar/999/cd8.jpeg", WebURL: "http://localhost:3000/serviceaccount", } require.Equal(t, want, user) } func TestCreateUser(t *testing.T) { mux, client := setup(t) path := "/api/v4/users" mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) if !strings.Contains(r.Header.Get("Content-Type"), "application/json") { t.Fatalf("Users.CreateUser request content-type %+v want application/json;", r.Header.Get("Content-Type")) } if r.ContentLength == -1 { t.Fatalf("Users.CreateUser request content-length is -1") } w.WriteHeader(http.StatusCreated) w.Write([]byte(` { "email": "user999@example.com", "id": 999, "name":"Firstname Lastname", "username":"user" }`)) }) user, _, err := client.Users.CreateUser(&CreateUserOptions{ Email: Ptr("user999@example.com"), Name: Ptr("Firstname Lastname"), Username: Ptr("user"), }) require.NoError(t, err) want := &User{ Email: "user999@example.com", ID: 999, Name: "Firstname Lastname", Username: "user", } require.Equal(t, want, user) } func TestCreateUserAvatar(t *testing.T) { mux, client := setup(t) path := "/api/v4/users" mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) if !strings.Contains(r.Header.Get("Content-Type"), "multipart/form-data") { t.Fatalf("Users.CreateUser request content-type %+v want multipart/form-data;", r.Header.Get("Content-Type")) } if r.ContentLength == -1 { t.Fatalf("Users.CreateUser request content-length is -1") } w.WriteHeader(http.StatusCreated) w.Write([]byte(` { "avatar_url":"http://localhost:3000/uploads/-/system/user/avatar/999/avatar.png", "email": "user999@example.com", "id": 999, "name":"Firstname Lastname", "username":"user" }`)) }) avatar := new(bytes.Buffer) userAvatar := &UserAvatar{ Image: avatar, Filename: "avatar.png", } user, _, err := client.Users.CreateUser(&CreateUserOptions{ Avatar: userAvatar, Email: Ptr("user999@example.com"), Name: Ptr("Firstname Lastname"), Username: Ptr("user"), }) require.NoError(t, err) want := &User{ AvatarURL: "http://localhost:3000/uploads/-/system/user/avatar/999/avatar.png", Email: "user999@example.com", ID: 999, Name: "Firstname Lastname", Username: "user", } require.Equal(t, want, user) } func TestModifyUser(t *testing.T) { mux, client := setup(t) path := "/api/v4/users/1" mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPut) if !strings.Contains(r.Header.Get("Content-Type"), "application/json") { t.Fatalf("Users.ModifyUser request content-type %+v want application/json;", r.Header.Get("Content-Type")) } if r.ContentLength == -1 { t.Fatalf("Users.ModifyUser request content-length is -1") } fmt.Fprint(w, `{}`) }) _, _, err := client.Users.ModifyUser(1, &ModifyUserOptions{}) require.NoError(t, err) } func TestModifyUserAvatar(t *testing.T) { mux, client := setup(t) path := "/api/v4/users/1" mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPut) if !strings.Contains(r.Header.Get("Content-Type"), "multipart/form-data;") { t.Fatalf("Users.ModifyUser request content-type %+v want multipart/form-data;", r.Header.Get("Content-Type")) } if r.ContentLength == -1 { t.Fatalf("Users.ModifyUser request content-length is -1") } fmt.Fprint(w, `{}`) }) avatar := new(bytes.Buffer) userAvatar := &UserAvatar{ Image: avatar, Filename: "avatar.png", } _, _, err := client.Users.ModifyUser(1, &ModifyUserOptions{Avatar: userAvatar}) require.NoError(t, err) } func TestUploadAvatarUser(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/user/avatar", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPut) if !strings.Contains(r.Header.Get("Content-Type"), "multipart/form-data;") { t.Fatalf("Users.UploadAvatar request content-type %+v want multipart/form-data;", r.Header.Get("Content-Type")) } if r.ContentLength == -1 { t.Fatalf("Users.UploadAvatar request content-length is -1") } fmt.Fprint(w, `{}`) }) avatar := new(bytes.Buffer) _, _, err := client.Users.UploadAvatar(avatar, "avatar.png") if err != nil { t.Fatalf("Users.UploadAvatar returns an error: %v", err) } } func TestListServiceAccounts(t *testing.T) { mux, client := setup(t) path := "/api/v4/service_accounts" mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) mustWriteHTTPResponse(t, w, "testdata/get_serviceaccounts.json") }) serviceaccounts, _, err := client.Users.ListServiceAccounts(&ListServiceAccountsOptions{}) require.NoError(t, err) want := []*ServiceAccount{ { ID: 114, Username: "service_account_33", Name: "Service account user", }, { ID: 137, Username: "service_account_34", Name: "john doe", }, } require.Equal(t, want, serviceaccounts) } golang-gitlab-gitlab-org-api-client-go-0.123.0/validate.go000066400000000000000000000133131475761473200232000ustar00rootroot00000000000000// // Copyright 2021, Sander van Harmelen // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "fmt" "net/http" ) // ValidateService handles communication with the validation related methods of // the GitLab API. // // GitLab API docs: https://docs.gitlab.com/ee/api/lint.html type ValidateService struct { client *Client } // LintResult represents the linting results. // // GitLab API docs: https://docs.gitlab.com/ee/api/lint.html type LintResult struct { Status string `json:"status"` Errors []string `json:"errors"` Warnings []string `json:"warnings"` MergedYaml string `json:"merged_yaml"` } // ProjectLintResult represents the linting results by project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/lint.html#validate-a-projects-ci-configuration type ProjectLintResult struct { Valid bool `json:"valid"` Errors []string `json:"errors"` Warnings []string `json:"warnings"` MergedYaml string `json:"merged_yaml"` Includes []Include `json:"includes"` } // Include contains the details about an include block in the .gitlab-ci.yml file. // It is used in ProjectLintResult. // // Reference can be found at the lint API endpoint in the openapi yaml: // https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/api/openapi/openapi_v2.yaml type Include struct { Type string `json:"type"` Location string `json:"location"` Blob string `json:"blob"` Raw string `json:"raw"` Extra map[string]interface{} `json:"extra"` ContextProject string `json:"context_project"` ContextSHA string `json:"context_sha"` } // LintOptions represents the available Lint() options. // // Gitlab API docs: // https://docs.gitlab.com/ee/api/lint.html#validate-the-ci-yaml-configuration type LintOptions struct { Content string `url:"content,omitempty" json:"content,omitempty"` IncludeMergedYAML bool `url:"include_merged_yaml,omitempty" json:"include_merged_yaml,omitempty"` IncludeJobs bool `url:"include_jobs,omitempty" json:"include_jobs,omitempty"` } // Lint validates .gitlab-ci.yml content. // Deprecated: This endpoint was removed in GitLab 16.0. // // Gitlab API docs: // https://docs.gitlab.com/ee/api/lint.html#validate-the-ci-yaml-configuration-deprecated func (s *ValidateService) Lint(opts *LintOptions, options ...RequestOptionFunc) (*LintResult, *Response, error) { req, err := s.client.NewRequest(http.MethodPost, "ci/lint", &opts, options) if err != nil { return nil, nil, err } l := new(LintResult) resp, err := s.client.Do(req, l) if err != nil { return nil, resp, err } return l, resp, nil } // ProjectNamespaceLintOptions represents the available ProjectNamespaceLint() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/lint.html#validate-a-ci-yaml-configuration-with-a-namespace type ProjectNamespaceLintOptions struct { Content *string `url:"content,omitempty" json:"content,omitempty"` DryRun *bool `url:"dry_run,omitempty" json:"dry_run,omitempty"` IncludeJobs *bool `url:"include_jobs,omitempty" json:"include_jobs,omitempty"` Ref *string `url:"ref,omitempty" json:"ref,omitempty"` } // ProjectNamespaceLint validates .gitlab-ci.yml content by project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/lint.html#validate-a-ci-yaml-configuration-with-a-namespace func (s *ValidateService) ProjectNamespaceLint(pid interface{}, opt *ProjectNamespaceLintOptions, options ...RequestOptionFunc) (*ProjectLintResult, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/ci/lint", PathEscape(project)) req, err := s.client.NewRequest(http.MethodPost, u, &opt, options) if err != nil { return nil, nil, err } l := new(ProjectLintResult) resp, err := s.client.Do(req, l) if err != nil { return nil, resp, err } return l, resp, nil } // ProjectLintOptions represents the available ProjectLint() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/lint.html#validate-a-projects-ci-configuration type ProjectLintOptions struct { ContentRef *string `url:"content_ref,omitempty" json:"content_ref,omitempty"` DryRunRef *string `url:"dry_run_ref,omitempty" json:"dry_run_ref,omitempty"` DryRun *bool `url:"dry_run,omitempty" json:"dry_run,omitempty"` IncludeJobs *bool `url:"include_jobs,omitempty" json:"include_jobs,omitempty"` Ref *string `url:"ref,omitempty" json:"ref,omitempty"` } // ProjectLint validates .gitlab-ci.yml content by project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/lint.html#validate-a-projects-ci-configuration func (s *ValidateService) ProjectLint(pid interface{}, opt *ProjectLintOptions, options ...RequestOptionFunc) (*ProjectLintResult, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/ci/lint", PathEscape(project)) req, err := s.client.NewRequest(http.MethodGet, u, &opt, options) if err != nil { return nil, nil, err } l := new(ProjectLintResult) resp, err := s.client.Do(req, l) if err != nil { return nil, resp, err } return l, resp, nil } golang-gitlab-gitlab-org-api-client-go-0.123.0/validate_test.go000066400000000000000000000216001475761473200242350ustar00rootroot00000000000000// // Copyright 2021, Sander van Harmelen // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "fmt" "net/http" "reflect" "testing" ) func TestValidate(t *testing.T) { testCases := []struct { description string opts *LintOptions response string want *LintResult }{ { description: "valid", opts: &LintOptions{ Content: ` build1: stage: build script: - echo "Do your build here"`, IncludeMergedYAML: true, IncludeJobs: false, }, response: `{ "status": "valid", "errors": [], "merged_yaml":"---\nbuild1:\n stage: build\n script:\n - echo\"Do your build here\"" }`, want: &LintResult{ Status: "valid", MergedYaml: "---\nbuild1:\n stage: build\n script:\n - echo\"Do your build here\"", Errors: []string{}, }, }, { description: "invalid", opts: &LintOptions{ Content: ` build1: - echo "Do your build here"`, }, response: `{ "status": "invalid", "errors": ["error message when content is invalid"] }`, want: &LintResult{ Status: "invalid", Errors: []string{"error message when content is invalid"}, }, }, } for _, tc := range testCases { t.Run(tc.description, func(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/ci/lint", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) fmt.Fprint(w, tc.response) }) got, _, err := client.Validate.Lint(tc.opts) if err != nil { t.Errorf("Validate returned error: %v", err) } want := tc.want if !reflect.DeepEqual(got, want) { t.Errorf("Validate returned \ngot:\n%v\nwant:\n%v", Stringify(got), Stringify(want)) } }) } } func TestValidateProject(t *testing.T) { testCases := []struct { description string response string want *ProjectLintResult }{ { description: "valid", response: `{ "valid": true, "errors": [], "warnings": [], "merged_yaml": "---\n:build:\n :script:\n - echo build" }`, want: &ProjectLintResult{ Valid: true, Warnings: []string{}, Errors: []string{}, MergedYaml: "---\n:build:\n :script:\n - echo build", }, }, { description: "invalid", response: `{ "valid": false, "errors": ["jobs:build config contains unknown keys: bad_key"], "warnings": [], "merged_yaml": "---\n:build:\n :script:\n - echo build\n :bad_key: value" }`, want: &ProjectLintResult{ Valid: false, Warnings: []string{}, Errors: []string{"jobs:build config contains unknown keys: bad_key"}, MergedYaml: "---\n:build:\n :script:\n - echo build\n :bad_key: value", }, }, } for _, tc := range testCases { t.Run(tc.description, func(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/ci/lint", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, tc.response) }) opt := &ProjectLintOptions{} got, _, err := client.Validate.ProjectLint(1, opt) if err != nil { t.Errorf("Validate returned error: %v", err) } want := tc.want if !reflect.DeepEqual(got, want) { t.Errorf("Validate returned \ngot:\n%v\nwant:\n%v", Stringify(got), Stringify(want)) } }) } } func TestValidateProjectNamespace(t *testing.T) { testCases := []struct { description string request *ProjectNamespaceLintOptions response string want *ProjectLintResult }{ { description: "valid", request: &ProjectNamespaceLintOptions{ Content: Ptr("{'build': {'script': 'echo build'}}"), DryRun: Ptr(false), IncludeJobs: Ptr(true), Ref: Ptr("foo"), }, response: `{ "valid": true, "errors": [], "warnings": [], "merged_yaml": "---\n:build:\n :script:\n - echo build", "includes": [ { "type": "file", "location": "template/pipeline.yml", "blob": "https://gitlab.com/namespace/project/-/blob/abcd1234/template/pipeline.yml", "raw": "https://gitlab.com/namespace/project/-/raw/abcd1234/template/pipeline.yml", "extra": { "project": "namespace/project", "ref": "1.2.3" }, "context_project": "namespace/current-project", "context_sha": "abcd1234" } ] }`, want: &ProjectLintResult{ Valid: true, Warnings: []string{}, Errors: []string{}, MergedYaml: "---\n:build:\n :script:\n - echo build", Includes: []Include{ { Type: "file", Location: "template/pipeline.yml", Blob: "https://gitlab.com/namespace/project/-/blob/abcd1234/template/pipeline.yml", Raw: "https://gitlab.com/namespace/project/-/raw/abcd1234/template/pipeline.yml", Extra: map[string]interface{}{ "project": "namespace/project", "ref": "1.2.3", }, ContextProject: "namespace/current-project", ContextSHA: "abcd1234", }, }, }, }, { description: "invalid", request: &ProjectNamespaceLintOptions{ Content: Ptr("{'build': {'script': 'echo build', 'bad_key': 'value'}}"), DryRun: Ptr(false), }, response: `{ "valid": false, "errors": ["jobs:build config contains unknown keys: bad_key"], "warnings": [], "merged_yaml": "---\n:build:\n :script:\n - echo build\n :bad_key: value" }`, want: &ProjectLintResult{ Valid: false, Warnings: []string{}, Errors: []string{"jobs:build config contains unknown keys: bad_key"}, MergedYaml: "---\n:build:\n :script:\n - echo build\n :bad_key: value", }, }, } for _, tc := range testCases { t.Run(tc.description, func(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/ci/lint", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) fmt.Fprint(w, tc.response) }) got, _, err := client.Validate.ProjectNamespaceLint(1, tc.request) if err != nil { t.Errorf("Validate returned error: %v", err) } want := tc.want if !reflect.DeepEqual(got, want) { t.Errorf("Validate returned \ngot:\n%v\nwant:\n%v", Stringify(got), Stringify(want)) } }) } } func TestValidateProjectLint(t *testing.T) { testCases := []struct { description string request *ProjectLintOptions response string want *ProjectLintResult }{ { description: "valid", request: &ProjectLintOptions{ DryRun: Ptr(false), IncludeJobs: Ptr(true), ContentRef: Ptr("foo"), }, response: `{ "valid": true, "errors": [], "warnings": [], "merged_yaml": "---\n:build:\n :script:\n - echo build", "includes": [ { "type": "file", "location": "template/pipeline.yml", "blob": "https://gitlab.com/namespace/project/-/blob/abcd1234/template/pipeline.yml", "raw": "https://gitlab.com/namespace/project/-/raw/abcd1234/template/pipeline.yml", "extra": { "project": "namespace/project", "ref": "1.2.3" }, "context_project": "namespace/current-project", "context_sha": "abcd1234" } ] }`, want: &ProjectLintResult{ Valid: true, Warnings: []string{}, Errors: []string{}, MergedYaml: "---\n:build:\n :script:\n - echo build", Includes: []Include{ { Type: "file", Location: "template/pipeline.yml", Blob: "https://gitlab.com/namespace/project/-/blob/abcd1234/template/pipeline.yml", Raw: "https://gitlab.com/namespace/project/-/raw/abcd1234/template/pipeline.yml", Extra: map[string]interface{}{ "project": "namespace/project", "ref": "1.2.3", }, ContextProject: "namespace/current-project", ContextSHA: "abcd1234", }, }, }, }, } for _, tc := range testCases { t.Run(tc.description, func(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/ci/lint", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, tc.response) }) got, _, err := client.Validate.ProjectLint(1, tc.request) if err != nil { t.Errorf("Validate returned error: %v", err) } want := tc.want if !reflect.DeepEqual(got, want) { t.Errorf("Validate returned \ngot:\n%v\nwant:\n%v", Stringify(got), Stringify(want)) } }) } } golang-gitlab-gitlab-org-api-client-go-0.123.0/version.go000066400000000000000000000031641475761473200230770ustar00rootroot00000000000000// // Copyright 2021, Andrea Funto' // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import "net/http" // VersionService handles communication with the GitLab server instance to // retrieve its version information via the GitLab API. // // GitLab API docs: https://docs.gitlab.com/ee/api/version.html type VersionService struct { client *Client } // Version represents a GitLab instance version. // // GitLab API docs: https://docs.gitlab.com/ee/api/version.html type Version struct { Version string `json:"version"` Revision string `json:"revision"` } func (s Version) String() string { return Stringify(s) } // GetVersion gets a GitLab server instance version; it is only available to // authenticated users. // // GitLab API docs: https://docs.gitlab.com/ee/api/version.html func (s *VersionService) GetVersion(options ...RequestOptionFunc) (*Version, *Response, error) { req, err := s.client.NewRequest(http.MethodGet, "version", nil, options) if err != nil { return nil, nil, err } v := new(Version) resp, err := s.client.Do(req, v) if err != nil { return nil, resp, err } return v, resp, nil } golang-gitlab-gitlab-org-api-client-go-0.123.0/version_test.go000066400000000000000000000023161475761473200241340ustar00rootroot00000000000000// // Copyright 2021, Sander van Harmelen // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package gitlab import ( "fmt" "net/http" "reflect" "testing" ) func TestGetVersion(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/version", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, `{"version":"11.3.4-ee", "revision":"14d3a1d"}`) }) version, _, err := client.Version.GetVersion() if err != nil { t.Errorf("Version.GetVersion returned error: %v", err) } want := &Version{Version: "11.3.4-ee", Revision: "14d3a1d"} if !reflect.DeepEqual(want, version) { t.Errorf("Version.GetVersion returned %+v, want %+v", version, want) } } golang-gitlab-gitlab-org-api-client-go-0.123.0/wikis.go000066400000000000000000000136571475761473200225500ustar00rootroot00000000000000// // Copyright 2021, Stany MARCEL // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package gitlab import ( "fmt" "net/http" "net/url" ) // WikisService handles communication with the wikis related methods of // the Gitlab API. // // GitLab API docs: https://docs.gitlab.com/ee/api/wikis.html type WikisService struct { client *Client } // Wiki represents a GitLab wiki. // // GitLab API docs: https://docs.gitlab.com/ee/api/wikis.html type Wiki struct { Content string `json:"content"` Encoding string `json:"encoding"` Format WikiFormatValue `json:"format"` Slug string `json:"slug"` Title string `json:"title"` } func (w Wiki) String() string { return Stringify(w) } // ListWikisOptions represents the available ListWikis options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/wikis.html#list-wiki-pages type ListWikisOptions struct { WithContent *bool `url:"with_content,omitempty" json:"with_content,omitempty"` } // ListWikis lists all pages of the wiki of the given project id. // When with_content is set, it also returns the content of the pages. // // GitLab API docs: // https://docs.gitlab.com/ee/api/wikis.html#list-wiki-pages func (s *WikisService) ListWikis(pid interface{}, opt *ListWikisOptions, options ...RequestOptionFunc) ([]*Wiki, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/wikis", PathEscape(project)) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } var ws []*Wiki resp, err := s.client.Do(req, &ws) if err != nil { return nil, resp, err } return ws, resp, nil } // GetWikiPageOptions represents options to GetWikiPage // // GitLab API docs: // https://docs.gitlab.com/ee/api/wikis.html#get-a-wiki-page type GetWikiPageOptions struct { RenderHTML *bool `url:"render_html,omitempty" json:"render_html,omitempty"` Version *string `url:"version,omitempty" json:"version,omitempty"` } // GetWikiPage gets a wiki page for a given project. // // GitLab API docs: // https://docs.gitlab.com/ee/api/wikis.html#get-a-wiki-page func (s *WikisService) GetWikiPage(pid interface{}, slug string, opt *GetWikiPageOptions, options ...RequestOptionFunc) (*Wiki, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/wikis/%s", PathEscape(project), url.PathEscape(slug)) req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return nil, nil, err } w := new(Wiki) resp, err := s.client.Do(req, w) if err != nil { return nil, resp, err } return w, resp, nil } // CreateWikiPageOptions represents options to CreateWikiPage. // // GitLab API docs: // https://docs.gitlab.com/ee/api/wikis.html#create-a-new-wiki-page type CreateWikiPageOptions struct { Content *string `url:"content,omitempty" json:"content,omitempty"` Title *string `url:"title,omitempty" json:"title,omitempty"` Format *WikiFormatValue `url:"format,omitempty" json:"format,omitempty"` } // CreateWikiPage creates a new wiki page for the given repository with // the given title, slug, and content. // // GitLab API docs: // https://docs.gitlab.com/ee/api/wikis.html#create-a-new-wiki-page func (s *WikisService) CreateWikiPage(pid interface{}, opt *CreateWikiPageOptions, options ...RequestOptionFunc) (*Wiki, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/wikis", PathEscape(project)) req, err := s.client.NewRequest(http.MethodPost, u, opt, options) if err != nil { return nil, nil, err } w := new(Wiki) resp, err := s.client.Do(req, w) if err != nil { return nil, resp, err } return w, resp, nil } // EditWikiPageOptions represents options to EditWikiPage. // // GitLab API docs: // https://docs.gitlab.com/ee/api/wikis.html#edit-an-existing-wiki-page type EditWikiPageOptions struct { Content *string `url:"content,omitempty" json:"content,omitempty"` Title *string `url:"title,omitempty" json:"title,omitempty"` Format *WikiFormatValue `url:"format,omitempty" json:"format,omitempty"` } // EditWikiPage Updates an existing wiki page. At least one parameter is // required to update the wiki page. // // GitLab API docs: // https://docs.gitlab.com/ee/api/wikis.html#edit-an-existing-wiki-page func (s *WikisService) EditWikiPage(pid interface{}, slug string, opt *EditWikiPageOptions, options ...RequestOptionFunc) (*Wiki, *Response, error) { project, err := parseID(pid) if err != nil { return nil, nil, err } u := fmt.Sprintf("projects/%s/wikis/%s", PathEscape(project), url.PathEscape(slug)) req, err := s.client.NewRequest(http.MethodPut, u, opt, options) if err != nil { return nil, nil, err } w := new(Wiki) resp, err := s.client.Do(req, w) if err != nil { return nil, resp, err } return w, resp, nil } // DeleteWikiPage deletes a wiki page with a given slug. // // GitLab API docs: // https://docs.gitlab.com/ee/api/wikis.html#delete-a-wiki-page func (s *WikisService) DeleteWikiPage(pid interface{}, slug string, options ...RequestOptionFunc) (*Response, error) { project, err := parseID(pid) if err != nil { return nil, err } u := fmt.Sprintf("projects/%s/wikis/%s", PathEscape(project), url.PathEscape(slug)) req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) if err != nil { return nil, err } return s.client.Do(req, nil) } golang-gitlab-gitlab-org-api-client-go-0.123.0/wikis_test.go000066400000000000000000000111761475761473200236010ustar00rootroot00000000000000// // Copyright 2021, Stany MARCEL // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package gitlab import ( "fmt" "net/http" "reflect" "testing" ) func TestListWikis(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/wikis", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprintf(w, `[ { "content": "Here is an instruction how to deploy this project.", "format": "markdown", "slug": "deploy", "title": "deploy" }, { "content": "Our development process is described here.", "format": "markdown", "slug": "development", "title": "development" }, { "content": "* [Deploy](deploy)\n* [Development](development)", "format": "markdown", "slug": "home", "title": "home" } ]`) }) wikis, _, err := client.Wikis.ListWikis(1, &ListWikisOptions{WithContent: Ptr(true)}) if err != nil { t.Errorf("Wikis.ListWikis returned error: %v", err) } want := []*Wiki{ { Content: "Here is an instruction how to deploy this project.", Format: "markdown", Slug: "deploy", Title: "deploy", }, { Content: "Our development process is described here.", Format: "markdown", Slug: "development", Title: "development", }, { Content: "* [Deploy](deploy)\n* [Development](development)", Format: "markdown", Slug: "home", Title: "home", }, } if !reflect.DeepEqual(want, wikis) { t.Errorf("Labels.CreateLabel returned %+v, want %+v", wikis, want) } } func TestGetWikiPage(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/wikis/home", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprintf(w, `{ "content": "home page", "format": "markdown", "slug": "home", "title": "home", "encoding": "UTF-8" }`) }) wiki, _, err := client.Wikis.GetWikiPage(1, "home", &GetWikiPageOptions{}) if err != nil { t.Errorf("Wiki.GetWikiPage returned error: %v", err) } want := &Wiki{ Content: "home page", Encoding: "UTF-8", Format: "markdown", Slug: "home", Title: "home", } if !reflect.DeepEqual(want, wiki) { t.Errorf("Labels.CreateLabel returned %+v, want %+v", wiki, want) } } func TestCreateWikiPage(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/wikis", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) fmt.Fprintf(w, `{ "content": "Hello world", "format": "markdown", "slug": "Hello", "title": "Hello" }`) }) opt := &CreateWikiPageOptions{ Content: Ptr("Hello world"), Title: Ptr("Hello"), Format: Ptr(WikiFormatMarkdown), } wiki, _, err := client.Wikis.CreateWikiPage(1, opt) if err != nil { t.Errorf("Wiki.CreateWikiPage returned error: %v", err) } want := &Wiki{ Content: "Hello world", Format: "markdown", Slug: "Hello", Title: "Hello", } if !reflect.DeepEqual(want, wiki) { t.Errorf("Wiki.CreateWikiPage returned %+v, want %+v", wiki, want) } } func TestEditWikiPage(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/wikis/foo", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPut) fmt.Fprintf(w, `{ "content": "documentation", "format": "markdown", "slug": "Docs", "title": "Docs" }`) }) opt := &EditWikiPageOptions{ Content: Ptr("documentation"), Format: Ptr(WikiFormatMarkdown), Title: Ptr("Docs"), } wiki, _, err := client.Wikis.EditWikiPage(1, "foo", opt) if err != nil { t.Errorf("Wiki.EditWikiPage returned error: %v", err) } want := &Wiki{ Content: "documentation", Format: "markdown", Slug: "Docs", Title: "Docs", } if !reflect.DeepEqual(want, wiki) { t.Errorf("Wiki.EditWikiPage returned %+v, want %+v", wiki, want) } } func TestDeleteWikiPage(t *testing.T) { mux, client := setup(t) mux.HandleFunc("/api/v4/projects/1/wikis/foo", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodDelete) }) _, err := client.Wikis.DeleteWikiPage(1, "foo") if err != nil { t.Errorf("Wiki.DeleteWikiPage returned error: %v", err) } }