mirror of
https://github.com/strongdm/comply
synced 2025-12-15 02:34:13 +00:00
Compare commits
88 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c699c64627 | ||
|
|
24ff9dd762 | ||
|
|
5c160d1ec5 | ||
|
|
f3088bfe28 | ||
|
|
aa57be25c9 | ||
|
|
2cef618abb | ||
|
|
e8a0ecd076 | ||
|
|
1a3e2a2bf7 | ||
|
|
7225741a46 | ||
|
|
7105d2d4ef | ||
|
|
e3fb66b0e4 | ||
|
|
1028521ea7 | ||
|
|
b2c2bf3558 | ||
|
|
d44af2e1cd | ||
|
|
e2281b5fe3 | ||
|
|
d43aca58ba | ||
|
|
9e4cd2cf1a | ||
|
|
3711e0054d | ||
|
|
2e9f6cf270 | ||
|
|
c5868fa544 | ||
|
|
274986ad9c | ||
|
|
bcc9b06ac4 | ||
|
|
8f3d668789 | ||
|
|
358ac431a6 | ||
|
|
365e98222b | ||
|
|
84589a83f4 | ||
|
|
b329107079 | ||
|
|
3f5d9b4409 | ||
|
|
3df822206e | ||
|
|
007cf3dd3c | ||
|
|
97989c5cf6 | ||
|
|
ce5c4c3a4a | ||
|
|
cd89840164 | ||
|
|
e60d7285f4 | ||
|
|
c99d800397 | ||
|
|
0f1badca5b | ||
|
|
00b59ed620 | ||
|
|
749017761d | ||
|
|
f502225cde | ||
|
|
6cf6f70296 | ||
|
|
3494bdce7b | ||
|
|
924dd25744 | ||
|
|
02d3b75731 | ||
|
|
4a314c62d1 | ||
|
|
f2ef58e7bd | ||
|
|
fc1a1d9abc | ||
|
|
65dddc4332 | ||
|
|
eecfe49fbd | ||
|
|
44931ca808 | ||
|
|
06b8a2fe44 | ||
|
|
2d088cdf45 | ||
|
|
fb60f405ba | ||
|
|
3c696e6d01 | ||
|
|
4d63cf559b | ||
|
|
0ff74208cc | ||
|
|
75a80189ce | ||
|
|
f6c9f89792 | ||
|
|
25f7156ac2 | ||
|
|
2d5e6b48cb | ||
|
|
4d830789ec | ||
|
|
4969d179ec | ||
|
|
10dc0b70e0 | ||
|
|
0f68acae10 | ||
|
|
19e100801a | ||
|
|
46aaf1c663 | ||
|
|
815e7e5f61 | ||
|
|
ff626a5ee2 | ||
|
|
1ec70a67d1 | ||
|
|
096ad03ee1 | ||
|
|
5d67d60fd4 | ||
|
|
8e3ebdc94a | ||
|
|
39fd371c4e | ||
|
|
1e5383eb01 | ||
|
|
49e950c3c0 | ||
|
|
ff350a2b89 | ||
|
|
82baa57684 | ||
|
|
bb4200ff43 | ||
|
|
1b807da10e | ||
|
|
57a617abd5 | ||
|
|
1170ad6a92 | ||
|
|
b81d8388ef | ||
|
|
36331849c8 | ||
|
|
1d3dcc8f54 | ||
|
|
9309194a40 | ||
|
|
a37e8dc233 | ||
|
|
bef531973f | ||
|
|
a025ea5e39 | ||
|
|
cee7553319 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,3 +1,4 @@
|
||||
comply
|
||||
output
|
||||
dist
|
||||
.envrc
|
||||
|
||||
@@ -1,3 +1,12 @@
|
||||
# Authors in alphabetical order:
|
||||
|
||||
arambhashura
|
||||
Alan Cox
|
||||
Andy Magnusson
|
||||
Anthony Oliver
|
||||
Justin Bodeutsch
|
||||
Justin McCarthy <justin@strongdm.com>
|
||||
Kevin N. Murphy
|
||||
Manisha Singh
|
||||
Mason Hensley
|
||||
Matt Simerson
|
||||
29
Gopkg.lock
generated
29
Gopkg.lock
generated
@@ -7,6 +7,12 @@
|
||||
revision = "7da180ee92d8bd8bb8c37fc560e673e6557c392f"
|
||||
version = "v0.4.7"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/andygrunwald/go-jira"
|
||||
packages = ["."]
|
||||
revision = "5cfdb85cc91c6299f75b6504a1d0ec174c21be39"
|
||||
version = "v1.3.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/chzyer/readline"
|
||||
@@ -79,6 +85,12 @@
|
||||
revision = "507f6050b8568533fb3f5504de8e5205fa62a114"
|
||||
version = "v1.6.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/fatih/structs"
|
||||
packages = ["."]
|
||||
revision = "a720dfa8df582c51dee1b36feabb906bde1588bd"
|
||||
version = "v1.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/fsnotify/fsnotify"
|
||||
packages = ["."]
|
||||
@@ -194,12 +206,27 @@
|
||||
packages = ["open"]
|
||||
revision = "75fb7ed4208cf72d323d7d02fd1a5964a7a9073c"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/trivago/tgo"
|
||||
packages = [
|
||||
"tcontainer",
|
||||
"treflect"
|
||||
]
|
||||
revision = "e4d1ddd28c17dd89ed26327cf69fded22060671b"
|
||||
version = "v1.0.1"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/urfave/cli"
|
||||
packages = ["."]
|
||||
revision = "cfb38830724cc34fedffe9a2a29fb54fa9169cd1"
|
||||
version = "v1.20.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/xanzy/go-gitlab"
|
||||
packages = ["."]
|
||||
revision = "79dad8e74fd097eb2e0fd0883f1978213e88107a"
|
||||
version = "v0.10.7"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/yosssi/ace"
|
||||
packages = ["."]
|
||||
@@ -257,6 +284,6 @@
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "4fd2ff9f9869c3f3e30601504f4b00fce69d282ae8df42583a1c60848bfd0766"
|
||||
inputs-digest = "50dc710fa4b581d4fd6907e0e3337c53c3d530e46c53aa71ef9f8e80d3a54d8b"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
||||
|
||||
16
Makefile
16
Makefile
@@ -19,12 +19,14 @@ dist: clean
|
||||
$(eval LDFLAGS := -ldflags='-X "github.com/strongdm/comply/internal/cli.Version=$(VERSION)"')
|
||||
mkdir dist
|
||||
echo $(VERSION)
|
||||
GOOS=darwin GOARCH=amd64 CGO_ENABLED=0 go build -gcflags=-trimpath=$(GOPATH) -asmflags=-trimpath=$(GOPATH) $(LDFLAGS) -o dist/comply-$(VERSION)-darwin-amd64 .
|
||||
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -gcflags=-trimpath=$(GOPATH) -asmflags=-trimpath=$(GOPATH) $(LDFLAGS) -o dist/comply-$(VERSION)-linux-amd64 .
|
||||
GOOS=darwin GOARCH=amd64 CGO_ENABLED=0 go build -gcflags=-trimpath=$(GOPATH) -asmflags=-trimpath=$(GOPATH) -ldflags '-extldflags "-static"' $(LDFLAGS) -o dist/comply-$(VERSION)-darwin-amd64 .
|
||||
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -gcflags=-trimpath=$(GOPATH) -asmflags=-trimpath=$(GOPATH) -ldflags '-extldflags "-static"' $(LDFLAGS) -o dist/comply-$(VERSION)-linux-amd64 .
|
||||
GOOS=windows GOARCH=amd64 CGO_ENABLED=0 go build -gcflags=-trimpath=$(GOPATH) -asmflags=-trimpath=$(GOPATH) -ldflags '-extldflags "-static"' $(LDFLAGS) -o dist/comply-$(VERSION)-windows-amd64.exe .
|
||||
cd dist && tar -czvf comply-$(VERSION)-darwin-amd64.tgz comply-$(VERSION)-darwin-amd64
|
||||
cd dist && tar -czvf comply-$(VERSION)-linux-amd64.tgz comply-$(VERSION)-linux-amd64
|
||||
cd dist && zip comply-$(VERSION)-windows-amd64.zip comply-$(VERSION)-windows-amd64.exe
|
||||
|
||||
brew: clean assets $(GO_SOURCES)
|
||||
brew: clean $(GO_SOURCES)
|
||||
$(eval VERSION := $(shell cat version))
|
||||
$(eval LDFLAGS := -ldflags='-X "github.com/strongdm/comply/internal/cli.Version=$(VERSION)"')
|
||||
mkdir bin
|
||||
@@ -62,7 +64,6 @@ export-example:
|
||||
|
||||
docker:
|
||||
cd build && docker build -t strongdm/pandoc .
|
||||
docker tag jagregory/pandoc:latest strongdm/pandoc:latest
|
||||
docker push strongdm/pandoc
|
||||
|
||||
cleanse:
|
||||
@@ -78,6 +79,9 @@ release-env:
|
||||
ifndef GH_LOGIN
|
||||
$(error GH_LOGIN must be set to a valid GitHub token)
|
||||
endif
|
||||
ifndef COMPLY_TAPDIR
|
||||
$(error COMPLY_TAPDIR must be set to the path of the comply homebrew tap repo)
|
||||
endif
|
||||
|
||||
release: release-env dist release-deps
|
||||
$(eval VERSION := $(shell git describe --tags --always --dirty="-dev"))
|
||||
@@ -105,9 +109,9 @@ release: release-env dist release-deps
|
||||
--file dist/comply-$(VERSION)-linux-amd64.tgz
|
||||
|
||||
@echo "Update homebrew formula with the following: "
|
||||
$(eval SHA := $(shell curl -s -L https://github.com/strongdm/comply/archive/$(VERSION).tar.gz |shasum -a 256|awk '{print $1}'))
|
||||
$(eval SHA := $(shell curl -s -L https://github.com/strongdm/comply/archive/$(VERSION).tar.gz |shasum -a 256|cut -d" " -f1))
|
||||
@echo "version $(VERSION) sha $(SHA)"
|
||||
|
||||
cd $$COMPLY_TAPDIR && ./update.sh $(VERSION) $(SHA)
|
||||
|
||||
patch-release: release-env push-assets patch release
|
||||
$(eval VERSION := $(shell git describe --tags --always --dirty="-dev"))
|
||||
|
||||
48
README.md
48
README.md
@@ -1,4 +1,4 @@
|
||||
# Comply
|
||||

|
||||
|
||||
Comply is a SOC2-focused compliance automation tool:
|
||||
|
||||
@@ -12,6 +12,10 @@ macOS:
|
||||
|
||||
`brew tap strongdm/comply; brew install comply`
|
||||
|
||||
Linux:
|
||||
|
||||
[Download latest release](https://github.com/strongdm/comply/releases)
|
||||
|
||||
Go users:
|
||||
|
||||
`go get github.com/strongdm/comply`
|
||||
@@ -34,6 +38,8 @@ Join us in [Comply Users](https://join.slack.com/t/comply-users/shared_invite/en
|
||||
|
||||
# Screenshots
|
||||
|
||||
[Demo video](https://vimeo.com/270257486)
|
||||
|
||||
## Start a Project
|
||||

|
||||
|
||||
@@ -48,6 +54,10 @@ Join us in [Comply Users](https://join.slack.com/t/comply-users/shared_invite/en
|
||||
## Dashboard
|
||||

|
||||
|
||||
## Dependencies
|
||||
|
||||
Comply relies on [pandoc](https://pandoc.org/), which can be installed directly as an OS package or invoked via Docker.
|
||||
|
||||
## CLI
|
||||
|
||||
```
|
||||
@@ -67,4 +77,40 @@ COMMANDS:
|
||||
todo list declared vs satisfied compliance controls
|
||||
help, h Shows a list of commands or help for one command
|
||||
```
|
||||
## Ticketing Integrations:
|
||||
- Jira
|
||||
- Github
|
||||
- Gitlab
|
||||
|
||||
## Configuring Jira
|
||||
When comply creates a ticket (through `proc`, for instance), it sets the following fields.
|
||||
|
||||
- assignee
|
||||
- description
|
||||
- issuetype
|
||||
- labels
|
||||
- project key
|
||||
- reporter
|
||||
- summary
|
||||
|
||||
Please make sure that the default *Create Screen* has all of those fields enabled. Additionally, make sure that there are no other required fields for the issue type you choose.
|
||||
|
||||
|
||||
|
||||
|
||||
## Forking and local development
|
||||
> Assumes installation of golang and configuration of GOPATH in .bash_profile, .zshrc, etc
|
||||
> Inspiration: http://code.openark.org/blog/development/forking-golang-repositories-on-github-and-managing-the-import-path
|
||||
|
||||
```
|
||||
$ go get github.com/strongdm/comply
|
||||
$ cd $GOPATH/src/github.com/strongdm/comply ; go get ./...
|
||||
$ make
|
||||
$ cd example
|
||||
$ mv comply.yml.example comply.yml
|
||||
$ ../comply -h
|
||||
$ ../comply sync
|
||||
$ ../comply serve
|
||||
#
|
||||
$ make # recompile as needed with in $GOPATH/src/github.com/strongdm/comply
|
||||
```
|
||||
|
||||
@@ -1,3 +1,29 @@
|
||||
FROM scratch
|
||||
FROM haskell:latest
|
||||
|
||||
MAINTAINER strongDM Comply <comply@strongdm.com>
|
||||
# based on implementation by James Gregory <james@jagregory.com>
|
||||
MAINTAINER Comply <comply@strongdm.com>
|
||||
|
||||
# install latex packages
|
||||
RUN apt-get update -y \
|
||||
&& apt-get install -y -o Acquire::Retries=10 --no-install-recommends \
|
||||
texlive-latex-base \
|
||||
texlive-xetex \
|
||||
texlive-fonts-recommended \
|
||||
latex-xcolor \
|
||||
texlive-latex-extra \
|
||||
fontconfig \
|
||||
unzip \
|
||||
lmodern
|
||||
|
||||
# will ease up the update process
|
||||
# updating this env variable will trigger the automatic build of the Docker image
|
||||
ENV PANDOC_VERSION "2.2.1"
|
||||
|
||||
# install pandoc
|
||||
RUN cabal update && cabal install pandoc-${PANDOC_VERSION}
|
||||
|
||||
WORKDIR /source
|
||||
|
||||
ENTRYPOINT ["/root/.cabal/bin/pandoc"]
|
||||
|
||||
CMD ["--help"]
|
||||
|
||||
@@ -1,7 +1,31 @@
|
||||
name: "Acme"
|
||||
filePrefix: "Acme"
|
||||
|
||||
# The following setting is optional.
|
||||
# If you set this (to, e.g. master), and you build the policies
|
||||
# on that branch, then a section is appended to each policy that
|
||||
# describes the approval. Text will look like:
|
||||
#
|
||||
# Last edit made by John Doe (jdoe@email.com) on Wed, 15 Aug 2018 12:45:28 -0400.
|
||||
# Approved by Joan Smith (jsmith@email.com) on Wed, 15 Aug 2018 16:54:48 -0400 in commit abc123123.
|
||||
#
|
||||
# The change author gets credit for the edit.
|
||||
# The person who committed or merged to the approval branch gets credit for approval.
|
||||
approvedBranch: master
|
||||
tickets:
|
||||
github:
|
||||
token: XXX
|
||||
username: strongdm
|
||||
repo: comply
|
||||
repo: comply
|
||||
# jira:
|
||||
# username: xxxx # This is the username you log in to Jira's UI with. Probably your email address.
|
||||
# password: xxxx # If you don't have a "managed account", use your password in this field. But if your organization
|
||||
# # uses SAML or OAuth, or Jira's built-in multi-factor authentication, you need to use
|
||||
# # an API token. Learn more here: https://confluence.atlassian.com/cloud/api-tokens-938839638.html
|
||||
# project: comply
|
||||
# url: https://yourjira
|
||||
# taskType: Task # This must be an Issue, not a sub-task
|
||||
# gitlab:
|
||||
# domain: https://gitlab.example.com:443/ # or https://gitlab.com/
|
||||
# token: token-here
|
||||
# repo: full-slug/of-project
|
||||
|
||||
@@ -17,6 +17,78 @@ majorRevisions:
|
||||
|
||||
# Control Environment Narrative
|
||||
|
||||
Here we narrate why our control environment satisfies the control keys listed in the YML block
|
||||
The following provides a description of the control structure of {{.Name}}.
|
||||
|
||||
# Template Coming Soon
|
||||
The intent of this description is to enumerate the logical, policy, and procedural controls that serve to monitor {{.Name}}'s application and data security. Changes uncovered by these procedures in the logical, policy, procedural, or customer environment are addressed by remediations specific to the noted change.
|
||||
|
||||
# Logical Controls
|
||||
|
||||
{{.Name}} employs several logical controls to protect confidential data and ensure normal operation of its core product.
|
||||
|
||||
- Mandatory data encryption at rest and in motion
|
||||
- Multi-factor authentication for access to cloud infrastructure
|
||||
- Activity and anomaly monitoring on production systems
|
||||
- Vulnerability management program
|
||||
|
||||
# Policy Controls
|
||||
|
||||
{{.Name}} employs several policy controls to protect confidential data and ensure normal operation of its core product. These policies include, but are not limited to:
|
||||
|
||||
- Access Control Policy
|
||||
- Encryption Policy
|
||||
- Office Security Policy
|
||||
- Password Policy
|
||||
- Policy Training Policy
|
||||
- Vendor Policy
|
||||
- Workstation Policy
|
||||
|
||||
# Procedural Controls
|
||||
|
||||
{{.Name}} has numerous scheduled procedures to monitor and tune the effectiveness of ongoing security controls, and a series of event-driven procedures to respond to security-related events.
|
||||
|
||||
TODO: Finalize these lists
|
||||
|
||||
## Scheduled Security and Audit Procedures
|
||||
|
||||
- Review Access [quarterly]
|
||||
- Review Security Logs [weekly]
|
||||
- Review Cyber Risk Assessment (enumerate possible compromise scenarios) [quarterly]
|
||||
- Review Data Classification [quarterly]
|
||||
- Backup Testing [quarterly]
|
||||
- Disaster Recovery Testing [semi-annual]
|
||||
- Review Devices & Workstations [quarterly]
|
||||
- Review & Clear Low-Priority Alerts [weekly]
|
||||
- Apply OS Patches [monthly]
|
||||
- Verify Data Disposal per Retention Policy [quarterly]
|
||||
- Conduct Security Training [annual]
|
||||
- Review Security Monitoring and Alerting Configuration [quarterly]
|
||||
- Penetration Test [annual]
|
||||
- Whitebox Security Review [annual]
|
||||
- SOC2 Audit [annual]
|
||||
|
||||
## Event-Driven Security and Audit Procedures
|
||||
|
||||
- Onboard Employee
|
||||
- Offboard Employee
|
||||
- Investigate Security Alert
|
||||
- Investigate Security Incident
|
||||
|
||||
# Remediations
|
||||
|
||||
{{.Name}} uses the outcomes of the aforementioned controls and procedures to identify shortcomings in the existing control environment. Once identified, these shortcomes are remediated by improving existing controls and procedures, and creating new controls and procedures as needed.
|
||||
|
||||
# Communications
|
||||
|
||||
{{.Name}} communicates relevant information regarding the functioning of the above controls with internal and external parties on an as-needed basis and according to statutory requirements.
|
||||
|
||||
## Internal
|
||||
|
||||
{{.Name}} communicates control outcomes, anomalies, and remediations internally using the following channels:
|
||||
|
||||
- Slack
|
||||
- Email
|
||||
- Github ticketing
|
||||
|
||||
## External
|
||||
|
||||
{{.Name}} communicates relevant control-related information to external parties including shareholders, customers, contractors, regulators, and government entities as needed according to contractual and regulatory/statutory obligation.
|
||||
@@ -9,4 +9,39 @@ majorRevisions:
|
||||
|
||||
Here we describe the key products marketed by our organization
|
||||
|
||||
# Template Coming Soon
|
||||
# Products
|
||||
|
||||
## Product 1
|
||||
|
||||
Overview of product 1
|
||||
|
||||
### Architecture
|
||||
|
||||
Brief architectural discussion of product 1
|
||||
|
||||
### Security Considerations
|
||||
|
||||
Specific security considerations for product 1. Refer to policies, procedures here.
|
||||
|
||||
# References
|
||||
|
||||
## Narratives
|
||||
|
||||
List relevant narratives, probably including
|
||||
Organizational Narrative
|
||||
Security Narrative
|
||||
System Narrative
|
||||
|
||||
## Policies
|
||||
|
||||
List relevant policies, probably including
|
||||
Application Security Policy
|
||||
Datacenter Policy
|
||||
Log Management Policy
|
||||
Password Policy
|
||||
Security Incident Response Policy
|
||||
Risk Assessment Policy
|
||||
|
||||
## Procedures
|
||||
|
||||
List relevant procedures, probably including access review, patching, alert monitoring, log review, pen testing
|
||||
@@ -15,4 +15,99 @@ majorRevisions:
|
||||
|
||||
Here we narrate why our org satisfies the control keys listed in the YML block
|
||||
|
||||
# Template Coming Soon
|
||||
# {{.Name}} Product Architecture
|
||||
|
||||
Describe product architecture here, emphasizing security implications
|
||||
|
||||
# {{.Name}} Infrastructure
|
||||
|
||||
## Product Infrastructure
|
||||
|
||||
Describe product infrastructure, emphasizing security measures
|
||||
|
||||
### Authorized Personnel
|
||||
|
||||
- **AWS root account** access is granted only to the CTO and CEO
|
||||
- **AWS IAM** access is granted to to a limited group of **Operators**
|
||||
- **{{.Name}} SSH** access is granted to a limited group of **Operators**
|
||||
- **{{.Name}} DB** access is granted to a limited group of **Data Operators**
|
||||
|
||||
## IT Infrastructure
|
||||
|
||||
{{.Name}} uses the following cloud services for its internal infrastructure:
|
||||
|
||||
- List cloud services
|
||||
|
||||
Access to these cloud services is limited according to the role of the {{.Name}} employee and is reviewed quarterly as well as via regular onboarding/offboarding tasks for new and departing employees.
|
||||
|
||||
# {{.Name}} Workstations
|
||||
|
||||
{{.Name}} workstations are hardened against logical and physical attack by the following measures:
|
||||
|
||||
- operating system must be within one generation of current
|
||||
- full-disk encryption
|
||||
- onboard antivirus/antimalware software
|
||||
- OS and AV automatically updated
|
||||
|
||||
Workstation compliance with these measures is evaluated on a quarterly basis.
|
||||
|
||||
## Remote Access
|
||||
|
||||
Many {{.Name}} employees work remotely on a regular basis and connect to production and internal IT systems via the same methods as those employees connecting from the {{.Name}} physical office, i.e., direct encrypted access to cloud services. It is the employee's responsibility to ensure that only authorized personnel use {{.Name}} resources and access {{.Name}} systems.
|
||||
|
||||
# Access Review
|
||||
|
||||
Access to {{.Name}} infrastructure, both internal and product, is reviewed quarterly and inactive users are removed. Any anomalies are reported to the security team for further investigation. When employees start or depart, an onboarding/offboarding procedure is followed to provision or deprovision appropriate account access.
|
||||
|
||||
# Penetration Testing
|
||||
|
||||
{{.Name}} commissions an external penetration test on an annual basis. All findings are immediately reviewed and addressed to the satisfaction of the CTO/CEO.
|
||||
|
||||
# {{.Name}} Physical Security
|
||||
|
||||
{{.Name}} has one physical location, in San Francisco, CA. Key issuance is tracked by the Office Physical Security Policy Ledger. Office keys are additionally held by the lessor, property management, and custodial staff. These keys are not tracked by the Office Physical Security Policy Ledger. {{.Name}} managers regularly review physical access privileges.
|
||||
|
||||
{{.Name}} infrastructure is located within AWS. {{.Name}} does not have physical access to AWS infrastructure.
|
||||
|
||||
# Risk Assessment
|
||||
|
||||
{{.Name}} updates its Cyber Risk Assessment on an annual basis in order to keep pace with the evolving threat landscape. The following is an inventory of adversarial and non-adversarial threats assessed to be of importance to {{.Name}}.
|
||||
|
||||
## Adversarial Threats
|
||||
|
||||
The following represents the inventory of adversarial threats:
|
||||
|
||||
|Threat|Source|Vector|Target|Likelihood|Severity|
|
||||
|----------------------------+--------------+------------+-----------------+----------+------|
|
||||
| | | | | | |
|
||||
|
||||
## Non-Adversarial Threats
|
||||
|
||||
The following represents the inventory of non-adversarial threats:
|
||||
|
||||
|Threat|Vector|Target|Likelihood|Severity|
|
||||
|----------------------------+--------------+-------------+----------+------|
|
||||
| | | | | |
|
||||
|
||||
# References
|
||||
|
||||
## Narratives
|
||||
|
||||
Products and Services Narrative
|
||||
System Architecture Narrative
|
||||
|
||||
## Policies
|
||||
|
||||
Encryption Policy
|
||||
Log Management Policy
|
||||
Office Security Policy
|
||||
Remote Access Policy
|
||||
Security Incident Response Policy
|
||||
Workstation Policy
|
||||
|
||||
## Procedures
|
||||
|
||||
Apply OS Patches
|
||||
Review & Clear Low-Priority Alerts
|
||||
Review Access
|
||||
Review Devices & Workstations
|
||||
|
||||
@@ -9,5 +9,49 @@ majorRevisions:
|
||||
- date: Jun 1 2018
|
||||
comment: Initial document
|
||||
---
|
||||
# Purpose and Scope
|
||||
|
||||
a. The purpose of this policy to define procedures to onboard and offboard users to technical infrastructure in a manner that minimizes the risk of information loss or exposure.
|
||||
|
||||
a. This policy applies to all technical infrastructure within the organization.
|
||||
|
||||
a. This policy applies to all full-time and part-time employees and contractors.
|
||||
|
||||
# Background
|
||||
|
||||
a. In order to minimize the risk of information loss or exposure (from both inside and outside the organization), the organization is reliant on the principle of least privilege. Account creation and permission levels are restricted to only the resources absolutely needed to perform each person’s job duties. When a user’s role within the organization changes, those accounts and permission levels are changed/revoked to fit the new role and disabled when the user leaves the organization altogether.
|
||||
|
||||
# Policy
|
||||
|
||||
a. *During onboarding:*
|
||||
|
||||
i. Hiring Manager informs HR upon hire of a new employee.
|
||||
|
||||
i. HR emails IT to inform them of a new hire and their role.
|
||||
|
||||
i. IT creates a checklist of accounts and permission levels needed for that role.
|
||||
|
||||
i. The owner of each resource reviews and approves account creation and the
|
||||
associated permissions.
|
||||
|
||||
i. IT works with the owner of each resource to set up the user.
|
||||
|
||||
a. *During offboarding:*
|
||||
|
||||
i. Hiring Manager notifies HR when an employee has been terminated.
|
||||
|
||||
i. HR sends a weekly email report to IT summarizing list of users terminated and instructs IT to disable their access.
|
||||
|
||||
i. IT terminates access within five business days from receipt of notification.
|
||||
|
||||
a. *When an employee changes roles within the organization:*
|
||||
|
||||
i. Hiring Manager will inform HR of a change in role.
|
||||
|
||||
i. HR and IT will follow the same steps as outlined in the onboarding and offboarding procedures.
|
||||
|
||||
a. *Review of accounts and permissions:*
|
||||
|
||||
i. Each month, IT and HR will review accounts and permission levels for accuracy.
|
||||
|
||||
|
||||
# Coming Soon
|
||||
@@ -8,7 +8,8 @@ majorRevisions:
|
||||
- date: Jun 1 2018
|
||||
comment: Initial document
|
||||
---
|
||||
#Purpose and Scope
|
||||
|
||||
# Purpose and Scope
|
||||
|
||||
a. The purpose of this policy is to define the organization’s procedures to recover Information Technology (IT) infrastructure and IT services within set deadlines in the case of a disaster or other disruptive incident. The objective of this plan is to complete the recovery of IT infrastructure and IT services within a set Recovery Time Objective (RTO).
|
||||
|
||||
|
||||
@@ -6,15 +6,24 @@ import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/strongdm/comply/internal/config"
|
||||
"github.com/strongdm/comply/internal/gitlab"
|
||||
"github.com/strongdm/comply/internal/jira"
|
||||
"github.com/strongdm/comply/internal/plugin/github"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
@@ -40,18 +49,20 @@ func newApp() *cli.App {
|
||||
app.Usage = "policy compliance toolkit"
|
||||
|
||||
app.Commands = []cli.Command{
|
||||
initCommand,
|
||||
beforeCommand(initCommand, notifyVersion),
|
||||
}
|
||||
|
||||
app.Commands = append(app.Commands, beforeCommand(buildCommand, projectMustExist))
|
||||
app.Commands = append(app.Commands, beforeCommand(procedureCommand, projectMustExist))
|
||||
app.Commands = append(app.Commands, beforeCommand(schedulerCommand, projectMustExist))
|
||||
app.Commands = append(app.Commands, beforeCommand(serveCommand, projectMustExist))
|
||||
app.Commands = append(app.Commands, beforeCommand(syncCommand, projectMustExist))
|
||||
app.Commands = append(app.Commands, beforeCommand(todoCommand, projectMustExist))
|
||||
app.Commands = append(app.Commands, beforeCommand(buildCommand, projectMustExist, notifyVersion))
|
||||
app.Commands = append(app.Commands, beforeCommand(procedureCommand, projectMustExist, notifyVersion))
|
||||
app.Commands = append(app.Commands, beforeCommand(schedulerCommand, projectMustExist, notifyVersion))
|
||||
app.Commands = append(app.Commands, beforeCommand(serveCommand, projectMustExist, notifyVersion))
|
||||
app.Commands = append(app.Commands, beforeCommand(syncCommand, projectMustExist, notifyVersion))
|
||||
app.Commands = append(app.Commands, beforeCommand(todoCommand, projectMustExist, notifyVersion))
|
||||
|
||||
// Plugins
|
||||
github.Register()
|
||||
jira.Register()
|
||||
gitlab.Register()
|
||||
|
||||
return app
|
||||
}
|
||||
@@ -96,7 +107,157 @@ func ticketingMustBeConfigured(c *cli.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func dockerMustExist(c *cli.Context) error {
|
||||
// notifyVersion asynchronously notifies the availability of version updates
|
||||
func notifyVersion(c *cli.Context) error {
|
||||
go func() {
|
||||
defer func() {
|
||||
recover() // suppress panic
|
||||
}()
|
||||
|
||||
r, err := http.Get("http://comply-releases.s3.amazonaws.com/channel/stable/VERSION")
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
// fail silently
|
||||
}
|
||||
|
||||
version := strings.TrimSpace(string(body))
|
||||
|
||||
// only when numeric versions are present
|
||||
firstRune, _ := utf8.DecodeRuneInString(string(body))
|
||||
if unicode.IsDigit(firstRune) && version != Version {
|
||||
// only once every ~10 times
|
||||
if rand.Intn(10) == 0 {
|
||||
fmt.Fprintf(os.Stderr, "a new version of comply is available")
|
||||
}
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
func pandocMustExist(c *cli.Context) error {
|
||||
eitherMustExistErr := fmt.Errorf("\n\nPlease install either Docker or the pandoc package and re-run `%s`. Find OS-specific pandoc installation instructions at: [TODO]", c.Command.Name)
|
||||
|
||||
pandocExistErr, found, goodVersion, pdfLatex := pandocBinaryMustExist(c)
|
||||
dockerExistErr, inPath, isRunning := dockerMustExist(c)
|
||||
|
||||
config.SetPandoc(pandocExistErr == nil, dockerExistErr == nil)
|
||||
check := func(b bool) string {
|
||||
if b {
|
||||
return "✔"
|
||||
} else {
|
||||
return "✖"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if pandocExistErr != nil && dockerExistErr != nil {
|
||||
|
||||
fmt.Printf(`
|
||||
[%s] pandoc binary installed and in PATH
|
||||
[%s] pandoc version compatible
|
||||
[%s] pdflatex binary installed and in PATH
|
||||
[%s] docker binary installed
|
||||
[%s] docker running
|
||||
|
||||
`, check(found), check(goodVersion), check(pdfLatex), check(inPath), check(isRunning))
|
||||
|
||||
return eitherMustExistErr
|
||||
}
|
||||
|
||||
// if we don't have pandoc, but we do have docker, execute a pull
|
||||
if (pandocExistErr != nil && dockerExistErr == nil) || config.WhichPandoc() == config.UseDocker {
|
||||
dockerPull(c)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func pandocBinaryMustExist(c *cli.Context) (e error, found, goodVersion, pdfLatex bool) {
|
||||
cmd := exec.Command("pandoc", "-v")
|
||||
outputRaw, err := cmd.Output()
|
||||
|
||||
e = nil
|
||||
found = false
|
||||
goodVersion = false
|
||||
pdfLatex = false
|
||||
|
||||
if err != nil {
|
||||
e = errors.Wrap(err, "error calling pandoc")
|
||||
} else {
|
||||
found = true
|
||||
goodVersion = true
|
||||
output := strings.TrimSpace((string(outputRaw)))
|
||||
versionErr := errors.New("cannot determine pandoc version")
|
||||
if !strings.HasPrefix(output, "pandoc") {
|
||||
e = versionErr
|
||||
goodVersion = false
|
||||
} else {
|
||||
re := regexp.MustCompile(`pandoc (\d+)\.(\d+)`)
|
||||
result := re.FindStringSubmatch(output)
|
||||
if len(result) != 3 {
|
||||
e = versionErr
|
||||
goodVersion = false
|
||||
} else {
|
||||
major, err := strconv.Atoi(result[1])
|
||||
if err != nil {
|
||||
e = versionErr
|
||||
goodVersion = false
|
||||
}
|
||||
minor, err := strconv.Atoi(result[2])
|
||||
if err != nil {
|
||||
e = versionErr
|
||||
goodVersion = false
|
||||
}
|
||||
if major < 2 || minor < 1 {
|
||||
e = errors.New("pandoc 2.1 or greater required")
|
||||
goodVersion = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// pdflatex must also be present
|
||||
cmd = exec.Command("pdflatex", "--version")
|
||||
outputRaw, err = cmd.Output()
|
||||
if err != nil {
|
||||
e = errors.Wrap(err, "error calling pdflatex")
|
||||
} else if !strings.Contains(string(outputRaw), "TeX") {
|
||||
e = errors.New("pdflatex is required")
|
||||
} else {
|
||||
pdfLatex = true
|
||||
}
|
||||
|
||||
return e, found, goodVersion, pdfLatex
|
||||
}
|
||||
|
||||
func dockerMustExist(c *cli.Context) (e error, inPath, isRunning bool) {
|
||||
dockerErr := fmt.Errorf("Docker must be available in order to run `%s`", c.Command.Name)
|
||||
|
||||
inPath = true
|
||||
cmd := exec.Command("docker", "--version")
|
||||
_, err := cmd.Output()
|
||||
if err != nil {
|
||||
inPath = false
|
||||
}
|
||||
|
||||
isRunning = true
|
||||
ctx := context.Background()
|
||||
cli, err := client.NewEnvClient()
|
||||
if err != nil {
|
||||
isRunning = false
|
||||
return dockerErr, inPath, isRunning
|
||||
}
|
||||
|
||||
_, err = cli.Ping(ctx)
|
||||
if err != nil {
|
||||
isRunning = false
|
||||
return dockerErr, inPath, isRunning
|
||||
}
|
||||
|
||||
return nil, inPath, isRunning
|
||||
}
|
||||
|
||||
func dockerPull(c *cli.Context) error {
|
||||
dockerErr := fmt.Errorf("Docker must be available in order to run `%s`", c.Command.Name)
|
||||
|
||||
ctx := context.Background()
|
||||
@@ -146,12 +307,17 @@ func dockerMustExist(c *cli.Context) error {
|
||||
}
|
||||
|
||||
func cleanContainers(c *cli.Context) error {
|
||||
dockerErr := fmt.Errorf("Docker must be available in order to run `%s`", c.Command.Name)
|
||||
|
||||
ctx := context.Background()
|
||||
cli, err := client.NewEnvClient()
|
||||
if err != nil {
|
||||
return dockerErr
|
||||
// no Docker? nothing to clean.
|
||||
return nil
|
||||
}
|
||||
|
||||
_, err = cli.Ping(ctx)
|
||||
if err != nil {
|
||||
// no Docker? nothing to clean.
|
||||
return nil
|
||||
}
|
||||
|
||||
containers, err := cli.ContainerList(ctx, types.ContainerListOptions{All: true})
|
||||
|
||||
@@ -11,7 +11,7 @@ var buildCommand = cli.Command{
|
||||
ShortName: "b",
|
||||
Usage: "generate a static website summarizing the compliance program",
|
||||
Action: buildAction,
|
||||
Before: beforeAll(dockerMustExist, cleanContainers),
|
||||
Before: beforeAll(pandocMustExist, cleanContainers),
|
||||
}
|
||||
|
||||
func buildAction(c *cli.Context) error {
|
||||
|
||||
@@ -100,7 +100,7 @@ func initAction(c *cli.Context) error {
|
||||
|
||||
chooser = promptui.Select{
|
||||
Label: "Ticket System",
|
||||
Items: []string{"GitHub", "Jira", "None"},
|
||||
Items: []string{"GitHub", "Jira", "GitLab", "None"},
|
||||
}
|
||||
|
||||
choice, _, err = chooser.Run()
|
||||
@@ -116,8 +116,9 @@ func initAction(c *cli.Context) error {
|
||||
case 0:
|
||||
ticketing = model.GitHub
|
||||
case 1:
|
||||
fmt.Println("\nHello Jira user! The Jira ticketing plugin is currently in development, please join us on Slack for a status update.")
|
||||
ticketing = model.NoTickets
|
||||
ticketing = model.Jira
|
||||
case 2:
|
||||
ticketing = model.GitLab
|
||||
default:
|
||||
ticketing = model.NoTickets
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package cli
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/strongdm/comply/internal/config"
|
||||
"github.com/strongdm/comply/internal/model"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
@@ -13,7 +14,7 @@ var procedureCommand = cli.Command{
|
||||
Usage: "create ticket by procedure ID",
|
||||
ArgsUsage: "procedureID",
|
||||
Action: procedureAction,
|
||||
Before: projectMustExist,
|
||||
Before: beforeAll(projectMustExist, ticketingMustBeConfigured),
|
||||
}
|
||||
|
||||
func procedureAction(c *cli.Context) error {
|
||||
@@ -28,14 +29,22 @@ func procedureAction(c *cli.Context) error {
|
||||
|
||||
procedureID := c.Args().First()
|
||||
|
||||
ts, err := config.Config().TicketSystem()
|
||||
if err != nil {
|
||||
return cli.NewExitError("error in ticket system configuration", 1)
|
||||
}
|
||||
|
||||
tp := model.GetPlugin(model.TicketSystem(ts))
|
||||
|
||||
for _, procedure := range procedures {
|
||||
if procedure.ID == procedureID {
|
||||
// TODO: don't hardcode GH
|
||||
tp := model.GetPlugin(model.GitHub)
|
||||
tp.Create(&model.Ticket{
|
||||
err = tp.Create(&model.Ticket{
|
||||
Name: procedure.Name,
|
||||
Body: fmt.Sprintf("%s\n\n\n---\nProcedure-ID: %s", procedure.Body, procedure.ID),
|
||||
}, []string{"comply", "comply-procedure"})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ var serveCommand = cli.Command{
|
||||
Name: "serve",
|
||||
Usage: "live updating version of the build command",
|
||||
Action: serveAction,
|
||||
Before: beforeAll(dockerMustExist, cleanContainers),
|
||||
Before: beforeAll(pandocMustExist, cleanContainers),
|
||||
}
|
||||
|
||||
func serveAction(c *cli.Context) error {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"github.com/strongdm/comply/internal/config"
|
||||
"github.com/strongdm/comply/internal/model"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
@@ -13,8 +14,12 @@ var syncCommand = cli.Command{
|
||||
}
|
||||
|
||||
func syncAction(c *cli.Context) error {
|
||||
// TODO: unhardcode plugin
|
||||
tp := model.GetPlugin(model.GitHub)
|
||||
ts, err := config.Config().TicketSystem()
|
||||
if err != nil {
|
||||
return cli.NewExitError("error in ticket system configuration", 1)
|
||||
}
|
||||
|
||||
tp := model.GetPlugin(model.TicketSystem(ts))
|
||||
tickets, err := tp.FindByTagName("comply")
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -10,15 +11,54 @@ import (
|
||||
|
||||
var projectRoot string
|
||||
|
||||
var dockerAvailable, pandocAvailable bool
|
||||
|
||||
const (
|
||||
Jira = "jira"
|
||||
GitHub = "github"
|
||||
GitLab = "gitlab"
|
||||
NoTickets = "none"
|
||||
)
|
||||
|
||||
const (
|
||||
// UseDocker invokes pandoc within Docker
|
||||
UseDocker = "docker"
|
||||
// UsePandoc invokes pandoc directly
|
||||
UsePandoc = "pandoc"
|
||||
)
|
||||
|
||||
// SetProjectRoot is used by the test suite.
|
||||
func SetProjectRoot(dir string) {
|
||||
projectRoot = dir
|
||||
}
|
||||
|
||||
type Project struct {
|
||||
Name string `yaml:"name"`
|
||||
FilePrefix string `yaml:"filePrefix"`
|
||||
Tickets map[string]interface{} `yaml:"tickets"`
|
||||
Name string `yaml:"name"`
|
||||
Pandoc string `yaml:"pandoc,omitempty"`
|
||||
FilePrefix string `yaml:"filePrefix"`
|
||||
Tickets map[string]interface{} `yaml:"tickets"`
|
||||
ApprovedBranch string `yaml:"approvedBranch"`
|
||||
}
|
||||
|
||||
// SetPandoc records pandoc availability during initialization
|
||||
func SetPandoc(pandoc bool, docker bool) {
|
||||
pandocAvailable = pandoc
|
||||
dockerAvailable = docker
|
||||
}
|
||||
|
||||
// WhichPandoc indicates which pandoc invocation path should be used
|
||||
func WhichPandoc() string {
|
||||
cfg := Config()
|
||||
if cfg.Pandoc == UsePandoc {
|
||||
return UsePandoc
|
||||
}
|
||||
if cfg.Pandoc == UseDocker {
|
||||
return UseDocker
|
||||
}
|
||||
if pandocAvailable {
|
||||
return UsePandoc
|
||||
}
|
||||
return UseDocker
|
||||
}
|
||||
|
||||
// YAML is the parsed contents of ProjectRoot()/config.yml.
|
||||
@@ -42,14 +82,14 @@ func Exists() bool {
|
||||
}
|
||||
|
||||
// Config is the parsed contents of ProjectRoot()/config.yml.
|
||||
func Config() Project {
|
||||
func Config() *Project {
|
||||
p := Project{}
|
||||
cfgBytes, err := ioutil.ReadFile(filepath.Join(ProjectRoot(), "comply.yml"))
|
||||
if err != nil {
|
||||
panic("unable to load config.yml: " + err.Error())
|
||||
}
|
||||
yaml.Unmarshal(cfgBytes, &p)
|
||||
return p
|
||||
return &p
|
||||
}
|
||||
|
||||
// ProjectRoot is the fully-qualified path to the root directory.
|
||||
@@ -64,3 +104,29 @@ func ProjectRoot() string {
|
||||
|
||||
return projectRoot
|
||||
}
|
||||
|
||||
// TicketSystem indicates the type of the configured ticket system
|
||||
func (p *Project) TicketSystem() (string, error) {
|
||||
if len(p.Tickets) > 1 {
|
||||
return NoTickets, errors.New("multiple ticket systems configured")
|
||||
}
|
||||
|
||||
for k := range p.Tickets {
|
||||
switch k {
|
||||
case GitHub:
|
||||
return GitHub, nil
|
||||
case Jira:
|
||||
return Jira, nil
|
||||
case GitLab:
|
||||
return GitLab, nil
|
||||
case NoTickets:
|
||||
return NoTickets, nil
|
||||
default:
|
||||
// explicit error for this case
|
||||
return "", errors.New("unrecognized ticket system configured")
|
||||
}
|
||||
}
|
||||
|
||||
// no ticket block configured
|
||||
return NoTickets, nil
|
||||
}
|
||||
|
||||
189
internal/gitlab/gitlab.go
Normal file
189
internal/gitlab/gitlab.go
Normal file
@@ -0,0 +1,189 @@
|
||||
package gitlab
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/strongdm/comply/internal/model"
|
||||
"github.com/xanzy/go-gitlab"
|
||||
)
|
||||
|
||||
const (
|
||||
cfgDomain = "domain"
|
||||
cfgToken = "token"
|
||||
cfgRepo = "repo"
|
||||
)
|
||||
|
||||
var prompts = map[string]string{
|
||||
cfgDomain: "Fully Qualified GitLab Domain",
|
||||
cfgToken: "GitLab Token",
|
||||
cfgRepo: "GitLab Repository",
|
||||
}
|
||||
|
||||
// Prompts are human-readable configuration element names
|
||||
func (g *gitlabPlugin) Prompts() map[string]string {
|
||||
return prompts
|
||||
}
|
||||
|
||||
// Register causes the Github plugin to register itself
|
||||
func Register() {
|
||||
model.Register(model.GitLab, &gitlabPlugin{})
|
||||
}
|
||||
|
||||
type gitlabPlugin struct {
|
||||
domain string
|
||||
token string
|
||||
reponame string
|
||||
|
||||
clientMu sync.Mutex
|
||||
client *gitlab.Client
|
||||
}
|
||||
|
||||
func (g *gitlabPlugin) api() *gitlab.Client {
|
||||
g.clientMu.Lock()
|
||||
defer g.clientMu.Unlock()
|
||||
if g.client == nil {
|
||||
// get go-gitlab client
|
||||
gl := gitlab.NewClient(nil, g.token)
|
||||
gl.SetBaseURL(g.domain)
|
||||
g.client = gl
|
||||
}
|
||||
return g.client
|
||||
}
|
||||
|
||||
func (g *gitlabPlugin) Get(ID string) (*model.Ticket, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (g *gitlabPlugin) Configured() bool {
|
||||
return g.reponame != "" && g.token != ""
|
||||
}
|
||||
|
||||
func (g *gitlabPlugin) Links() model.TicketLinks {
|
||||
links := model.TicketLinks{}
|
||||
links.AuditAll = fmt.Sprintf("%s/%s/issues?scope=all&utf8=✓&state=all&label_name[]=comply-audit", g.domain, g.reponame)
|
||||
links.AuditOpen = fmt.Sprintf("%s/%s/issues?scope=all&utf8=✓&state=opened&label_name[]=comply-audit", g.domain, g.reponame)
|
||||
links.ProcedureAll = fmt.Sprintf("%s/%s/issues?scope=all&utf8=✓&state=all&label_name[]=comply-procedure", g.domain, g.reponame)
|
||||
links.ProcedureOpen = fmt.Sprintf("%s/%s/issues?scope=all&utf8=✓&state=opened&label_name[]=comply-procedure", g.domain, g.reponame)
|
||||
return links
|
||||
}
|
||||
|
||||
func (g *gitlabPlugin) Configure(cfg map[string]interface{}) error {
|
||||
var err error
|
||||
|
||||
if g.domain, err = getCfg(cfg, cfgDomain); err != nil {
|
||||
return err
|
||||
}
|
||||
if g.token, err = getCfg(cfg, cfgToken); err != nil {
|
||||
return err
|
||||
}
|
||||
if g.reponame, err = getCfg(cfg, cfgRepo); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getCfg(cfg map[string]interface{}, k string) (string, error) {
|
||||
v, ok := cfg[k]
|
||||
if !ok {
|
||||
return "", errors.New("Missing key: " + k)
|
||||
}
|
||||
|
||||
vS, ok := v.(string)
|
||||
if !ok {
|
||||
return "", errors.New("Malformatted key: " + k)
|
||||
}
|
||||
return vS, nil
|
||||
}
|
||||
|
||||
func (g *gitlabPlugin) FindOpen() ([]*model.Ticket, error) {
|
||||
options := &gitlab.ListProjectIssuesOptions{
|
||||
State: gitlab.String("opened"),
|
||||
}
|
||||
|
||||
issues, _, err := g.api().Issues.ListProjectIssues(g.reponame, options)
|
||||
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error during FindOpen")
|
||||
}
|
||||
|
||||
return toTickets(issues), nil
|
||||
}
|
||||
|
||||
func (g *gitlabPlugin) FindByTag(name, value string) ([]*model.Ticket, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (g *gitlabPlugin) FindByTagName(name string) ([]*model.Ticket, error) {
|
||||
options := &gitlab.ListProjectIssuesOptions{
|
||||
State: gitlab.String("all"),
|
||||
Labels: []string{name},
|
||||
}
|
||||
|
||||
issues, _, err := g.api().Issues.ListProjectIssues(g.reponame, options)
|
||||
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error during FindOpen")
|
||||
}
|
||||
|
||||
return toTickets(issues), nil
|
||||
}
|
||||
|
||||
func (g *gitlabPlugin) LinkFor(t *model.Ticket) string {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (g *gitlabPlugin) Create(ticket *model.Ticket, labels []string) error {
|
||||
options := &gitlab.CreateIssueOptions{
|
||||
Title: gitlab.String(ticket.Name),
|
||||
Description: gitlab.String(ticket.Body),
|
||||
Labels: labels,
|
||||
}
|
||||
_, _, err := g.api().Issues.CreateIssue(g.reponame, options)
|
||||
return err
|
||||
}
|
||||
|
||||
func toTickets(issues []*gitlab.Issue) []*model.Ticket {
|
||||
var tickets []*model.Ticket
|
||||
for _, i := range issues {
|
||||
tickets = append(tickets, toTicket(i))
|
||||
}
|
||||
return tickets
|
||||
}
|
||||
|
||||
func toTicket(i *gitlab.Issue) *model.Ticket {
|
||||
t := &model.Ticket{Attributes: make(map[string]interface{})}
|
||||
t.ID = strconv.Itoa(i.ID)
|
||||
t.Name = i.Title
|
||||
t.Body = i.Description
|
||||
t.CreatedAt = i.CreatedAt
|
||||
t.State = toState(i.State)
|
||||
|
||||
for _, l := range i.Labels {
|
||||
if l == "audit" {
|
||||
t.SetBool("comply-audit")
|
||||
}
|
||||
if l == "procedure" {
|
||||
t.SetBool("comply-procedure")
|
||||
}
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func toState(state string) model.TicketState {
|
||||
switch state {
|
||||
case "closed":
|
||||
return model.Closed
|
||||
}
|
||||
return model.Open
|
||||
}
|
||||
|
||||
func ss(s *string) string {
|
||||
if s == nil {
|
||||
return ""
|
||||
}
|
||||
return *s
|
||||
}
|
||||
9
internal/gitlab/gitlab_test.go
Normal file
9
internal/gitlab/gitlab_test.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package gitlab
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGitlab(t *testing.T) {
|
||||
createOne()
|
||||
}
|
||||
193
internal/jira/jira.go
Normal file
193
internal/jira/jira.go
Normal file
@@ -0,0 +1,193 @@
|
||||
package jira
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/strongdm/comply/internal/model"
|
||||
|
||||
jira "github.com/andygrunwald/go-jira"
|
||||
)
|
||||
|
||||
const (
|
||||
cfgUsername = "username"
|
||||
cfgPassword = "password"
|
||||
cfgURL = "url"
|
||||
cfgProject = "project"
|
||||
cfgTaskType = "taskType"
|
||||
)
|
||||
|
||||
var prompts = map[string]string{
|
||||
cfgUsername: "Jira Username",
|
||||
cfgPassword: "Jira Password",
|
||||
cfgURL: "Jira URL",
|
||||
cfgProject: "Jira Project Code",
|
||||
cfgTaskType: "Jira Task Type",
|
||||
}
|
||||
|
||||
// Prompts are human-readable configuration element names
|
||||
func (j *jiraPlugin) Prompts() map[string]string {
|
||||
return prompts
|
||||
}
|
||||
|
||||
// Register causes the Github plugin to register itself
|
||||
func Register() {
|
||||
model.Register(model.Jira, &jiraPlugin{})
|
||||
}
|
||||
|
||||
type jiraPlugin struct {
|
||||
username string
|
||||
password string
|
||||
url string
|
||||
project string
|
||||
taskType string
|
||||
|
||||
clientMu sync.Mutex
|
||||
client *jira.Client
|
||||
}
|
||||
|
||||
func (j *jiraPlugin) api() *jira.Client {
|
||||
j.clientMu.Lock()
|
||||
defer j.clientMu.Unlock()
|
||||
|
||||
if j.client == nil {
|
||||
tp := jira.BasicAuthTransport{
|
||||
Username: j.username,
|
||||
Password: j.password,
|
||||
}
|
||||
|
||||
client, _ := jira.NewClient(tp.Client(), j.url)
|
||||
j.client = client
|
||||
}
|
||||
return j.client
|
||||
}
|
||||
|
||||
func (j *jiraPlugin) Get(ID string) (*model.Ticket, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (j *jiraPlugin) Configured() bool {
|
||||
return j.username != "" && j.password != "" && j.url != "" && j.project != "" && j.taskType != ""
|
||||
}
|
||||
|
||||
func (j *jiraPlugin) Links() model.TicketLinks {
|
||||
links := model.TicketLinks{}
|
||||
links.ProcedureAll = fmt.Sprintf("%s/issues/?jql=labels+=+comply-procedure", j.url)
|
||||
links.ProcedureOpen = fmt.Sprintf("%s/issues/?jql=labels+=+comply-procedure+AND+resolution+=+Unresolved", j.url)
|
||||
// links.AuditAll = fmt.Sprintf("%s/issues?q=is%3Aissue+is%3Aopen+label%3Acomply+label%3Aaudit", j.url)
|
||||
// links.AuditOpen = fmt.Sprintf("%s/issues?q=is%3Aissue+is%3Aopen+label%3Acomply+label%3Aaudit", j.url)
|
||||
return links
|
||||
}
|
||||
|
||||
func (j *jiraPlugin) Configure(cfg map[string]interface{}) error {
|
||||
var err error
|
||||
|
||||
if j.username, err = getCfg(cfg, cfgUsername); err != nil {
|
||||
return err
|
||||
}
|
||||
if j.password, err = getCfg(cfg, cfgPassword); err != nil {
|
||||
return err
|
||||
}
|
||||
if j.url, err = getCfg(cfg, cfgURL); err != nil {
|
||||
return err
|
||||
}
|
||||
if j.project, err = getCfg(cfg, cfgProject); err != nil {
|
||||
return err
|
||||
}
|
||||
if j.taskType, err = getCfg(cfg, cfgTaskType); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getCfg(cfg map[string]interface{}, k string) (string, error) {
|
||||
v, ok := cfg[k]
|
||||
if !ok {
|
||||
return "", errors.New("Missing key: " + k)
|
||||
}
|
||||
|
||||
vS, ok := v.(string)
|
||||
if !ok {
|
||||
return "", errors.New("Malformatted key: " + k)
|
||||
}
|
||||
return vS, nil
|
||||
}
|
||||
|
||||
func (j *jiraPlugin) FindOpen() ([]*model.Ticket, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (j *jiraPlugin) FindByTag(name, value string) ([]*model.Ticket, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (j *jiraPlugin) FindByTagName(name string) ([]*model.Ticket, error) {
|
||||
issues, _, err := j.api().Issue.Search("labels=comply", &jira.SearchOptions{MaxResults: 1000})
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "unable to fetch Jira issues")
|
||||
}
|
||||
return toTickets(issues), nil
|
||||
}
|
||||
|
||||
func (j *jiraPlugin) LinkFor(t *model.Ticket) string {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (j *jiraPlugin) Create(ticket *model.Ticket, labels []string) error {
|
||||
i := jira.Issue{
|
||||
Fields: &jira.IssueFields{
|
||||
Type: jira.IssueType{
|
||||
Name: j.taskType,
|
||||
},
|
||||
Project: jira.Project{
|
||||
Key: j.project,
|
||||
},
|
||||
Summary: ticket.Name,
|
||||
Description: ticket.Body,
|
||||
Labels: labels,
|
||||
},
|
||||
}
|
||||
|
||||
_, _, err := j.api().Issue.Create(&i)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "unable to create ticket")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func toTickets(issues []jira.Issue) []*model.Ticket {
|
||||
var tickets []*model.Ticket
|
||||
for _, i := range issues {
|
||||
tickets = append(tickets, toTicket(&i))
|
||||
}
|
||||
return tickets
|
||||
}
|
||||
|
||||
func toTicket(i *jira.Issue) *model.Ticket {
|
||||
t := &model.Ticket{Attributes: make(map[string]interface{})}
|
||||
t.ID = i.ID
|
||||
t.Name = i.Fields.Summary
|
||||
t.Body = i.Fields.Description
|
||||
createdAt := time.Time(i.Fields.Created)
|
||||
t.CreatedAt = &createdAt
|
||||
t.State = toState(i.Fields.Resolution)
|
||||
|
||||
for _, l := range i.Fields.Labels {
|
||||
t.SetBool(l)
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func toState(status *jira.Resolution) model.TicketState {
|
||||
if status == nil {
|
||||
return model.Open
|
||||
}
|
||||
switch status.Name {
|
||||
case "Done":
|
||||
return model.Closed
|
||||
}
|
||||
return model.Open
|
||||
}
|
||||
9
internal/jira/jira_test.go
Normal file
9
internal/jira/jira_test.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package jira
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestJira(t *testing.T) {
|
||||
createOne()
|
||||
}
|
||||
@@ -2,7 +2,7 @@ package model
|
||||
|
||||
import "time"
|
||||
|
||||
type Policy struct {
|
||||
type Document struct {
|
||||
Name string `yaml:"name"`
|
||||
Acronym string `yaml:"acronym"`
|
||||
|
||||
@@ -91,8 +91,8 @@ func ReadStandards() ([]*Standard, error) {
|
||||
}
|
||||
|
||||
// ReadNarratives loads narrative descriptions from the filesystem.
|
||||
func ReadNarratives() ([]*Narrative, error) {
|
||||
var narratives []*Narrative
|
||||
func ReadNarratives() ([]*Document, error) {
|
||||
var narratives []*Document
|
||||
|
||||
files, err := path.Narratives()
|
||||
if err != nil {
|
||||
@@ -100,7 +100,7 @@ func ReadNarratives() ([]*Narrative, error) {
|
||||
}
|
||||
|
||||
for _, f := range files {
|
||||
n := &Narrative{}
|
||||
n := &Document{}
|
||||
mdmd := loadMDMD(f.FullPath)
|
||||
err = yaml.Unmarshal([]byte(mdmd.yaml), &n)
|
||||
if err != nil {
|
||||
@@ -141,8 +141,8 @@ func ReadProcedures() ([]*Procedure, error) {
|
||||
}
|
||||
|
||||
// ReadPolicies loads policy documents from the filesystem.
|
||||
func ReadPolicies() ([]*Policy, error) {
|
||||
var policies []*Policy
|
||||
func ReadPolicies() ([]*Document, error) {
|
||||
var policies []*Document
|
||||
|
||||
files, err := path.Policies()
|
||||
if err != nil {
|
||||
@@ -150,7 +150,7 @@ func ReadPolicies() ([]*Policy, error) {
|
||||
}
|
||||
|
||||
for _, f := range files {
|
||||
p := &Policy{}
|
||||
p := &Document{}
|
||||
mdmd := loadMDMD(f.FullPath)
|
||||
err = yaml.Unmarshal([]byte(mdmd.yaml), &p)
|
||||
if err != nil {
|
||||
|
||||
@@ -2,8 +2,8 @@ package model
|
||||
|
||||
type Data struct {
|
||||
Standards []*Standard
|
||||
Narratives []*Narrative
|
||||
Policies []*Policy
|
||||
Narratives []*Document
|
||||
Policies []*Document
|
||||
Procedures []*Procedure
|
||||
Tickets []*Ticket
|
||||
Audits []*Audit
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
package model
|
||||
|
||||
import "time"
|
||||
|
||||
type Narrative struct {
|
||||
Name string `yaml:"name"`
|
||||
Acronym string `yaml:"acronym"`
|
||||
|
||||
Revisions []Revision `yaml:"majorRevisions"`
|
||||
Satisfies Satisfaction `yaml:"satisfies"`
|
||||
FullPath string
|
||||
OutputFilename string
|
||||
ModifiedAt time.Time
|
||||
Body string
|
||||
}
|
||||
@@ -17,11 +17,13 @@ type TicketSystem string
|
||||
|
||||
const (
|
||||
// Jira from Atlassian.
|
||||
Jira = TicketSystem("jira")
|
||||
Jira = TicketSystem(config.Jira)
|
||||
// GitHub from GitHub.
|
||||
GitHub = TicketSystem("github")
|
||||
GitHub = TicketSystem(config.GitHub)
|
||||
// GitLab from GitLab.
|
||||
GitLab = TicketSystem(config.GitLab)
|
||||
// NoTickets indicates no ticketing system integration.
|
||||
NoTickets = TicketSystem("none")
|
||||
NoTickets = TicketSystem(config.NoTickets)
|
||||
)
|
||||
|
||||
type TicketLinks struct {
|
||||
@@ -50,6 +52,10 @@ func GetPlugin(ts TicketSystem) TicketPlugin {
|
||||
tsPluginsMu.Lock()
|
||||
defer tsPluginsMu.Unlock()
|
||||
|
||||
if ts == NoTickets {
|
||||
return &noopTicketSystem{}
|
||||
}
|
||||
|
||||
tp, ok := tsPlugins[ts]
|
||||
if !ok {
|
||||
panic("Unknown ticket system: " + ts)
|
||||
@@ -81,7 +87,10 @@ func GetPlugin(ts TicketSystem) TicketPlugin {
|
||||
}
|
||||
cfgStringed[kS] = v
|
||||
}
|
||||
tp.Configure(cfgStringed)
|
||||
err := tp.Configure(cfgStringed)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("Configuration error `%s` in project YAML", err))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -100,3 +109,36 @@ func Register(ts TicketSystem, plugin TicketPlugin) {
|
||||
|
||||
tsPlugins[ts] = plugin
|
||||
}
|
||||
|
||||
type noopTicketSystem struct{}
|
||||
|
||||
func (*noopTicketSystem) Get(ID string) (*Ticket, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (*noopTicketSystem) FindOpen() ([]*Ticket, error) {
|
||||
return []*Ticket{}, nil
|
||||
}
|
||||
func (*noopTicketSystem) FindByTag(name, value string) ([]*Ticket, error) {
|
||||
return []*Ticket{}, nil
|
||||
}
|
||||
func (*noopTicketSystem) FindByTagName(name string) ([]*Ticket, error) {
|
||||
return []*Ticket{}, nil
|
||||
}
|
||||
func (*noopTicketSystem) Create(ticket *Ticket, labels []string) error {
|
||||
return nil
|
||||
}
|
||||
func (*noopTicketSystem) Configure(map[string]interface{}) error {
|
||||
return nil
|
||||
}
|
||||
func (*noopTicketSystem) Prompts() map[string]string {
|
||||
return make(map[string]string)
|
||||
}
|
||||
func (*noopTicketSystem) Links() TicketLinks {
|
||||
return TicketLinks{}
|
||||
}
|
||||
func (*noopTicketSystem) LinkFor(ticket *Ticket) string {
|
||||
return ""
|
||||
}
|
||||
func (*noopTicketSystem) Configured() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -69,10 +69,10 @@ func (g *githubPlugin) Configured() bool {
|
||||
|
||||
func (g *githubPlugin) Links() model.TicketLinks {
|
||||
links := model.TicketLinks{}
|
||||
links.AuditAll = fmt.Sprintf("https://github.com/%s/%s/issues?q=is%3Aissue+is%3Aopen+label%3Acomply+label%3Aaudit", g.username, g.reponame)
|
||||
links.AuditOpen = fmt.Sprintf("https://github.com/%s/%s/issues?q=is%3Aissue+is%3Aopen+label%3Acomply+label%3Aaudit", g.username, g.reponame)
|
||||
links.ProcedureAll = fmt.Sprintf("https://github.com/%s/%s/issues?q=is%3Aissue+label%3Acomply+label%3Aprocedure", g.username, g.reponame)
|
||||
links.ProcedureOpen = fmt.Sprintf("https://github.com/%s/%s/issues?q=is%3Aissue+is%3Aopen+label%3Acomply+label%3Aprocedure", g.username, g.reponame)
|
||||
links.AuditAll = fmt.Sprintf("https://github.com/%s/%s/issues?q=is%%3Aissue+is%%3Aopen+label%%3Acomply+label%%3Aaudit", g.username, g.reponame)
|
||||
links.AuditOpen = fmt.Sprintf("https://github.com/%s/%s/issues?q=is%%3Aissue+is%%3Aopen+label%%3Acomply+label%%3Aaudit", g.username, g.reponame)
|
||||
links.ProcedureAll = fmt.Sprintf("https://github.com/%s/%s/issues?q=is%%3Aissue+label%%3Acomply+label%%3Acomply-procedure", g.username, g.reponame)
|
||||
links.ProcedureOpen = fmt.Sprintf("https://github.com/%s/%s/issues?q=is%%3Aissue+is%%3Aopen+label%%3Acomply+label%%3Acomply-procedure", g.username, g.reponame)
|
||||
return links
|
||||
}
|
||||
|
||||
@@ -135,7 +135,8 @@ func (g *githubPlugin) FindByTagName(name string) ([]*model.Ticket, error) {
|
||||
}
|
||||
|
||||
func (g *githubPlugin) LinkFor(t *model.Ticket) string {
|
||||
return fmt.Sprintf("https://github.com/strongdm/comply/issues/%s", t.ID)
|
||||
// return fmt.Sprintf("https://github.com/strongdm/comply/issues/%s", t.ID)
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (g *githubPlugin) Create(ticket *model.Ticket, labels []string) error {
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/strongdm/comply/internal/config"
|
||||
"github.com/strongdm/comply/internal/model"
|
||||
)
|
||||
@@ -32,8 +33,8 @@ type renderData struct {
|
||||
Name string
|
||||
Project *project
|
||||
Stats *stats
|
||||
Narratives []*model.Narrative
|
||||
Policies []*model.Policy
|
||||
Narratives []*model.Document
|
||||
Policies []*model.Document
|
||||
Procedures []*model.Procedure
|
||||
Standards []*model.Standard
|
||||
Tickets []*model.Ticket
|
||||
@@ -93,8 +94,12 @@ func load() (*model.Data, *renderData, error) {
|
||||
rd.Name = project.OrganizationName
|
||||
rd.Controls = controls
|
||||
|
||||
// TODO: unhardcode plugin
|
||||
tp := model.GetPlugin(model.GitHub)
|
||||
ts, err := config.Config().TicketSystem()
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "error in ticket system configuration")
|
||||
}
|
||||
|
||||
tp := model.GetPlugin(model.TicketSystem(ts))
|
||||
if tp.Configured() {
|
||||
links := tp.Links()
|
||||
rd.Links = &links
|
||||
@@ -133,7 +138,7 @@ func addStats(modelData *model.Data, renderData *renderData) {
|
||||
}
|
||||
|
||||
if t.State == model.Open {
|
||||
if t.Bool("procedure") {
|
||||
if t.Bool("comply-procedure") {
|
||||
stats.ProcedureOpen++
|
||||
if t.CreatedAt != nil {
|
||||
age := int(time.Since(*t.CreatedAt).Hours() / float64(24))
|
||||
|
||||
@@ -2,7 +2,6 @@ package render
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
@@ -12,89 +11,34 @@ import (
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/client"
|
||||
"os/exec"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/strongdm/comply/internal/config"
|
||||
"github.com/strongdm/comply/internal/model"
|
||||
)
|
||||
|
||||
// TODO: refactor and eliminate duplication among narrative, policy renderers
|
||||
func renderPolicyToDisk(wg *sync.WaitGroup, errOutputCh chan error, data *renderData, policy *model.Policy, live bool) {
|
||||
func renderToFilesystem(wg *sync.WaitGroup, errOutputCh chan error, data *renderData, doc *model.Document, live bool) {
|
||||
// only files that have been touched
|
||||
if !isNewer(policy.FullPath, policy.ModifiedAt) {
|
||||
if !isNewer(doc.FullPath, doc.ModifiedAt) {
|
||||
return
|
||||
}
|
||||
recordModified(policy.FullPath, policy.ModifiedAt)
|
||||
|
||||
ctx := context.Background()
|
||||
cli, err := client.NewEnvClient()
|
||||
if err != nil {
|
||||
errOutputCh <- errors.Wrap(err, "unable to read Docker environment")
|
||||
return
|
||||
}
|
||||
|
||||
pwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
errOutputCh <- errors.Wrap(err, "unable to get workding directory")
|
||||
return
|
||||
}
|
||||
|
||||
hc := &container.HostConfig{
|
||||
Binds: []string{pwd + ":/source"},
|
||||
}
|
||||
recordModified(doc.FullPath, doc.ModifiedAt)
|
||||
|
||||
wg.Add(1)
|
||||
go func(p *model.Policy) {
|
||||
go func(p *model.Document) {
|
||||
defer wg.Done()
|
||||
|
||||
outputFilename := p.OutputFilename
|
||||
// save preprocessed markdown
|
||||
err = preprocessPolicy(data, p, filepath.Join(".", "output", outputFilename+".md"))
|
||||
err := preprocessDoc(data, p, filepath.Join(".", "output", outputFilename+".md"))
|
||||
if err != nil {
|
||||
errOutputCh <- errors.Wrap(err, "unable to preprocess")
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := cli.ContainerCreate(ctx, &container.Config{
|
||||
Image: "strongdm/pandoc",
|
||||
Cmd: []string{"--smart", "--toc", "-N", "--template=/source/templates/default.latex", "-o",
|
||||
fmt.Sprintf("/source/output/%s", outputFilename),
|
||||
fmt.Sprintf("/source/output/%s.md", outputFilename),
|
||||
},
|
||||
}, hc, nil, "")
|
||||
if err != nil {
|
||||
errOutputCh <- errors.Wrap(err, "unable to create Docker container")
|
||||
return
|
||||
}
|
||||
|
||||
defer func() {
|
||||
timeout := 2 * time.Second
|
||||
cli.ContainerStop(ctx, resp.ID, &timeout)
|
||||
err := cli.ContainerRemove(ctx, resp.ID, types.ContainerRemoveOptions{Force: true})
|
||||
if err != nil {
|
||||
errOutputCh <- errors.Wrap(err, "unable to remove container")
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
if err := cli.ContainerStart(ctx, resp.ID, types.ContainerStartOptions{}); err != nil {
|
||||
errOutputCh <- errors.Wrap(err, "unable to start Docker container")
|
||||
return
|
||||
}
|
||||
|
||||
_, err = cli.ContainerWait(ctx, resp.ID)
|
||||
if err != nil {
|
||||
errOutputCh <- errors.Wrap(err, "error awaiting Docker container")
|
||||
return
|
||||
}
|
||||
|
||||
_, err = cli.ContainerLogs(ctx, resp.ID, types.ContainerLogsOptions{ShowStdout: true})
|
||||
if err != nil {
|
||||
errOutputCh <- errors.Wrap(err, "error reading Docker container logs")
|
||||
return
|
||||
}
|
||||
pandoc(outputFilename, errOutputCh)
|
||||
|
||||
// remove preprocessed markdown
|
||||
err = os.Remove(filepath.Join(".", "output", outputFilename+".md"))
|
||||
@@ -108,10 +52,44 @@ func renderPolicyToDisk(wg *sync.WaitGroup, errOutputCh chan error, data *render
|
||||
rel = p.FullPath
|
||||
}
|
||||
fmt.Printf("%s -> %s\n", rel, filepath.Join("output", p.OutputFilename))
|
||||
}(policy)
|
||||
}(doc)
|
||||
}
|
||||
|
||||
func preprocessPolicy(data *renderData, pol *model.Policy, fullPath string) error {
|
||||
func getGitApprovalInfo(pol *model.Document) (string, error) {
|
||||
cfg := config.Config()
|
||||
|
||||
// if no approved branch specified in config.yaml, then nothing gets added to the document
|
||||
if cfg.ApprovedBranch == "" {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// Decide whether we are on the git branch that contains the approved policies
|
||||
gitBranchArgs := []string{"rev-parse", "--abbrev-ref", "HEAD"}
|
||||
gitBranchCmd := exec.Command("git", gitBranchArgs...)
|
||||
gitBranchInfo, err := gitBranchCmd.CombinedOutput()
|
||||
if err != nil {
|
||||
fmt.Println(string(gitBranchInfo))
|
||||
return "", errors.Wrap(err, "error looking up git branch")
|
||||
}
|
||||
|
||||
// if on a different branch than the approved branch, then nothing gets added to the document
|
||||
if strings.Compare(strings.TrimSpace(fmt.Sprintf("%s", gitBranchInfo)), cfg.ApprovedBranch) != 0 {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// Grab information related to commit, so that we can put approval information in the document
|
||||
gitArgs := []string{"log", "-n", "1", "--pretty=format:Last edit made by %an (%aE) on %aD.\n\nApproved by %cn (%cE) on %cD in commit %H.", "--", pol.FullPath}
|
||||
cmd := exec.Command("git", gitArgs...)
|
||||
gitApprovalInfo, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
fmt.Println(string(gitApprovalInfo))
|
||||
return "", errors.Wrap(err, "error looking up git committer and author data")
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s\n%s", "# Authorship and Approval", gitApprovalInfo), nil
|
||||
}
|
||||
|
||||
func preprocessDoc(data *renderData, pol *model.Document, fullPath string) error {
|
||||
cfg := config.Config()
|
||||
|
||||
var w bytes.Buffer
|
||||
@@ -147,6 +125,11 @@ func preprocessPolicy(data *renderData, pol *model.Policy, fullPath string) erro
|
||||
revisionTable = fmt.Sprintf("|Date|Comment|\n|---+--------------------------------------------|\n%s\nTable: Document history\n", rows)
|
||||
}
|
||||
|
||||
gitApprovalInfo, err := getGitApprovalInfo(pol)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
doc := fmt.Sprintf(`%% %s
|
||||
%% %s
|
||||
%% %s
|
||||
@@ -162,6 +145,8 @@ foot-content: "%s confidential %d"
|
||||
%s
|
||||
|
||||
\newpage
|
||||
%s
|
||||
|
||||
%s`,
|
||||
pol.Name,
|
||||
cfg.Name,
|
||||
@@ -172,6 +157,7 @@ foot-content: "%s confidential %d"
|
||||
satisfiesTable,
|
||||
revisionTable,
|
||||
body,
|
||||
gitApprovalInfo,
|
||||
)
|
||||
err = ioutil.WriteFile(fullPath, []byte(doc), os.FileMode(0644))
|
||||
if err != nil {
|
||||
@@ -82,7 +82,7 @@ func html(output string, live bool, errCh chan error, wg *sync.WaitGroup) {
|
||||
if live {
|
||||
if !opened {
|
||||
opened = true
|
||||
open.Run("output/index.html")
|
||||
open.Run(filepath.Join(".", "output", "index.html"))
|
||||
}
|
||||
} else {
|
||||
wg.Done()
|
||||
|
||||
@@ -1,184 +0,0 @@
|
||||
package render
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/strongdm/comply/internal/config"
|
||||
"github.com/strongdm/comply/internal/model"
|
||||
)
|
||||
|
||||
// TODO: refactor and eliminate duplication among narrative, policy renderers
|
||||
func renderNarrativeToDisk(wg *sync.WaitGroup, errOutputCh chan error, data *renderData, narrative *model.Narrative, live bool) {
|
||||
// only files that have been touched
|
||||
if !isNewer(narrative.FullPath, narrative.ModifiedAt) {
|
||||
return
|
||||
}
|
||||
recordModified(narrative.FullPath, narrative.ModifiedAt)
|
||||
|
||||
ctx := context.Background()
|
||||
cli, err := client.NewEnvClient()
|
||||
if err != nil {
|
||||
errOutputCh <- errors.Wrap(err, "unable to read Docker environment")
|
||||
return
|
||||
}
|
||||
|
||||
pwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
errOutputCh <- errors.Wrap(err, "unable to get workding directory")
|
||||
return
|
||||
}
|
||||
|
||||
hc := &container.HostConfig{
|
||||
Binds: []string{pwd + ":/source"},
|
||||
}
|
||||
|
||||
wg.Add(1)
|
||||
go func(p *model.Narrative) {
|
||||
defer wg.Done()
|
||||
|
||||
outputFilename := p.OutputFilename
|
||||
// save preprocessed markdown
|
||||
err = preprocessNarrative(data, p, filepath.Join(".", "output", outputFilename+".md"))
|
||||
if err != nil {
|
||||
errOutputCh <- errors.Wrap(err, "unable to preprocess")
|
||||
return
|
||||
}
|
||||
|
||||
cmd := []string{"--smart", "--toc", "-N", "--template=/source/templates/default.latex", "-o",
|
||||
fmt.Sprintf("/source/output/%s", outputFilename),
|
||||
fmt.Sprintf("/source/output/%s.md", outputFilename)}
|
||||
|
||||
resp, err := cli.ContainerCreate(ctx, &container.Config{
|
||||
Image: "strongdm/pandoc",
|
||||
Cmd: cmd},
|
||||
hc, nil, "")
|
||||
|
||||
if err != nil {
|
||||
errOutputCh <- errors.Wrap(err, "unable to create Docker container")
|
||||
return
|
||||
}
|
||||
|
||||
defer func() {
|
||||
timeout := 2 * time.Second
|
||||
cli.ContainerStop(ctx, resp.ID, &timeout)
|
||||
err := cli.ContainerRemove(ctx, resp.ID, types.ContainerRemoveOptions{Force: true})
|
||||
if err != nil {
|
||||
errOutputCh <- errors.Wrap(err, "unable to remove container")
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
if err := cli.ContainerStart(ctx, resp.ID, types.ContainerStartOptions{}); err != nil {
|
||||
errOutputCh <- errors.Wrap(err, "unable to start Docker container")
|
||||
return
|
||||
}
|
||||
|
||||
_, err = cli.ContainerWait(ctx, resp.ID)
|
||||
if err != nil {
|
||||
errOutputCh <- errors.Wrap(err, "error awaiting Docker container")
|
||||
return
|
||||
}
|
||||
|
||||
_, err = cli.ContainerLogs(ctx, resp.ID, types.ContainerLogsOptions{ShowStdout: true})
|
||||
if err != nil {
|
||||
errOutputCh <- errors.Wrap(err, "error reading Docker container logs")
|
||||
return
|
||||
}
|
||||
|
||||
// remove preprocessed markdown
|
||||
err = os.Remove(filepath.Join(".", "output", outputFilename+".md"))
|
||||
if err != nil {
|
||||
errOutputCh <- err
|
||||
return
|
||||
}
|
||||
|
||||
rel, err := filepath.Rel(config.ProjectRoot(), p.FullPath)
|
||||
if err != nil {
|
||||
rel = p.FullPath
|
||||
}
|
||||
fmt.Printf("%s -> %s\n", rel, filepath.Join("output", p.OutputFilename))
|
||||
|
||||
}(narrative)
|
||||
}
|
||||
|
||||
func preprocessNarrative(data *renderData, pol *model.Narrative, fullPath string) error {
|
||||
cfg := config.Config()
|
||||
|
||||
var w bytes.Buffer
|
||||
bodyTemplate, err := template.New("body").Parse(pol.Body)
|
||||
if err != nil {
|
||||
w.WriteString(fmt.Sprintf("# Error processing template:\n\n%s\n", err.Error()))
|
||||
} else {
|
||||
bodyTemplate.Execute(&w, data)
|
||||
}
|
||||
body := w.String()
|
||||
|
||||
revisionTable := ""
|
||||
satisfiesTable := ""
|
||||
|
||||
// ||Date|Comment|
|
||||
// |---+------|
|
||||
// | 4 Jan 2018 | Initial Version |
|
||||
// Table: Document history
|
||||
|
||||
if len(pol.Satisfies) > 0 {
|
||||
rows := ""
|
||||
for standard, keys := range pol.Satisfies {
|
||||
rows += fmt.Sprintf("| %s | %s |\n", standard, strings.Join(keys, ", "))
|
||||
}
|
||||
satisfiesTable = fmt.Sprintf("|Standard|Controls Satisfied|\n|-------+--------------------------------------------|\n%s\nTable: Control satisfaction\n", rows)
|
||||
}
|
||||
|
||||
if len(pol.Revisions) > 0 {
|
||||
rows := ""
|
||||
for _, rev := range pol.Revisions {
|
||||
rows += fmt.Sprintf("| %s | %s |\n", rev.Date, rev.Comment)
|
||||
}
|
||||
revisionTable = fmt.Sprintf("|Date|Comment|\n|---+--------------------------------------------|\n%s\nTable: Document history\n", rows)
|
||||
}
|
||||
|
||||
doc := fmt.Sprintf(`%% %s
|
||||
%% %s
|
||||
%% %s
|
||||
|
||||
---
|
||||
header-includes: yes
|
||||
head-content: "%s"
|
||||
foot-content: "%s confidential %d"
|
||||
---
|
||||
|
||||
%s
|
||||
|
||||
%s
|
||||
|
||||
\newpage
|
||||
%s`,
|
||||
pol.Name,
|
||||
cfg.Name,
|
||||
fmt.Sprintf("%s %d", pol.ModifiedAt.Month().String(), pol.ModifiedAt.Year()),
|
||||
pol.Name,
|
||||
cfg.Name,
|
||||
time.Now().Year(),
|
||||
satisfiesTable,
|
||||
revisionTable,
|
||||
body,
|
||||
)
|
||||
err = ioutil.WriteFile(fullPath, []byte(doc), os.FileMode(0644))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "unable to write preprocessed narrative to disk")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
101
internal/render/pandoc.go
Normal file
101
internal/render/pandoc.go
Normal file
@@ -0,0 +1,101 @@
|
||||
package render
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/strongdm/comply/internal/config"
|
||||
)
|
||||
|
||||
var pandocArgs = []string{"-f", "markdown+smart", "--toc", "-N", "--template", "templates/default.latex", "-o"}
|
||||
|
||||
func pandoc(outputFilename string, errOutputCh chan error) {
|
||||
if config.WhichPandoc() == config.UsePandoc {
|
||||
err := pandocPandoc(outputFilename)
|
||||
if err != nil {
|
||||
errOutputCh <- err
|
||||
}
|
||||
} else {
|
||||
dockerPandoc(outputFilename, errOutputCh)
|
||||
}
|
||||
}
|
||||
|
||||
func dockerPandoc(outputFilename string, errOutputCh chan error) {
|
||||
pandocCmd := append(pandocArgs, fmt.Sprintf("/source/output/%s", outputFilename), fmt.Sprintf("/source/output/%s.md", outputFilename))
|
||||
ctx := context.Background()
|
||||
cli, err := client.NewEnvClient()
|
||||
if err != nil {
|
||||
errOutputCh <- errors.Wrap(err, "unable to read Docker environment")
|
||||
return
|
||||
}
|
||||
|
||||
pwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
errOutputCh <- errors.Wrap(err, "unable to get workding directory")
|
||||
return
|
||||
}
|
||||
|
||||
hc := &container.HostConfig{
|
||||
Binds: []string{pwd + ":/source"},
|
||||
}
|
||||
|
||||
resp, err := cli.ContainerCreate(ctx, &container.Config{
|
||||
Image: "strongdm/pandoc",
|
||||
Cmd: pandocCmd},
|
||||
hc, nil, "")
|
||||
|
||||
if err != nil {
|
||||
errOutputCh <- errors.Wrap(err, "unable to create Docker container")
|
||||
return
|
||||
}
|
||||
|
||||
defer func() {
|
||||
timeout := 2 * time.Second
|
||||
cli.ContainerStop(ctx, resp.ID, &timeout)
|
||||
err := cli.ContainerRemove(ctx, resp.ID, types.ContainerRemoveOptions{Force: true})
|
||||
if err != nil {
|
||||
errOutputCh <- errors.Wrap(err, "unable to remove container")
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
if err := cli.ContainerStart(ctx, resp.ID, types.ContainerStartOptions{}); err != nil {
|
||||
errOutputCh <- errors.Wrap(err, "unable to start Docker container")
|
||||
return
|
||||
}
|
||||
|
||||
_, err = cli.ContainerWait(ctx, resp.ID)
|
||||
if err != nil {
|
||||
errOutputCh <- errors.Wrap(err, "error awaiting Docker container")
|
||||
return
|
||||
}
|
||||
|
||||
_, err = cli.ContainerLogs(ctx, resp.ID, types.ContainerLogsOptions{ShowStdout: true})
|
||||
if err != nil {
|
||||
errOutputCh <- errors.Wrap(err, "error reading Docker container logs")
|
||||
return
|
||||
}
|
||||
|
||||
if _, err = os.Stat(fmt.Sprintf("output/%s", outputFilename)); err != nil && os.IsNotExist(err) {
|
||||
errOutputCh <- errors.Wrap(err, "output not generated; verify your Docker image is up to date")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 🐼
|
||||
func pandocPandoc(outputFilename string) error {
|
||||
cmd := exec.Command("pandoc", append(pandocArgs, fmt.Sprintf("output/%s", outputFilename), fmt.Sprintf("output/%s.md", outputFilename))...)
|
||||
outputRaw, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
fmt.Println(string(outputRaw))
|
||||
return errors.Wrap(err, "error calling pandoc")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -25,7 +25,7 @@ func pdf(output string, live bool, errCh chan error, wg *sync.WaitGroup) {
|
||||
return
|
||||
}
|
||||
for _, policy := range policies {
|
||||
renderPolicyToDisk(&pdfWG, errOutputCh, data, policy, live)
|
||||
renderToFilesystem(&pdfWG, errOutputCh, data, policy, live)
|
||||
}
|
||||
|
||||
narratives, err := model.ReadNarratives()
|
||||
@@ -35,7 +35,7 @@ func pdf(output string, live bool, errCh chan error, wg *sync.WaitGroup) {
|
||||
}
|
||||
|
||||
for _, narrative := range narratives {
|
||||
renderNarrativeToDisk(&pdfWG, errOutputCh, data, narrative, live)
|
||||
renderToFilesystem(&pdfWG, errOutputCh, data, narrative, live)
|
||||
}
|
||||
|
||||
pdfWG.Wait()
|
||||
|
||||
@@ -137,7 +137,7 @@ func complyBlankReadmeMd() (*asset, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "comply-blank/README.md", size: 1965, mode: os.FileMode(420), modTime: time.Unix(1526687060, 0)}
|
||||
info := bindataFileInfo{name: "comply-blank/README.md", size: 1965, mode: os.FileMode(420), modTime: time.Unix(1545086630, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
@@ -157,7 +157,7 @@ func complyBlankTodoMd() (*asset, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "comply-blank/TODO.md", size: 1429, mode: os.FileMode(420), modTime: time.Unix(1526687060, 0)}
|
||||
info := bindataFileInfo{name: "comply-blank/TODO.md", size: 1429, mode: os.FileMode(420), modTime: time.Unix(1545086630, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
@@ -277,7 +277,7 @@ func complyBlankTemplatesDefaultLatex() (*asset, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "comply-blank/templates/default.latex", size: 7649, mode: os.FileMode(420), modTime: time.Unix(1526687060, 0)}
|
||||
info := bindataFileInfo{name: "comply-blank/templates/default.latex", size: 7649, mode: os.FileMode(420), modTime: time.Unix(1545086630, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
@@ -297,7 +297,7 @@ func complyBlankTemplatesIndexAce() (*asset, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "comply-blank/templates/index.ace", size: 7596, mode: os.FileMode(420), modTime: time.Unix(1526687060, 0)}
|
||||
info := bindataFileInfo{name: "comply-blank/templates/index.ace", size: 7596, mode: os.FileMode(420), modTime: time.Unix(1545086630, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
@@ -317,7 +317,7 @@ func complySoc2ReadmeMd() (*asset, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "comply-soc2/README.md", size: 1965, mode: os.FileMode(420), modTime: time.Unix(1526687060, 0)}
|
||||
info := bindataFileInfo{name: "comply-soc2/README.md", size: 1965, mode: os.FileMode(420), modTime: time.Unix(1545086630, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
@@ -337,7 +337,7 @@ func complySoc2TodoMd() (*asset, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "comply-soc2/TODO.md", size: 1429, mode: os.FileMode(420), modTime: time.Unix(1526687060, 0)}
|
||||
info := bindataFileInfo{name: "comply-soc2/TODO.md", size: 1429, mode: os.FileMode(420), modTime: time.Unix(1545086630, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
@@ -357,12 +357,12 @@ func complySoc2NarrativesReadmeMd() (*asset, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "comply-soc2/narratives/README.md", size: 96, mode: os.FileMode(420), modTime: time.Unix(1526687060, 0)}
|
||||
info := bindataFileInfo{name: "comply-soc2/narratives/README.md", size: 96, mode: os.FileMode(420), modTime: time.Unix(1545086630, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
var _complySoc2NarrativesControlMd = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x84\x90\x31\x4f\xc3\x30\x10\x85\x77\xff\x8a\x27\x31\x07\x91\xb4\x95\x50\x56\xab\x12\x20\xe8\x40\xbb\x30\x9a\xe4\xa0\x47\xe3\x3b\x64\x3b\xa9\xf2\xef\x91\x23\x14\x99\x89\xc9\x4f\x9f\xdf\xb3\xdf\x9d\x38\x4f\x2d\xac\x4a\x0a\x3a\x60\x2f\x13\x07\x15\x4f\x92\x70\x70\x21\xb8\xc4\x13\x19\xd7\x05\x95\xd9\xb7\xb0\xfb\x83\x89\x2e\x71\xfc\x60\x8a\xad\x01\x4e\x47\x9b\x0f\xa0\x82\xb5\xcd\x6d\x5d\xe8\xa6\xd0\x9b\x55\x6f\x0b\xcf\xb6\xf0\xec\x0a\xbe\xfb\xc3\x37\xc6\xbb\x2f\x0d\xaf\x34\x71\x64\x95\xe5\xdb\x0a\xbd\x4b\xd4\xe2\x69\x14\xd4\x68\xee\xea\xfb\x25\xd0\xa9\xcf\xcd\x5b\x3c\x0a\x27\x76\x03\x7a\xed\xc6\x4c\x4c\x55\x55\xc6\xdc\xfc\x33\xa6\x79\xa0\x40\xb8\x12\x64\x41\x84\xeb\x79\x86\x8e\x01\xdd\x6f\x8c\x8a\xd8\xba\x06\xa4\x33\xad\x8e\x0b\xcd\x11\x03\xc7\x44\x3d\x58\x96\xab\xb7\x97\x67\xbc\x0f\xda\x5d\x72\x81\x13\xf9\xef\x21\x3f\x6d\xd5\xb3\x7c\xe2\xa8\x2a\xe6\x27\x00\x00\xff\xff\x49\x83\xc0\x0b\x83\x01\x00\x00")
|
||||
var _complySoc2NarrativesControlMd = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xb4\x56\x4d\x6f\x1b\xc9\x11\xbd\xcf\xaf\x28\x40\xc0\x26\x01\x44\x26\xf6\xee\x02\x81\x6e\x0a\xa5\x04\x0e\xbc\x96\x60\x09\xde\x83\x91\x43\xb1\xa7\x86\xac\xa8\xa7\x6b\xb6\xab\x9b\xf2\x64\xe1\xff\x1e\x54\xf7\x70\x38\xb4\x2d\x38\x97\x9c\x38\xec\x9e\xa9\x8f\xf7\x5e\x7d\x04\xec\xe9\x0a\x36\x12\x52\x14\x0f\xb7\xe1\xc0\x51\x42\x4f\x21\xc1\x3b\x8c\x11\x13\x1f\xa8\x41\x17\x25\x8c\xfd\x15\x6c\x6e\xdf\x35\x8a\x89\xb5\x63\xd2\xab\x06\xe0\xf1\x61\x63\x3f\x00\x2b\xd8\x6c\x5e\xaf\x5f\x2d\x9e\x5f\x2f\x9e\x7f\x9c\x9f\x7f\x5a\xbc\xf3\xd3\xe2\x9d\x9f\x17\xe7\x3f\x9f\x9d\xff\xd8\xf4\xf8\x6f\x89\xef\xe9\xc0\xca\x12\x8a\xdb\x15\xb4\x98\xe8\x0a\xfe\x99\x03\xbc\x82\xd7\x7f\x79\xf5\xd7\xf2\x81\x93\xde\x22\xbf\x82\x37\x81\x13\xa3\x87\x56\x5c\xb6\x93\x66\xb5\x5a\x35\xcd\xc5\x77\xd2\x6c\x1e\xf7\x04\x9d\x78\x2f\xcf\x1c\x76\x30\x44\x39\x70\x4b\x0a\x08\x2d\xa9\x8b\x3c\x24\x96\x00\xd2\x41\xda\x13\xb8\xc9\x94\xa6\x98\x5d\xca\x91\xec\xe2\xf7\xdf\xd7\xef\xb0\xa7\xcf\x9f\xd7\xd5\x18\x87\x64\x2e\xca\x27\xac\x67\x66\x58\x21\x09\x50\xc8\x3d\x45\x4c\x54\x6c\x7a\xd9\xb1\x43\x7f\x09\x83\x78\x76\xe3\x25\x60\x68\x2d\x0c\x47\x6d\x8e\xe8\x8f\x3e\x15\xd2\x1e\x13\x28\xc5\x03\x99\x91\x5e\x02\x27\x89\x27\xef\x7f\x50\xc0\x61\xf0\xec\xb0\xb8\x32\x2b\x2d\x26\x04\x25\x97\x23\xa7\x71\x0d\x9b\x3d\x86\x1d\x29\xe4\xe0\xe4\x40\x91\x5a\xd8\x8e\x16\x82\xd2\xec\x8f\x14\x38\x7c\x3b\xac\x53\x48\x97\x20\x11\x5c\xd6\x24\x3d\x45\xa0\x05\xac\x18\x09\xb0\x6d\x23\xa9\x56\xeb\x91\x7a\x6a\xb9\x44\xa4\xa0\x03\x39\xee\xd8\x59\xf8\xe6\x22\x48\xa2\x16\x5c\x89\x6a\x6d\x4c\xbd\xad\x3e\x8f\x8c\x69\xd3\xcc\xd9\x01\xf5\x83\x97\x51\x41\xe9\x40\x06\xcb\x14\xdf\x02\x1e\xb1\x10\x13\xb9\x64\x67\x1d\xb7\x14\xaa\x1c\x0c\x04\x43\x83\x82\x1a\x63\x41\x62\x8f\x1e\x64\x30\x0a\x26\x6e\x39\x29\x38\x89\x05\x87\x36\xbb\xb4\x6e\x9a\x15\xfc\x82\xa1\xc5\x24\x71\xac\x26\x28\xb8\x38\x56\x1a\x31\x41\x24\x4d\xc5\x2a\x07\xe8\xc5\x4e\xed\x8b\xec\x13\xaf\x3a\x74\x46\x0c\xe6\xb4\xb7\x10\x26\x3e\x3a\x3b\x72\x8e\xb4\x44\xea\xbc\x64\xfb\xb6\x8b\x38\x6b\xa9\x59\xc1\xb5\x4b\x7c\xe0\x34\x16\xcb\x18\xa4\x47\x3f\x1e\x99\x36\x75\x4a\x38\x46\x68\x26\x75\xd4\x44\xbd\x36\x2b\xf8\x90\x7d\xa0\x88\x5b\xf6\xf6\x71\x8f\x01\x77\x54\x08\x19\xa2\xec\x22\xf6\x06\xee\x7d\xe1\xf1\x7f\xc2\xb6\x52\xfe\x7f\x82\x16\x1e\xab\xe2\xcc\x07\x17\xbd\x39\x9f\x5b\xba\x84\x6d\xae\x02\x0a\x92\xc0\x73\xcf\x26\x8e\x24\x57\x4d\xc1\xa5\x00\x77\xac\xe4\x9a\x4a\xb3\x82\xdb\x13\x27\xf3\xd9\x5d\xd7\xb1\x23\x78\x98\x64\x7f\xba\xb8\x47\xd5\x67\x89\xed\xe2\xa4\xe6\xf9\x18\x91\x83\xc1\x3b\x5f\x7c\xa0\xd0\x4a\x3c\xfd\xff\x55\xe2\x93\x26\x5c\x3a\x32\x44\x4f\x45\xfa\x2d\x54\xf7\xa8\x50\x2a\x5d\xb2\x82\xba\x3d\xb5\xd9\x53\xbb\x2c\xb5\x45\x19\x1b\x8a\x29\x87\xda\x12\xa8\xeb\xc8\x94\x40\xc1\xb2\x96\x0e\x24\xec\xc4\x02\x3c\xd6\xf2\x4c\x4d\xed\x16\x56\xe4\xd1\xb0\x94\x0e\xe8\x40\x21\xad\xda\x68\x5f\x7f\xe1\x2b\x92\x0e\x62\x7e\x64\x36\xb4\x8a\xe4\xd1\x80\x2e\x9f\xa9\x75\xb0\xbb\x9b\xbb\x2b\xf8\x3b\x07\xf4\xfc\x1f\x9a\xda\x83\x67\x4d\xda\x34\x17\x17\xf0\x30\xe7\x31\x03\x6c\x11\x5c\xe7\x96\xd3\x0c\x08\xa9\x91\x66\xcd\x9b\x9e\x8f\xdc\x7d\xfc\x2d\x63\x4c\x14\xfd\xf8\xaf\xd3\xdd\x6c\xe3\xad\xec\x14\x3e\x3e\x13\x3d\x9d\xdd\x6f\xc6\x2d\x45\x78\xcf\xfa\x04\xd7\xaa\xa4\x5a\x44\xfd\xc7\x53\x03\x1d\x44\x95\xb7\xde\x3a\x73\x3f\x44\xe9\x59\x09\xd4\x51\xc0\xc8\xa2\x7f\xfa\xb6\xd3\x1b\x53\xed\xc6\xa3\xaa\x35\xa3\x4a\xea\xf9\x8b\x7f\x43\xf7\x94\x07\x78\x24\x4d\x86\xfa\xf9\xe5\x0d\x2b\x6a\xb2\xb0\xa8\xb4\xd1\xf1\xf4\x9e\x52\xcf\x2b\x0c\x21\xa3\x5f\xfa\xa3\x03\x3b\x52\xf8\x61\x29\xa3\x17\x00\xf9\x01\x36\x9e\x30\xc2\x5b\x79\x5e\xdd\x47\x96\x02\xce\xb5\xa7\x98\xce\xe0\xb9\x1e\x06\x3f\xc2\xdd\x03\xdc\x63\x72\x7b\x52\xf8\xd8\x4b\x48\xfb\x7a\xf9\x81\x22\x77\x63\x4d\xf3\x86\x75\x10\xb5\x92\x2e\x01\xdb\x60\x3a\x69\xf8\x8b\x10\x36\x12\xac\x40\x4f\xa4\xcc\x75\xf1\xf1\xab\x9c\xe6\x77\x7e\x39\x35\xa7\xa2\x03\x0b\xd5\xfe\x6c\xac\x4f\xec\x72\xfc\x16\xbc\xf7\x14\x28\x4d\x37\x86\xdd\xd2\xfe\xaf\x7b\x4e\xb4\x95\x4f\x27\x0f\x93\xc7\xc5\x3b\x0f\x77\x9b\xd7\x93\xe2\xe6\x53\xd3\xe6\x6d\x91\xfe\x4d\x95\xfe\x77\xe5\x79\x17\xb6\x82\xb1\x85\xdb\xd2\xfc\x88\x6a\xeb\xf8\xea\xec\x4d\x38\x18\xbd\x3b\x53\xdb\x6c\xb3\xa4\xf9\xd2\xe5\x9b\xe0\x4a\x87\xb4\x16\xf1\x7e\x31\xff\x96\xbd\x21\xab\x95\xe4\x9e\x40\x72\x72\xd2\xd7\xd2\xb5\xff\xd8\x49\x2c\xad\x9b\x25\xd8\x70\x3c\xb6\xe0\xe5\x52\x50\xab\xb9\x76\xe1\x6e\x04\xdd\x4b\x34\x23\x1c\x76\xf3\xf4\xa6\x4f\x5c\x35\x79\xdc\x58\x16\x53\x7a\x0d\x77\xc1\xd1\xf1\x7b\xa6\xf6\x72\xaa\xf2\xa3\x21\xdb\x7e\x22\xcd\xb3\xbb\x4e\x72\xee\xcb\x6a\x14\x76\x5f\xd9\xfe\x32\xb8\xda\x93\x5c\x24\x2c\x6f\x05\x7a\x7e\x31\x0d\xeb\x90\x44\x2d\xb5\xeb\xba\xa8\xf5\x7d\x0e\x53\x49\x9e\xc1\xe5\xe6\x1b\x52\x88\xe4\xe9\x80\x21\xd9\xf8\xb4\x81\x53\x74\x14\x69\x87\xb1\x35\x7f\x96\x7e\x97\x43\x19\x91\x65\x66\x4e\xc0\x6e\xe5\x40\xa7\x40\x9e\x39\xed\xcb\xa6\x16\x03\xfa\x3a\xc3\x3e\x4d\x7f\x06\x8c\xa9\x34\x53\x5b\xa2\x00\x75\x55\x43\x84\x2d\x2a\xd7\x0c\xd0\x39\x99\x9c\x09\x58\x41\xe7\xb2\x26\x44\xfa\x2d\x73\x65\xcf\x3a\xe9\xc5\x05\xbc\x99\x1c\xbc\x98\xcb\x91\x9e\xa3\x0c\x2e\xa7\xa9\xcf\x47\x18\xcf\x16\xa8\x63\xbc\x7e\x84\xac\x73\xae\xf3\xee\x6a\x9b\x54\x20\xaf\x65\x64\x3e\x78\x74\x4f\x36\x22\x7b\x64\xdf\xac\xe0\x1f\x9c\xf6\x79\x0b\x89\xdd\x13\x19\x2f\xb5\x62\x3e\x7d\x27\xbe\x19\xeb\x29\xd0\x79\x5e\x2c\xb1\xb7\x95\xf6\x4b\xf0\xea\x54\x2f\x33\x6b\x8f\x91\xf6\xe2\x5b\x8a\x7a\x39\xaf\x8d\xf6\x68\x26\xcb\xae\xa4\x97\xc6\x5f\xf6\x58\x9f\x2d\xef\x9d\xb5\xd6\xba\x54\x9a\x4c\x8b\xcd\x59\x2d\xe7\x04\x1c\xed\xe4\x89\xc8\xd9\xd4\xf8\xe7\x13\x37\xb2\xf5\x56\xa6\x2c\x61\xfd\xdf\x00\x00\x00\xff\xff\xd5\x1b\xb6\x85\xfd\x0c\x00\x00")
|
||||
|
||||
func complySoc2NarrativesControlMdBytes() ([]byte, error) {
|
||||
return bindataRead(
|
||||
@@ -377,7 +377,7 @@ func complySoc2NarrativesControlMd() (*asset, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "comply-soc2/narratives/control.md", size: 387, mode: os.FileMode(420), modTime: time.Unix(1526687060, 0)}
|
||||
info := bindataFileInfo{name: "comply-soc2/narratives/control.md", size: 3325, mode: os.FileMode(420), modTime: time.Unix(1545086630, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
@@ -397,12 +397,12 @@ func complySoc2NarrativesOrganizationalMd() (*asset, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "comply-soc2/narratives/organizational.md", size: 2378, mode: os.FileMode(420), modTime: time.Unix(1526687060, 0)}
|
||||
info := bindataFileInfo{name: "comply-soc2/narratives/organizational.md", size: 2378, mode: os.FileMode(420), modTime: time.Unix(1545086630, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
var _complySoc2NarrativesProductsMd = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x44\xcd\xb1\x4e\x03\x31\x10\x84\xe1\xde\x4f\x31\x12\xf5\x49\x84\x0a\xb9\xa5\x01\x8a\x28\xe2\x78\x81\x8d\x3d\x0a\x4b\xe2\xdd\x68\xed\x3b\x74\x3c\x3d\x4a\x81\xd2\x8e\x46\xff\x67\xd2\x98\x71\x08\xaf\x4b\x19\x1d\x62\x15\x33\x63\xd5\xc2\x8e\xbd\x44\xc8\xd0\x95\x49\x4a\xb8\x6d\x2d\xe3\x30\xef\x53\x93\x6f\x8f\x0f\xae\xda\xd5\xad\xe7\x04\x4c\xa8\x32\x98\xf1\xbe\x18\x76\x78\x7a\xdc\x3d\x27\x00\x28\xde\x1a\x6d\x64\xbc\x99\x0e\x95\x0b\xaa\x97\xe5\xb6\xa4\x69\x9a\x52\x7a\xb8\xb3\x77\x29\xbd\x32\x88\x1f\xa2\xb2\x97\xd0\x23\x31\xbe\x88\x33\x37\x5c\xff\xcf\x4d\xe2\xcc\xc1\x8a\xe3\x06\x5f\x02\x1e\x27\x31\xfd\x95\xa1\x6e\xb7\xea\x27\xdb\xf5\x22\x83\x78\xf1\xa6\x76\xc2\xec\x6e\xe9\x2f\x00\x00\xff\xff\x2c\x6f\xb1\xc2\xe8\x00\x00\x00")
|
||||
var _complySoc2NarrativesProductsMd = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x6c\x52\xcb\x6e\xdb\x40\x10\xbb\xef\x57\x10\xf0\x35\x0e\x9a\x9e\x0a\xdf\xd2\xf6\xd0\x14\x69\x62\xd8\x5f\xb0\x5e\xd1\xf2\xd4\xd2\xae\x30\xb3\xb2\xa1\x7e\x7d\x21\xd5\x7a\x24\xcd\x51\x1c\x8a\x1c\x72\x27\xfa\x9a\x1b\x6c\x35\x15\x6d\xc8\x06\x1f\x0b\xec\xa9\x17\x09\x34\xbc\x78\x55\x9f\xe5\x42\xe7\x83\xa6\xd8\xd5\x1b\x6c\xf7\x2f\xae\xf6\xbf\x93\xee\x78\x11\x93\x14\x6d\xe3\x80\x35\x0a\x9f\xb9\xc1\xcf\x36\xe2\x01\x9f\x3f\x3d\x7c\x71\x00\x10\x52\x5d\x33\xe6\x0d\x9e\xa2\x64\xf1\x15\x8a\x14\xda\x1e\x71\xeb\xf5\xda\xb9\xd5\x6c\x3b\x3b\xb9\x1f\x54\xe2\x4a\x14\xb4\xa0\x72\x20\xf2\x89\x38\xb3\x43\x33\x92\x6b\xaf\x67\x66\x16\x38\x74\x48\xad\x22\x69\xe9\xa3\xfc\xf1\x59\x52\x5c\xaa\x3a\xb7\x9a\x3e\xf0\xe0\xdc\xeb\xa5\x0f\xc6\x2b\xd2\x71\x14\xeb\xe1\xd5\x6a\x85\x47\x0d\x27\xc9\x0c\xb9\x55\x3a\xf7\x55\x85\x47\xf8\x19\xeb\x57\x17\x0b\xad\xf5\x89\x3f\xf8\x7d\xcf\xd0\xaa\xe4\x0e\xdf\x52\x34\x29\xa8\xc3\x2e\xe6\xdc\xbe\x61\x90\xa3\x04\xd8\xc8\x08\x6f\x18\x38\x26\x9d\xc5\xee\xb1\xe3\x91\x8a\x9c\xd0\xa4\x4a\x82\xd0\xee\xfa\x69\x60\xd1\x2a\x0d\x27\x2a\xef\xfb\x84\x03\x8d\x31\xf0\x5f\xc6\xa9\x3d\x73\xee\x59\x2c\x43\x59\xf1\xe2\x63\x46\x9c\x26\x83\xd0\xc1\x1f\xaa\x0e\x12\x43\xd5\x16\x12\x4b\xf7\xba\x68\xce\x57\x8b\x57\x98\x02\x2d\xa0\xce\x32\xeb\xe5\x4b\xf5\xed\xde\xd6\x7c\xef\xfb\x66\xfd\xf7\xae\x8f\x4d\x53\x49\x18\x4c\xe7\xe6\x06\xa5\xce\x7d\xf7\xd9\x07\xc6\x4c\x1d\x91\xe7\x54\xe2\x97\x8f\xbe\x64\x7f\x39\x23\xba\xf5\x66\xd7\xa4\xc5\xf8\x3d\xe9\x3c\xc5\x20\x45\x4f\xdc\xd1\x9a\x14\x8d\x23\x63\x27\x76\xc6\xa3\x19\xcd\x96\x42\xb7\x1b\xb9\x55\xfc\x5f\x8e\x69\xf2\x51\x12\xf8\x10\x68\x06\x65\x7f\x56\x77\x68\x7c\x0e\x27\x89\xe5\x1d\x7c\x45\xcd\xa8\x53\x94\x9c\x74\x40\xaa\x54\xce\x3c\x46\x64\x5a\x96\x58\xfe\x0d\x00\x00\xff\xff\x5a\x81\xee\x7e\x7f\x03\x00\x00")
|
||||
|
||||
func complySoc2NarrativesProductsMdBytes() ([]byte, error) {
|
||||
return bindataRead(
|
||||
@@ -417,12 +417,12 @@ func complySoc2NarrativesProductsMd() (*asset, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "comply-soc2/narratives/products.md", size: 232, mode: os.FileMode(420), modTime: time.Unix(1526687060, 0)}
|
||||
info := bindataFileInfo{name: "comply-soc2/narratives/products.md", size: 895, mode: os.FileMode(420), modTime: time.Unix(1545086630, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
var _complySoc2NarrativesSecurityMd = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x84\x8f\xb1\x4e\xc3\x40\x0c\x86\xf7\x7b\x8a\x5f\x62\x4e\x45\x3b\xb4\x28\x1b\x8a\x90\x00\x41\x07\xd2\x85\xf1\xb8\x98\xc6\x34\x67\x23\x9f\xd3\x2a\x6f\x8f\x92\xa1\xea\xc6\xe4\x4f\xbf\xe5\xcf\xb6\xc4\x4c\x35\x5a\x4a\xa3\xb1\x4f\x78\xb4\xd4\xb3\x53\xf2\xd1\x08\xfb\x68\x16\x9d\xcf\x14\x62\x32\x95\x29\xd7\x68\x9f\xf6\xa1\x44\xe7\xf2\xcd\x54\xea\x00\x1c\xda\x66\x2e\x40\x85\xa6\xd9\xae\xb6\x37\xbc\xbb\xf2\x6e\xb5\xbe\xe1\x4d\xc8\xf1\x47\xed\x83\xce\x5c\x58\x65\xd1\x54\xe8\xa2\x53\x8d\xd7\x51\xb0\xc6\xe6\x7e\xfd\xb0\x0c\x24\xcd\x99\xc4\x6b\xbc\x08\x3b\xc7\x01\x9d\xa6\x71\x4e\x42\x55\x55\x21\xdc\xfd\x7b\x78\x78\x26\x23\x5c\x08\xb2\x44\x84\x4b\x3f\x41\x47\x83\xda\x11\xd7\x47\xe0\x3d\x21\xa9\xb8\xe9\x80\x13\x4d\x05\x03\x17\xa7\x0e\x2c\x4b\xeb\xf3\xfd\x0d\x5f\x83\xa6\xd3\xbc\xf2\x40\xf9\x77\x98\x55\x8d\x66\x96\x23\x5a\x55\x09\x7f\x01\x00\x00\xff\xff\x71\xb6\xff\x0e\x47\x01\x00\x00")
|
||||
var _complySoc2NarrativesSecurityMd = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xb4\x57\x5f\x6f\xe3\xc6\x11\x7f\xe7\xa7\x18\xc0\x40\x0a\x38\x96\xae\x97\x87\xa4\xf0\x9b\xaa\xbb\x22\x6e\x7d\x67\xc3\x32\x72\xe8\xe3\x88\x1c\x89\x5b\x2d\x77\xd8\x99\xa5\x54\x26\xca\x77\x2f\x66\x97\x94\x68\xdd\xf9\x82\xb4\xc8\x9d\x01\x2d\x97\xcb\xf9\xf7\x9b\xf9\xcd\x6c\xc0\x86\x6e\x61\x45\x65\x27\x2e\xf6\xb0\x90\xb2\x76\x91\xca\xd8\x09\xc1\x47\x14\xc1\xe8\xf6\x54\x60\x29\x1c\xfa\xe6\x16\x56\xef\x3f\x16\x8a\xd1\xe9\xc6\x91\xde\x16\x00\xcf\xab\xa5\xfd\x00\xcc\x60\xb9\xfc\x7e\xfe\xfd\x64\xfd\xc3\x69\xfd\xc3\xfc\xed\x64\xfd\x5d\xd1\xe0\xbf\x58\x9e\x68\xef\xd4\x71\x48\x62\x66\x50\x61\xa4\x5b\xf8\x7b\x17\xe0\x2d\x7c\xf7\xe7\xb7\x7f\x49\x1f\x94\xdc\x34\x14\xe2\x2d\xdc\x05\x17\x1d\x7a\xa8\xb8\xec\x6c\xa7\x98\xcd\x66\x45\x71\xf5\x9b\x86\x17\x3f\x92\x10\x1c\x08\x42\xda\x22\x38\xd4\x3d\x70\x27\xc0\xb2\x85\x93\x23\x10\x6b\x82\x92\x43\x14\xf6\xb0\xa3\x5e\xc1\x3b\x8d\x54\x81\x0b\xe9\xd5\x3f\x3f\xdc\xc3\xda\x73\xb9\x33\x95\xbf\xfc\x32\xff\x88\x0d\xfd\xfa\x2b\x3c\x0a\x57\x5d\x19\x5f\xe8\x2e\x8a\x77\xa4\xa5\xb8\x35\x41\x3b\xbc\xc6\xa9\x69\x35\x09\xdd\x00\x35\x6d\x8d\xea\x7e\x76\x61\x0b\x3a\xba\xe0\x9a\xd6\xbb\x12\xa3\xc5\xe4\xa5\xa2\xbb\xb0\x11\xd4\x28\xdd\xa0\xe2\xea\xea\xa4\xfb\xf2\xd5\x67\xda\xdd\x8b\x03\xaf\xa8\x6e\x08\xb5\x13\x32\xb5\x57\x57\xb0\xe8\x62\xcd\xe2\x7e\xa6\x0a\x1e\x49\x94\x43\x20\x5f\x14\x33\xb8\xbe\x5e\x7c\x5a\x81\x30\x47\xc0\xb2\xe4\x2e\xc4\xeb\x6b\x5b\x91\x2a\x38\x85\xad\x60\xb0\xa0\x71\xf0\x3d\x44\x4e\x91\x5b\x3e\x3f\x00\x86\x0a\x96\xef\x1f\x4e\x02\xee\x16\x1f\xbe\xf8\x9d\x7d\xc2\x80\xe0\x5d\xe3\xec\x79\x2b\xdc\xb5\xc0\x1b\xb8\xbe\x7e\x68\x49\x30\xb2\xe8\xf5\x75\x12\x73\x0e\xcd\x6a\xf5\xe3\x6b\xc2\x7e\xa7\xa4\x77\x7f\xfd\x5d\x82\xde\x61\x44\x98\x4a\x33\x54\xee\x9e\x3f\x03\xe4\xac\xa0\xd3\x21\xd3\x36\xec\x3d\x1f\x0c\x80\xd2\x73\x57\x81\x92\xec\x5d\x49\x0a\x1b\x16\x70\x51\xc1\x85\x48\x12\xd0\x5f\x80\x77\x6b\x20\xdc\x3b\x8d\x17\xdf\x15\xc5\x22\x9b\x9d\x83\xae\x74\x29\xd7\xe9\xc9\x03\x03\x4e\x2a\xd3\x3d\x20\x24\xec\xc9\x3c\xb2\xf5\xd9\x56\x6a\x5a\xcf\x3d\x51\x02\xcf\x29\x08\xed\x1d\x1d\xa8\x82\x7f\x77\x28\x91\xc4\xf7\x80\x0a\x07\xf2\xde\x7e\xf7\x0e\x41\x68\xdb\x79\x14\xe0\xb0\x66\x4c\x1a\xde\xf0\x66\x33\xae\x21\xa2\xee\xb2\x83\x81\x0e\x49\x6a\x45\x2d\x4a\xb4\x77\xa3\x32\x9d\xbf\x4c\xfb\x4f\x2c\x3b\x8d\x63\x41\x9c\xf7\x0f\x93\x7d\x40\x2b\x2a\x94\x8a\x82\x79\xb7\x45\x17\x34\x82\xe7\xad\x2b\xd1\x27\x3d\x6d\xdd\x6b\x7e\x88\x11\xcb\x1d\xac\xfb\x0b\x10\xc6\xe4\x4f\xe1\xe5\x84\x68\x2a\x8e\x5e\x23\x35\xd0\x74\x1a\x61\x4d\x70\x70\xb1\x76\x01\x38\x10\x6c\x29\xa4\x43\x1c\x2c\x70\x65\x27\x92\x18\x09\x36\x9d\xf7\xb3\xca\xe9\x0e\x28\x94\xd2\xb7\x76\xc2\x44\xe6\x88\x00\x86\xe8\xf6\x4e\x3a\x7d\x63\xab\x06\xfd\xc1\x6c\x57\xde\x44\x5b\x14\x33\x78\x58\x25\x83\x17\x3f\x01\x76\x91\x1b\x8c\x66\xb6\xef\xa1\x6b\x8d\x1c\xab\xa2\x98\x04\xc4\xa8\xb1\xf5\x0e\x43\x99\x4d\x1b\x80\x1f\x7d\x31\xcc\x68\x8f\xbe\xc3\x5c\x91\x80\x13\xe4\xd6\xa8\x2e\xc5\xfa\x0a\x9e\xa8\xe1\x48\x90\xf3\xa7\x28\x3e\x60\xe8\xbf\x90\x05\x9a\x42\x0e\x92\x0e\xfb\x3e\xcb\x1b\x01\x4f\xd2\x92\xe1\xa5\x31\x45\x19\x2d\xb3\x06\xf6\x31\x43\x53\x06\x8d\x09\x7d\xf7\x3c\xc4\x35\x27\x8d\x01\xa1\xd8\x98\xdd\xb1\xe6\x4a\x2d\x99\x62\xcd\x4a\x13\xd5\x83\x54\x83\x64\x23\xdc\x5c\x24\xea\x09\x5d\xde\x6c\x5c\x49\x37\xe0\xe6\x34\xbf\x81\xca\x89\x59\x32\xe0\x90\xf3\x7e\xa8\x90\x97\xb5\x31\x87\xbb\x68\xc1\x32\xa9\xa3\xce\x3f\x59\xbe\x6b\xcb\x41\xdd\xda\x79\xe3\xc7\xc8\x40\xc1\x02\x0b\xb1\xc6\x98\x19\x0e\xcf\x2c\xd9\x8e\x2c\x69\x35\x3e\x31\x4e\x48\xb9\x13\xab\x40\x0b\xc2\x60\xc2\xf9\xf5\x10\x89\x94\xf5\x43\x05\x3f\xa5\x3a\x9b\x16\xf4\xf9\xf8\x25\x93\xaf\x39\xd6\xe7\xc8\xa6\x54\xcf\x51\xbf\x79\xad\x62\x13\x12\x58\x5a\x67\x34\x4b\x25\x57\x8f\xc1\xba\xa7\x6a\x0e\x8b\x60\x67\xb8\x41\x6f\x3d\x31\xbf\x6a\x59\x46\x6e\x36\xac\xc6\x86\x11\x09\x9b\x54\xcd\x9b\x4e\x62\x4d\x02\x2e\xec\x49\xa3\xdb\xa6\xe4\x9c\xc3\xa7\x9a\xc2\x04\x43\x8d\x28\x11\x58\x86\xa2\xbf\x01\x0c\xaf\x11\x45\x2b\x5c\x52\x65\xa1\x76\x3a\x14\x69\xd6\xdf\x0a\xe7\x81\x61\x90\x73\x7a\xc4\xb6\x15\x6e\xc5\x59\x7b\x1f\xfa\xd2\x10\xeb\x14\xd9\x47\x0a\x14\x87\x72\x7d\x36\x1b\xc3\x76\x4a\x26\x36\x62\x38\xd5\xcc\x25\x01\xe8\x3f\x43\x3c\xdb\xc9\x67\x91\x34\xa6\xa4\xb7\xbf\xd0\xa1\x1f\x4a\x08\x16\xde\xc3\xc6\x05\x33\x3c\x07\xcc\x35\x0d\x55\x66\x89\xef\xcf\x08\x24\xf0\xab\x4a\x48\x75\x12\xca\x34\x7c\x60\x39\xd2\xc8\xd0\x2d\xdf\x2c\xdf\x3f\x5c\xd0\xe0\xe3\x98\xe2\xe3\xb0\x33\x35\xbf\x46\x4d\x9c\x74\xaa\x03\xcf\x79\x84\xb8\xb1\xe1\x65\x85\x01\xfe\x26\x18\x4a\xa7\x25\xdf\xc0\x72\x31\x87\x7f\x50\x0f\x4e\xb5\x4b\xcc\x61\x89\x2f\x58\xee\xa8\x1a\x69\xf1\x21\x95\xd1\xe7\x3a\xe1\x91\xbd\x2b\x7b\xb8\xa7\x6a\x4b\x32\x1f\xcf\xa5\x61\xc9\x1c\xc7\xaa\x72\xa6\x36\x11\x56\x4d\xfe\x24\xd0\x93\x2a\xcb\x8d\xc1\xd7\x92\xd8\xac\x81\x01\xb7\x64\x43\xdc\x4d\x66\x8d\x4e\x23\x57\x36\xdc\x69\xc4\xcd\x66\x0e\xcf\x89\xc9\x4e\x92\x03\xc7\xff\xd5\xc8\x73\x98\xb2\x52\xd1\x91\xb5\x4e\xf0\x4c\xda\x43\x2e\xb9\x56\xdc\xde\x79\xda\xa6\x6e\xf4\x5a\xf1\xa5\x8e\x6a\x81\xa6\x6a\x6c\x0c\x8b\x4f\xab\xa9\xc2\x8a\x49\x93\xe9\x35\xee\xe9\x33\x25\x91\xed\xfc\x85\xd0\x04\xfb\x93\xb5\x8f\x85\x2a\xa9\xa6\x39\x77\x3a\x3f\xa4\x36\xa0\x69\x42\x58\xf6\x6b\x92\xcb\xc3\x5f\x48\x51\x4b\x02\x96\x8a\xc4\x54\xee\x88\x5a\x68\x71\xd2\x31\x80\xf6\xec\xf7\xa9\x3f\xd7\x42\x18\xc1\x63\xa8\xb4\xc4\x96\x12\x0c\x93\x26\x99\x28\x3e\x55\x79\x88\x2c\xbd\x65\x2c\x56\x7b\x12\x45\x71\x03\xfb\x04\x0e\xb3\xe9\x5e\x16\x69\x9c\xae\xa7\xcc\x5f\xa7\x59\xc3\x35\x46\x2b\x29\x03\xa7\x0c\x97\x5b\xd2\x62\x22\xe2\x39\x8b\x28\x8a\x97\xc6\x08\xb5\x42\x4a\x21\x66\xde\x7e\xd5\xaa\xc1\x82\xdb\xa2\x38\x66\x49\xc7\x55\xe2\xe3\xe3\x4f\x54\x46\x96\xe3\x33\xca\x96\xe2\xf1\xde\xed\xc8\xbb\x9a\xb9\x3a\xae\x68\x4f\x96\x49\xc7\xe2\x38\xfb\xca\xbf\x6f\xbf\xf2\xf8\xed\xd7\x4e\x0f\xcb\x63\x71\x84\xc9\xff\xe4\xf7\x47\x0e\xb3\xff\xd3\xf7\x57\x10\x98\xf8\xff\x47\x3a\xfe\x5b\x7e\xa6\xf4\xa6\x0d\x09\x85\x32\x5f\x35\xce\x17\x35\x2d\x8a\xe1\x46\x93\x7b\xe5\x6a\x1c\x5d\xcf\x57\xb9\x55\x9e\xc7\x5e\xbb\xe8\xd9\x9d\xc8\xaa\xdf\x99\xac\xf7\xa7\xf9\x6b\xa0\x84\xe2\x9e\xb7\xf0\xe1\xc4\x3c\xe3\xee\x40\x24\x17\xfc\x51\xbc\x98\x8a\xc6\xcd\xd3\xa1\xbb\x50\xba\xca\x84\x3c\xe5\x21\x81\xc6\x13\xd3\xf1\x6c\xd8\x1a\xae\x6a\xb9\xa3\xd9\xa0\xde\xb6\xbe\xb7\x41\xef\x11\x63\x59\x93\x16\xb9\xe1\xc3\x37\xb0\xf4\x84\x02\xf7\x7c\x98\x3d\x8a\xe3\x7c\xa7\xf5\x24\xf1\x74\x64\x98\xd1\x86\xa7\x77\x94\xe3\xf3\xcd\xcb\x29\xf9\xbf\x01\x00\x00\xff\xff\xa2\xda\x57\x64\xcf\x0f\x00\x00")
|
||||
|
||||
func complySoc2NarrativesSecurityMdBytes() ([]byte, error) {
|
||||
return bindataRead(
|
||||
@@ -437,7 +437,7 @@ func complySoc2NarrativesSecurityMd() (*asset, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "comply-soc2/narratives/security.md", size: 327, mode: os.FileMode(420), modTime: time.Unix(1526687060, 0)}
|
||||
info := bindataFileInfo{name: "comply-soc2/narratives/security.md", size: 4047, mode: os.FileMode(420), modTime: time.Unix(1545086630, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
@@ -457,7 +457,7 @@ func complySoc2NarrativesSystemMd() (*asset, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "comply-soc2/narratives/system.md", size: 257, mode: os.FileMode(420), modTime: time.Unix(1526687060, 0)}
|
||||
info := bindataFileInfo{name: "comply-soc2/narratives/system.md", size: 257, mode: os.FileMode(420), modTime: time.Unix(1545086630, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
@@ -477,12 +477,12 @@ func complySoc2PoliciesReadmeMd() (*asset, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "comply-soc2/policies/README.md", size: 71, mode: os.FileMode(420), modTime: time.Unix(1526687060, 0)}
|
||||
info := bindataFileInfo{name: "comply-soc2/policies/README.md", size: 71, mode: os.FileMode(420), modTime: time.Unix(1545086630, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
var _complySoc2PoliciesAccessMd = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x4c\x8e\x31\x6a\xc4\x30\x10\x45\x7b\x9d\xe2\x43\x6a\x85\xd8\x81\x10\xd4\x19\x55\x49\x63\x13\xfb\x02\x13\x49\xbb\xcc\x62\xcd\x80\x24\x2f\xf8\xf6\x8b\x5d\xb9\xfa\x8f\x0f\x0f\x9e\x50\x4e\x0e\x43\x08\xa9\x56\x8c\xf2\xaf\x54\x22\xcb\x1d\x24\x11\x4b\x2a\x99\x85\x1a\xab\x60\xd2\x95\xc3\x6e\x28\x14\x95\x3d\x3b\x0c\xe3\x32\x99\x4a\x8d\xeb\x8d\x53\x75\x06\x58\x66\x7f\x0c\x60\xe1\xfd\xd7\x7b\x77\xe1\xfe\xc2\x9f\x26\xd3\x43\xcb\x5f\x7a\x72\x65\x95\x53\xb5\x88\xd4\x92\xc3\xef\x26\xe8\xd0\x7f\x74\xdf\xa7\x10\x34\xe7\x24\xcd\xe1\x47\xb8\x31\xad\x88\x1a\xb6\xe3\x31\xd6\x5a\x63\xde\xe0\x35\x1f\xad\xb3\xaa\xbc\x02\x00\x00\xff\xff\xcd\x46\x92\x04\xc8\x00\x00\x00")
|
||||
var _complySoc2PoliciesAccessMd = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x8c\x55\xc1\x6e\x1b\x47\x0c\xbd\xeb\x2b\x08\xe4\xd2\x1a\x91\x5a\xa7\x40\x51\xe8\x96\xba\x05\xec\x02\x45\x0c\x45\x40\xce\xd4\x2c\x57\xcb\x68\x76\xb8\x20\x67\xa5\xca\xa7\xfe\x46\x7f\xaf\x5f\x52\x70\x66\x25\xcb\x6e\xec\xe4\xa4\xdd\x19\x91\x7c\x8f\xef\x91\x9b\xb0\xa7\x25\xbc\x0f\x81\xcc\xe0\x43\xda\x08\x6a\xc3\x69\x0b\x98\x1a\x58\x93\xf6\x9c\x30\xb3\x24\xb8\x97\xc8\xe1\x38\xc3\xa0\x92\x8e\xfd\x12\xde\x7f\x58\xdf\xcf\x0c\x33\x5b\xcb\x64\xcb\x19\xc0\xfa\xe3\x8d\xff\x00\xcc\xe1\xe6\xe6\xe7\xc5\xf5\xc5\xf3\xbb\x8b\xe7\x9f\x66\x3d\x7e\x16\x5d\xd1\x9e\x8d\x25\x95\xd0\x39\x34\x98\x69\x09\x7f\x8c\x09\xae\xe1\xdd\x8f\xd7\xbf\x94\x80\x20\x7d\x4f\x29\x2f\xe1\x2e\x71\x66\x8c\xd0\x48\x18\xfd\x64\x36\x9f\xcf\x67\x6f\xe0\x7e\xd4\x41\x8c\x0a\xd6\x8f\x41\x06\x9a\xcd\x70\x01\xeb\x8e\x60\x98\x6e\xa4\x85\xdc\xb1\xc1\x50\xd0\x43\x16\x68\xa8\xe5\x44\x30\xa8\x04\x6a\x46\x25\xf3\x43\xa9\xbc\x4b\x22\x69\xdb\xfa\x32\x1a\x69\xb9\xcd\x14\xba\xc4\x01\x23\x70\x6a\x15\x2d\xeb\x18\xf2\xa8\x04\x9c\x00\xa1\xc7\x94\x48\x21\x77\x98\xa1\xe7\xc4\x3d\x3f\x78\xd2\x8e\x40\xd9\x76\x8e\x80\x53\x2b\xda\xd7\x36\x46\x31\x03\x51\xa0\xbf\x06\xb1\x51\x69\x01\x13\xe6\x47\x90\x38\x0c\x91\x2b\x2e\x8c\xf1\xe5\xea\x07\xce\x1d\xa7\x52\x49\x74\x8b\x89\x1f\x4a\x89\xaf\x67\x6c\xc7\x18\xe7\x99\xfb\xda\xb8\x01\x35\xd7\x37\xea\x87\x28\x47\x22\x2b\xe7\x41\x52\x56\x0c\x59\xd4\x3c\xe5\x1b\xf8\x15\xc3\x6e\xab\x32\xa6\xa6\x14\xb8\x4b\x20\xda\x38\x73\x39\xf3\xfe\x66\xda\xf0\x5d\xab\xd2\xc3\x46\x72\x07\x9c\x8c\x9b\x8a\x45\xc6\x5c\x9e\x9f\x73\xfa\xfe\xed\xff\x8e\x80\x0d\x94\x22\x63\xca\x20\xb5\x0b\x83\x72\x0a\x3c\xc4\x22\x7b\x24\xb4\xec\x47\x7b\x8e\xb4\xa5\x85\x5b\x5c\xc6\x94\x21\x28\xd5\x04\x85\xbc\x3b\xdc\xac\x40\xa4\x3d\x45\x03\x54\x02\x25\xcb\xca\x21\x53\x53\xcd\x11\x8f\x95\x18\x99\x8c\x1a\xbc\x3f\x1b\x93\x38\x66\x8a\x47\x48\x44\x4d\xfd\xdf\x40\xea\x84\x81\x30\x74\xfe\x62\x92\xfe\xfd\xfb\x1f\x83\xcf\xb2\x81\x66\xcc\x4c\xb6\x80\x4f\x1d\xb9\x67\xdc\x5a\xe5\x4e\x25\xbe\x28\x24\x84\x0e\xd3\x96\xcc\xb9\x17\x97\x57\x02\xf6\x0a\xf0\x1a\xd1\xfc\xa0\xb4\x97\x5d\x45\xd5\x72\x2e\x99\x13\x1d\x6a\x35\x8f\x6e\xd8\x70\x13\xa9\x81\x83\xe3\xf1\x6b\x47\xe4\x3d\xdb\x4f\xde\x7d\x02\x04\x63\x96\x2d\xe5\x8e\xb4\x3a\x61\xda\x05\xee\x82\xab\xdf\x46\xf5\x75\x21\xe7\xcd\xb1\xbc\x9a\x95\xe1\xe5\x05\xdc\x72\xb9\xfb\x13\x13\x6e\x49\x27\x43\x18\xdc\xae\x60\x1c\x24\x41\xc7\x5a\xa4\xc2\x02\xee\xe4\xbe\xc5\x63\xf8\x0a\xa8\x47\x8e\x06\x77\x6b\xa7\x52\xe3\x1d\x5e\xff\x18\x56\x92\x38\xa7\xdc\x11\x6b\xa1\xe8\x20\xa7\x14\x77\xeb\xaa\xb7\x6b\x06\xa1\xa3\xb0\x8b\x6c\xb9\x44\xbf\xde\xcd\x49\xd6\x56\xa6\xc9\x7e\x96\xd7\x77\x8c\x1c\x7c\xee\xa5\xad\x82\x9f\xcc\x01\x4a\x7b\xa6\x43\xcd\x8b\xc3\xa0\xe2\x2d\xc5\x2f\x99\xcf\xfb\x3c\x43\x33\x09\x8c\xee\xb5\x47\x14\xf6\x94\xc2\x41\x74\x67\xc5\x26\x55\x9b\x2f\x17\xce\x02\x46\x19\xc6\xe1\x2c\xe8\xb4\x0a\xce\x1a\x4d\x9b\xed\x55\x91\x92\x64\xf6\x8d\xee\xdd\x2f\xee\xc0\x74\x96\x06\x3a\x34\xd8\x90\x5b\x66\xfa\x32\x50\x73\x81\xf4\x76\x05\x46\xa9\xf1\x56\x1f\x88\x76\xf1\x58\xe5\x03\xa5\x41\x34\x3b\xbe\xbb\x35\xd8\xd8\xf7\xa8\xfc\xe0\x35\x4f\x5a\x4c\x9b\xf6\x9c\xb3\x74\x87\x53\x5d\x75\x27\xf5\x27\xcf\x4e\x3a\x63\xf9\x60\x3d\x6d\xd3\x39\x81\x4d\xd7\xa7\xc9\x6a\x79\x4f\xb0\x19\x8d\x93\x1f\x36\x78\x34\x28\xeb\x47\x29\x10\x0f\x05\x42\xa5\x1d\x2e\x37\xe8\xd5\xa7\xe7\xf4\xa7\x81\x2c\x66\xb0\x97\xc6\xf6\xe5\xd6\x1e\x38\xc6\x93\x89\x6f\x57\xd5\xc2\x35\xa5\x7f\x49\x8a\xc3\x4a\xe4\x45\x3f\xbd\x11\x6e\x00\x8f\x6c\x25\x46\x39\x94\x7a\x86\x3d\x81\x65\x1a\x0c\xd0\x7c\x6b\x46\x4e\xe4\x2d\xab\x68\x9e\x7e\xc5\x2f\x64\xbf\xf8\xec\x9d\x48\xae\x8a\x5d\x5f\x99\x08\xbb\x20\xf4\xbb\x1b\xae\x97\x94\xbb\xb7\x0e\xcb\xff\xe8\x36\x71\x74\xd5\xf6\x5f\x1b\x2b\x9f\x27\x0c\x61\x54\x0c\x47\x47\x30\xfb\x2f\x00\x00\xff\xff\x29\x19\x25\xcc\x7f\x08\x00\x00")
|
||||
|
||||
func complySoc2PoliciesAccessMdBytes() ([]byte, error) {
|
||||
return bindataRead(
|
||||
@@ -497,7 +497,7 @@ func complySoc2PoliciesAccessMd() (*asset, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "comply-soc2/policies/access.md", size: 200, mode: os.FileMode(420), modTime: time.Unix(1526687060, 0)}
|
||||
info := bindataFileInfo{name: "comply-soc2/policies/access.md", size: 2175, mode: os.FileMode(420), modTime: time.Unix(1545086630, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
@@ -517,7 +517,7 @@ func complySoc2PoliciesApplicationMd() (*asset, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "comply-soc2/policies/application.md", size: 8377, mode: os.FileMode(420), modTime: time.Unix(1526687060, 0)}
|
||||
info := bindataFileInfo{name: "comply-soc2/policies/application.md", size: 8377, mode: os.FileMode(420), modTime: time.Unix(1545086630, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
@@ -537,7 +537,7 @@ func complySoc2PoliciesAvailabilityMd() (*asset, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "comply-soc2/policies/availability.md", size: 7019, mode: os.FileMode(420), modTime: time.Unix(1526687060, 0)}
|
||||
info := bindataFileInfo{name: "comply-soc2/policies/availability.md", size: 7019, mode: os.FileMode(420), modTime: time.Unix(1545086630, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
@@ -557,7 +557,7 @@ func complySoc2PoliciesChangeMd() (*asset, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "comply-soc2/policies/change.md", size: 2793, mode: os.FileMode(420), modTime: time.Unix(1526687060, 0)}
|
||||
info := bindataFileInfo{name: "comply-soc2/policies/change.md", size: 2793, mode: os.FileMode(420), modTime: time.Unix(1545086630, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
@@ -577,7 +577,7 @@ func complySoc2PoliciesClassificationMd() (*asset, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "comply-soc2/policies/classification.md", size: 14376, mode: os.FileMode(420), modTime: time.Unix(1526687060, 0)}
|
||||
info := bindataFileInfo{name: "comply-soc2/policies/classification.md", size: 14376, mode: os.FileMode(420), modTime: time.Unix(1545086630, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
@@ -597,7 +597,7 @@ func complySoc2PoliciesConductMd() (*asset, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "comply-soc2/policies/conduct.md", size: 4492, mode: os.FileMode(420), modTime: time.Unix(1526687060, 0)}
|
||||
info := bindataFileInfo{name: "comply-soc2/policies/conduct.md", size: 4492, mode: os.FileMode(420), modTime: time.Unix(1545086630, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
@@ -617,7 +617,7 @@ func complySoc2PoliciesConfidentialityMd() (*asset, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "comply-soc2/policies/confidentiality.md", size: 3653, mode: os.FileMode(420), modTime: time.Unix(1526687060, 0)}
|
||||
info := bindataFileInfo{name: "comply-soc2/policies/confidentiality.md", size: 3653, mode: os.FileMode(420), modTime: time.Unix(1545086630, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
@@ -637,7 +637,7 @@ func complySoc2PoliciesContinuityMd() (*asset, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "comply-soc2/policies/continuity.md", size: 5043, mode: os.FileMode(420), modTime: time.Unix(1526687060, 0)}
|
||||
info := bindataFileInfo{name: "comply-soc2/policies/continuity.md", size: 5043, mode: os.FileMode(420), modTime: time.Unix(1545086630, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
@@ -657,7 +657,7 @@ func complySoc2PoliciesCyberMd() (*asset, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "comply-soc2/policies/cyber.md", size: 4805, mode: os.FileMode(420), modTime: time.Unix(1526687060, 0)}
|
||||
info := bindataFileInfo{name: "comply-soc2/policies/cyber.md", size: 4805, mode: os.FileMode(420), modTime: time.Unix(1545086630, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
@@ -677,7 +677,7 @@ func complySoc2PoliciesDatacenterMd() (*asset, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "comply-soc2/policies/datacenter.md", size: 3014, mode: os.FileMode(420), modTime: time.Unix(1526687060, 0)}
|
||||
info := bindataFileInfo{name: "comply-soc2/policies/datacenter.md", size: 3014, mode: os.FileMode(420), modTime: time.Unix(1545086630, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
@@ -697,12 +697,12 @@ func complySoc2PoliciesDevelopmentMd() (*asset, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "comply-soc2/policies/development.md", size: 8933, mode: os.FileMode(420), modTime: time.Unix(1526687060, 0)}
|
||||
info := bindataFileInfo{name: "comply-soc2/policies/development.md", size: 8933, mode: os.FileMode(420), modTime: time.Unix(1545086630, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
var _complySoc2PoliciesDisasterMd = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x5a\xcd\x6e\x1c\xb9\xf1\xbf\xcf\x53\x14\xb0\xc0\x1f\x92\x77\x34\xfa\x7b\x73\x09\x14\xe4\x20\xdb\x8b\x44\x9b\x85\x2d\x48\x02\xf6\xa0\xe8\x50\x4d\x56\x4f\xd3\x62\x93\x1d\x92\x3d\xe3\x31\x7c\xc8\x6b\xe4\xf5\xf2\x24\x41\x91\xdd\xec\xee\xf9\xb0\xe4\x18\x63\x60\x03\xcf\xc5\x23\x0e\x9b\xf5\xf5\xab\x5f\x55\xb1\x6d\xb0\xa6\x0b\x78\xa3\x3c\xfa\x40\x0e\x6e\x48\xd8\x15\xb9\x0d\x5c\x5b\xad\xc4\x66\x86\xc2\x59\xb3\xa9\x2f\xe0\xcd\xcd\xf5\xcc\x63\x50\xbe\x54\xe4\x2f\x66\x00\x77\xb7\xaf\xf9\x1f\x80\x33\xb8\x7c\xb9\xf8\x69\xf8\xfa\x87\x59\x8d\xef\xad\xbb\xa1\x95\xf2\xca\x9a\xb8\xf9\x0c\x24\x06\xba\x80\x5f\x5a\x03\x2f\xe1\xa7\xff\x7f\xf9\xc7\xb8\x5f\xd8\xba\x26\x13\x2e\xe0\xca\xa8\xa0\x50\x83\xb4\xa2\xe5\x95\xd9\xd9\xd9\xd9\xec\x87\xeb\xd6\x35\xd6\x13\xa0\x91\x70\x2b\x6c\x43\x30\x9b\xe1\x02\xee\x2a\x82\xa6\xfb\xc9\x96\x10\x2a\xe5\xa1\x89\xea\x82\xf2\x10\x2c\x48\x2a\x95\x21\x08\x15\x81\x75\x4b\x34\xea\x23\x06\x65\xcd\xbf\xff\xf9\x2f\x0f\x8d\xb3\x82\x64\xeb\x28\xee\x74\xc9\x5c\xb8\x32\xa5\x75\x75\xdc\x05\x77\x24\x2a\x63\xb5\x5d\x6e\xe0\xe4\xea\xee\x14\x94\x29\x1d\xfa\xe0\x5a\x11\x5a\x97\x94\xb9\xba\x03\x4f\x6e\xa5\x04\x79\x58\xab\x50\x29\x03\x9e\x02\x48\x42\xa9\x95\x21\x0f\xca\x44\xe9\x02\x93\x8a\x08\xb2\x77\xb0\x75\x60\x43\x45\x8e\x57\x5c\xdb\x04\xb5\x22\x50\x46\x28\x49\x26\x24\xd3\x6c\xf1\x9e\x44\x5c\xcf\xc6\x69\x34\x9d\x69\xc2\xd6\x8d\xa6\x90\x8c\x73\x7d\xb0\x6c\xc9\x2a\x3d\x4f\x51\x8c\xaa\xe6\x38\xdf\xa9\x9a\xe0\x5d\x16\x79\x72\x73\xf7\xee\x74\xd1\xb9\x79\xe4\x57\x23\x74\x2b\xc9\x03\x6a\x0d\x8e\xbc\x6d\x1d\x1f\xc9\x22\xa2\x43\xbd\x27\x0f\x86\xf8\x0b\xba\x0d\x94\xd6\xf5\x72\xe3\x1e\x89\x01\xb3\xb6\xf3\xb8\x14\xbf\xa7\xf3\xd4\xc8\xf9\x9e\x44\xeb\x54\xd8\x00\xfa\x86\x44\xf0\x6c\x5a\xd1\x7a\x76\xaa\x07\x61\x4d\x50\xa6\xe5\x9f\x6b\x34\xb8\x24\x86\xca\xae\xb2\xd8\x34\x5a\xa5\xf8\xf2\xf1\xc3\xd6\x39\x50\xdd\x68\xbb\xa1\x4e\x75\xdf\xc6\x9d\xce\x43\xa8\x30\x00\x3a\x0e\xc5\xca\xea\x15\xc9\x3e\x82\x4f\xbb\x78\xdb\xbf\xdb\xa8\x9b\xea\x56\xb7\x3e\x40\x41\x50\xa3\xe4\xc3\x51\x2a\xbd\x01\x5c\xa1\xd2\x58\x68\xea\x55\x5e\x57\xb6\x06\x15\x46\x96\x2c\x66\xb3\x1f\xe0\x15\x8a\xc7\xa5\xb3\xad\x91\x3b\x36\x27\xc8\xfb\x24\x7d\x45\x8e\x4f\xc9\x98\xcb\x46\xf8\xe0\x30\xd0\x32\x05\x68\x9f\xa2\x34\x6c\x91\xe4\x85\x53\x45\x7f\xe6\x76\x1e\x7d\x16\x40\x73\x58\x57\x4a\x54\x8c\xd9\xa4\x98\x04\x4c\xe7\xc8\xd6\xa5\x40\x33\xb6\xf9\xc9\x91\x0b\x41\xd3\x8a\x74\xd4\x4d\x38\x15\x94\x40\x3d\xc4\x7e\x80\x59\xb0\xec\x40\x47\x3e\x58\xc7\x07\x97\x6c\xe2\x13\x19\x46\xab\x18\x7e\xf4\xb0\x26\xad\x7b\x65\x06\x2e\x98\xf3\x79\x8d\x35\x5e\x15\x4a\x47\xf8\x19\x09\x81\x89\x20\x6a\xb1\x6c\x95\x44\x23\x58\xea\x3f\x5a\xc5\x52\x83\x85\x9a\x28\xc4\x63\x6e\xee\xde\x6d\x01\x50\x7b\x0b\x5a\xf9\x90\xc4\x30\x6c\x51\x84\x09\xce\xd9\xc8\x86\x9c\xb7\xc6\x90\x9e\x38\xa1\x71\x76\xa5\x64\x06\x65\x8d\x1b\x36\xd7\x10\x49\x92\xec\x3e\x65\x96\x63\x6b\x73\x6c\xa3\x89\x8b\x4c\x90\xa5\xd5\xda\xae\x79\xb3\xb0\x46\x2a\x16\xea\x07\xf4\x51\xe8\x20\xd0\xb3\x4b\x72\xea\x4a\x31\x0a\x2f\x66\x91\x9c\xd5\x02\x2e\xb5\x06\xb6\xb8\x49\xd9\xe3\x6d\x19\xd6\xe8\x46\x39\x7d\x92\x70\xa4\x1c\x14\x28\x1e\xdb\xc6\x9f\x97\xa8\x74\x4c\xed\xd3\x98\x4f\x03\xb4\x99\x23\x6d\xcd\xc8\x37\x86\xdc\x22\xcb\xb8\x2a\x81\xd9\xad\xa3\x40\x08\xf8\x48\x51\x27\xa6\x8e\x70\x80\xc3\xab\x8d\x8f\x71\xd1\x56\xc4\xd5\xf9\x16\x2f\x8d\xb3\x78\x70\x50\x59\x5a\x17\x7c\x52\xab\x4b\xb6\x82\x20\x38\x34\xbe\x24\xd7\x45\x15\x0d\xa0\x0e\xe4\x0c\x06\x82\xb5\x75\x8f\xe0\x55\x20\x38\xf1\xad\xa8\x3a\xdc\x28\x07\x15\x5b\x62\xcb\x52\x09\x3a\xdd\xa6\x65\xe5\x40\xb6\x41\x91\x1f\x6c\xe4\x80\x8c\x4b\xcc\x6d\xcf\x72\xef\xe2\x11\x8e\x13\x25\x03\x50\x53\x4a\x01\x6b\x9d\x54\x06\x43\x0c\x78\x64\x4c\x23\x5b\x91\xfe\x84\x42\x9d\xa1\x31\x2d\x6a\x38\xc1\x00\x9a\xd0\x87\x53\x70\x54\x11\x3a\x8f\x3a\x57\x8e\x11\x63\x72\x98\x17\x30\x22\x0e\x0e\xbb\xb4\xcc\xdb\x36\x24\x3a\x8e\xde\x1e\x80\x13\x36\x0d\x45\x02\xee\x83\xe3\x07\x64\x5c\xf5\x4b\x1d\x75\x96\x25\x89\x00\xa2\xf5\xc1\xd6\x8c\x5d\x86\x37\xba\x60\xf8\x7b\xd1\x06\xa8\x70\x45\x60\x2c\x07\x81\x37\xda\x5d\x9e\x8c\x91\xf5\x1b\x1f\xa8\xf6\x7f\x4a\xf4\xcb\x06\xa0\xa7\x79\xca\xa2\xee\xec\x04\xe2\x44\xe4\x9d\xbb\xed\xda\x4c\x2c\x1d\x53\x45\x8d\x8f\x04\x9e\xc9\x3a\x2a\x1a\x2a\xda\x80\xc0\xbc\x3f\xa2\x40\x99\x40\x8e\x33\x94\x29\x7c\x47\xb1\xe8\x7c\x15\xb2\x72\x8b\xa7\x9c\xa0\x6d\x2b\xb7\x2b\xc5\x50\x6a\x3a\x50\x0b\x1b\xab\xcd\x64\x53\xe4\xbf\x79\x57\x6c\x39\x02\xec\x39\x8e\x8e\x56\xb5\x0a\x09\x9f\x7f\xb1\x76\xa9\x69\x0e\x7f\x25\x67\x1f\xdb\x54\x4b\x2f\x6b\xfc\x68\x0d\xfc\x46\x05\xdc\x76\xd5\xa8\x6b\x25\xc6\x66\x48\x6a\xc8\x48\xcf\xbe\x8f\x60\x1e\x55\x3f\xfb\x3c\x7f\xc6\x22\xd4\x35\x86\x8c\xa3\x17\x37\xd4\x67\xe0\x8b\x71\x3e\x1f\x68\xbc\x54\xcd\xbd\xc1\x90\x53\xca\x43\x6b\x32\x41\xcc\x0f\xa5\x9e\xaf\x38\xb9\x0b\x82\xd6\x93\x84\x22\x56\x26\xb5\xe4\x4d\x72\xa0\xd0\x5d\x7b\xa3\xd0\x7d\xe7\x29\x9f\x78\x83\x6b\x47\x80\x7b\xe6\xe9\xa8\x31\x4a\xe9\xb8\xd2\xd8\x64\xc0\xbe\x47\x7b\x08\x4d\x1d\xbb\x56\x5a\xb3\x72\x0f\xd3\x84\x1f\xe8\x7d\x5c\x35\x1c\x35\xd6\x05\xfe\x76\x48\xc8\x2e\xc7\xe7\xee\x6b\x50\x76\x38\x3c\xa8\xa0\x69\x97\x3e\x92\x9c\x98\xc6\x07\x45\x3d\xa4\x72\xf1\xe2\x75\x5f\x6d\x7b\xf4\xcc\xe1\x6f\xb4\x81\x3b\xf4\x8f\xb1\x51\x9a\xf7\x3f\xc0\xaf\xb1\x42\x5f\x2e\x1d\xc5\x86\xca\xc3\xc9\xed\xaf\x97\xfe\xf4\xc5\xc4\xf0\x81\x41\x72\x6f\x94\xcb\x29\xf7\xd5\xdc\xd1\x30\x09\x0b\xd6\x57\x52\xe7\x98\x82\x86\xaa\xcf\x06\xe4\xca\x6f\x1b\x4a\x3d\x83\x4f\x68\x8f\xf9\xaf\xea\x9a\xa4\xc2\x40\x7a\x33\xe9\x06\x4e\x94\x61\xa4\xd9\xc8\xae\xd6\x49\x72\xa7\x3c\x7f\x44\xf5\xf8\xf3\x72\x31\x72\x62\x96\x77\x58\xcf\x54\x83\x59\xa2\xa3\x1a\xb9\x9c\xb4\xc6\xb0\x69\x3b\x51\x7a\x38\xe0\x83\x47\xda\x40\xe8\x3d\x09\xec\xae\x5c\x86\x47\x0e\x78\xaa\xb0\x33\x2b\x00\x0a\x61\x5d\x6a\x44\xf6\x32\x55\x84\x7c\x1e\x1f\xd8\x5d\x39\x50\xc9\x75\x9a\x96\xa8\xe7\x31\xb7\x99\xef\xb8\x7c\x44\xa8\x2c\x5b\x8d\xc1\x72\x97\x5b\x68\xb5\x4c\xce\xee\xb8\x7e\xe2\x34\x5b\x8e\xcc\x39\x4f\xc6\x0c\x1e\x2a\x08\x1e\xa9\x09\x43\xc0\x58\x56\x54\x94\xc1\xd9\xf5\x87\x79\x46\x7a\xc8\xad\xca\x97\xf5\x96\x9c\xbf\xf7\xbe\x6b\xbd\x6a\xfc\xa0\xea\xb6\x06\xac\x6d\x6b\x42\x6e\x2a\x0b\x2a\x99\x5e\x73\x7c\x87\x8a\xd0\x2b\xda\x03\x66\x9e\xf8\x3f\xe6\x17\xb8\x4c\x67\xd1\x5b\x4b\x0a\x31\x81\x76\x60\x72\x3e\x81\x08\x77\x3e\x60\x0d\x1b\xf5\xb0\x80\x9b\xe9\x19\x49\x4e\x6e\x79\x9f\x81\xb8\x01\x1c\xa9\xa5\x90\xc3\x60\xc1\x23\x20\x9b\xd7\x90\x53\x56\x76\xc9\xfb\xd6\x06\x55\x2a\x91\x45\x5c\x73\x5d\x4f\xf3\xf4\x94\x98\xa7\xb0\x1c\x08\xa4\x17\x68\xe2\x41\x2c\xaf\x22\x33\x1d\x3c\x55\x3a\x8e\x64\x06\x45\x86\x04\x73\xf3\x70\xd6\xc9\x50\xbb\x12\x2f\x9d\x4e\xf1\x91\x65\xd8\xb2\x3b\x3c\x2b\x0a\x43\x02\xdd\xa7\x03\x77\x38\xcd\x8c\x4c\xf5\xe3\x3a\x19\x65\x3d\xec\xeb\xa2\xe2\x23\x9b\xb8\x67\xc2\x9a\xac\x3b\x17\x80\xc2\xae\xa8\x73\x64\x74\xdc\x1b\x42\xc6\xda\x8e\xeb\x7a\x67\x44\x43\xac\x49\xac\x23\xfb\xcd\xa9\x2e\xf5\x6a\xe7\xd6\x3e\xcd\x84\x80\x6d\xa8\x12\x21\xc5\xbb\x89\xfe\x99\xa4\x91\x46\x33\xb6\xe4\xbd\x2d\x3a\x6b\xc6\x6d\x46\x62\xb2\xbd\x2d\xfb\x48\x87\xbe\x0b\x1e\x00\xc8\xbf\xa8\xa0\x3a\x98\x6d\x83\x0f\xce\x07\xe4\xf5\x49\xcd\x2d\xae\xa4\x80\x4a\xf7\xde\xc9\xb1\x2b\x5b\xad\x37\x23\x58\xa2\x91\xe7\x91\x3d\x52\x26\xed\xad\xfc\x1c\x12\x1f\xb8\x3e\x76\xa4\x60\x96\x91\xc8\x0c\xa8\xba\xc1\x58\x13\xbd\x20\x83\x4e\xd9\x79\xf6\x47\x9c\x79\xd2\x53\x3d\xa5\x60\x22\x80\xae\xfa\x4a\xe5\x85\xa3\x1e\xf0\xc7\x72\xfb\x57\xe4\xcb\x28\x28\xfb\x68\xf4\x2b\x73\x66\x40\xe8\x7e\x0e\xed\x3a\x64\x23\x09\x57\x8c\x18\xdb\xc7\x28\x76\xb1\x86\xa7\x10\xdd\xcd\xda\xe3\xdb\x95\xa1\xce\x32\x08\xbc\x65\x00\xf1\x48\xeb\x63\x36\xa5\x24\xb9\x84\xbe\x0e\x38\xd2\xb4\x42\x13\xa0\xb1\xca\xa4\x7b\x9a\x7e\xce\x2d\x6c\xa8\x52\x4f\x6d\x30\xcd\xb6\xf4\xa1\xfb\xa3\xeb\x45\xb6\x21\x42\x46\x68\xeb\xd3\xbc\x76\xd9\x70\x9b\xaa\x3e\xc0\x65\x12\xf9\xa6\xaf\x8c\xc2\x29\xaf\x38\xed\x03\x3f\xb2\x52\xa1\x6b\x14\x04\xb9\x80\xe3\x41\x2f\x21\x39\xa5\x47\x43\x8e\xc7\x2e\x92\xe0\xd4\xb2\x0a\x80\x6b\xdc\x6c\x87\x96\xfd\x19\xad\x76\x3c\x7d\xd3\x59\x07\x9c\x8f\xc3\x2d\x50\x2c\xc1\x5b\x17\x79\xd3\xfa\x3c\x4c\x46\xf7\x03\x96\x52\x20\x78\xa2\x8d\x21\xf7\x81\x1a\x0f\x5c\x09\x53\x3c\x7b\x4e\xdf\x3d\xb2\xa3\xc6\x2e\xed\x47\xf7\x94\x7b\x2b\xfe\xf8\x5e\x61\x94\xd0\x9f\xb9\xab\x5a\x44\x65\xf7\x69\xac\x3c\x8c\xcc\xef\xe7\xa7\xd6\x2d\xd9\x01\x4d\xeb\x44\x85\x3e\x0d\x85\xf9\x5e\x60\x7a\x0b\xd6\x36\xfc\xd4\x7d\x2a\xc8\x0f\x5f\x20\x48\xd8\xba\x6e\x0d\xb3\x7b\xd7\xdd\x08\xad\xb8\x73\xf9\x9a\x23\x62\xaa\xb7\x85\x56\xe2\x6b\x4e\x49\x27\x64\x3e\x61\x3a\xed\x6f\x03\x7c\xe0\x5d\xb1\xb5\xb2\xf1\xa2\x88\xa3\x64\x52\x6b\x1c\x57\x71\x0d\xc4\xe1\x11\xdd\x5d\xe5\x17\x68\x91\xf2\xb1\xd3\xe1\xde\x60\xbc\x71\xc8\x53\xdb\xb9\x6d\x43\xbc\xee\x88\xe4\x94\x46\xed\x6d\x77\xa3\x94\xe7\xb5\x95\xaa\xdc\x9c\x3b\xaa\x99\xcb\x7b\x19\x5d\x9e\xf7\x08\xf4\x24\x52\xc1\x18\x5d\xe2\x26\x6e\xb9\x4d\x68\x15\xe3\x3b\x44\x46\x31\x27\xde\xe4\xde\x36\xcd\xc6\x9f\xbd\x20\x4d\xf9\x15\x6f\xb7\xa6\x99\xfe\x6a\x31\x9b\xfd\xbd\xc1\x25\x15\x8e\xf0\x11\x78\xca\x1c\x58\xe0\x82\x5b\xaa\x44\x35\xd7\x99\x6a\x5e\x27\xaa\x99\xcd\xae\x7a\x9a\xe9\x56\xfc\x6c\xf6\xe3\xd9\xce\x67\xcf\xd2\xbe\xb5\x67\x2d\xfd\x38\xfb\xd4\xf1\xf9\x5b\x0e\x49\xfa\xa4\xa5\x5f\x6c\x01\x77\x1c\xcf\xbc\x76\x5d\x59\x43\xf0\xb6\xad\x0b\x72\x69\xe9\xe7\x1a\x95\x86\xcb\x6e\xd6\x04\xf8\x74\x99\xe7\xb2\xce\x84\x4f\xb3\x1f\xff\xbc\xf3\xd9\xb3\xb4\x6f\xed\x59\x4b\x83\x05\xa3\xcf\x9e\xa5\x7d\x6b\xcf\x5a\xfa\x74\x7c\x01\xdf\x2c\xc8\x47\xb3\xe0\xf7\xef\xa2\xd9\xcf\x1f\xbe\x27\xdf\xf7\xe4\x3b\x62\x90\x8f\x66\xc1\xef\xdf\x45\xa3\x82\x39\xa9\x97\xaf\x2e\x86\xeb\x93\xdb\x5c\xaa\x27\x2f\x1f\xba\x52\x7d\x35\x2d\xd5\xff\x97\x6f\xff\x66\xb3\xdd\xaa\x3f\x7a\x6d\xce\x65\xbc\x7f\x39\x18\xeb\x78\x9a\x17\xa1\x20\x6d\xd7\x17\x7b\x29\xe0\xf3\x4e\x38\xbc\xbc\x6d\x74\xf2\xf4\xf0\x7f\x14\x7a\xa5\xfa\x30\x5c\xa7\x49\xf0\x66\x74\x09\x30\x2c\x9f\xf8\x53\x78\xdb\xcf\x52\xbf\xf1\xcc\xf6\xba\x7f\x6b\x03\x7b\xd3\xfe\xf3\x04\x70\x78\x79\x37\xe1\x93\xcb\xf3\x9b\xcb\xa8\x3e\xc9\x8b\x43\xb0\x3b\xbc\xfc\xc4\xa6\x7d\xb8\x7e\xf2\xcc\xff\x4e\x14\x1c\x37\xce\x3c\xc3\xc1\xcb\x8b\x23\x98\x71\x44\x1f\x1d\xd1\x25\x63\x39\xbd\x7b\x7e\x3a\x86\x7b\xbe\x1d\x86\x8e\x0b\xa0\xef\x29\xf7\xa5\x1e\x3b\x5a\xca\x1d\xd9\x45\xff\x13\x59\xf7\xcd\x70\x74\x44\x6f\xfd\x27\x00\x00\xff\xff\xa3\x09\x5f\x0d\x4b\x28\x00\x00")
|
||||
var _complySoc2PoliciesDisasterMd = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x5a\xcd\x6e\x1c\xb9\x11\xbe\xcf\x53\x14\x10\x20\x90\xbc\xa3\x51\xbc\xb9\x04\x0a\x72\x90\xed\x45\xa2\xcd\xc2\x16\x24\x01\x7b\x50\x74\xa8\x26\xab\xa7\x69\xb1\xc9\x0e\xc9\x9e\xf1\x18\x3e\xe4\x35\xf2\x7a\x79\x92\xa0\xc8\x6e\x76\xf7\xfc\x58\x72\x8c\x31\xb0\x81\xe7\xe2\x11\x87\xcd\xfa\xfb\xea\xab\x2a\xb6\x0d\xd6\x74\x01\x6f\x94\x47\x1f\xc8\xc1\x0d\x09\xbb\x22\xb7\x81\x6b\xab\x95\xd8\xcc\x50\x38\x6b\x36\xf5\x05\xbc\xb9\xb9\x9e\x79\x0c\xca\x97\x8a\xfc\xc5\x0c\xe0\xee\xf6\x35\xff\x03\x70\x06\x97\x2f\x17\x3f\x0e\x5f\xff\x38\xab\xf1\xbd\x75\x37\xb4\x52\x5e\x59\x13\x37\x9f\x81\xc4\x40\x17\xf0\x73\x6b\xe0\x25\xfc\xf8\x87\x97\x7f\x8a\xfb\x85\xad\x6b\x32\xe1\x02\xae\x8c\x0a\x0a\x35\x48\x2b\x5a\x5e\x99\x9d\x9d\x9d\xcd\x66\xbf\x83\xeb\xd6\x35\xd6\x13\xa0\x91\x70\x2b\x6c\x43\xb3\x19\x2e\xe0\xae\x22\x68\xba\x5f\x6c\x09\xa1\x52\x1e\x9a\xa8\x2f\x28\x0f\xc1\x82\xa4\x52\x19\x82\x50\x11\x58\xb7\x44\xa3\x3e\x62\x50\xd6\xfc\xe7\x5f\xff\xf6\xd0\x38\x2b\x48\xb6\x8e\xe2\x4e\x97\xec\x85\x2b\x53\x5a\x57\xc7\x5d\x70\x47\xa2\x32\x56\xdb\xe5\x06\x4e\xae\xee\x4e\x41\x99\xd2\xa1\x0f\xae\x15\xa1\x75\x49\x97\xab\x3b\xf0\xe4\x56\x4a\x90\x87\xb5\x0a\x95\x32\xe0\x29\x80\x24\x94\x5a\x19\xf2\xa0\x4c\x94\x2e\x30\xa9\x88\x20\x7b\x0f\x5b\x07\x36\x54\xe4\x78\xc5\xb5\x4d\x50\x2b\x02\x65\x84\x92\x64\x42\x32\xcd\x16\xef\x49\xc4\xf5\x6c\x9c\x46\xd3\x99\x26\x6c\xdd\x68\x0a\xc9\x38\xd7\x47\xcb\x96\xac\xd2\xf3\x14\xc5\xa8\x6a\x0e\xf4\x9d\xaa\x09\xde\x65\x91\x27\x37\x77\xef\x4e\x17\x9d\x9b\x47\x7e\x35\x42\xb7\x92\x3c\xa0\xd6\xe0\xc8\xdb\xd6\xf1\x91\x2c\x22\x3a\xd4\x7b\xf2\x60\x88\xbf\xa0\xdb\x40\x69\x5d\x2f\x37\xee\x91\x18\x30\x6b\x3b\x8f\x4b\xf1\x7b\x3a\x4f\x8d\x9c\xef\x49\xb4\x4e\x85\x0d\xa0\x6f\x48\x04\xcf\xa6\x15\xad\x67\xa7\x7a\x10\xd6\x04\x65\x5a\xfe\xb9\x46\x83\x4b\x62\xac\xec\x2a\x8b\x4d\xa3\x55\x8a\x2f\x1f\x3f\x6c\x9d\x03\xd5\x8d\xb6\x1b\xea\x54\xf7\x6d\xdc\xe9\x3c\x84\x0a\x03\xa0\xe3\x50\xac\xac\x5e\x91\xec\x23\xf8\xb4\x8b\xb7\xfd\xbb\x8d\xba\xa9\x6e\x75\xeb\x03\x14\x04\x35\x4a\x3e\x1c\xa5\xd2\x1b\xc0\x15\x2a\x8d\x85\xa6\x5e\xe5\x75\x65\x6b\x50\x61\x64\xc9\x82\xd3\xe1\x15\x8a\xc7\xa5\xb3\xad\x91\x3b\x36\x27\xc8\xfb\x24\x7d\x45\x8e\x4f\xc9\x98\xcb\x46\xf8\xe0\x30\xd0\x32\x05\x68\x9f\xa2\x34\x6c\x91\xe4\x85\x53\x45\x7f\xe6\x76\x1e\x7d\x16\x40\x73\x58\x57\x4a\x54\x8c\xd9\xa4\x98\x04\x4c\xe7\xc8\xd6\xa5\x40\x33\xb6\xf9\xc9\x91\x0b\x41\xd3\x8a\x74\xd4\x4d\x38\x15\x94\x40\x3d\xc4\x7e\x80\x59\xb0\xec\x40\x47\x3e\x58\xc7\x07\x97\x6c\xe2\x13\x19\x46\xab\x18\x7e\xf4\xb0\x26\xad\x7b\x65\x06\x2e\x98\xf3\x79\x8d\x35\x5e\x15\x4a\x47\xf8\x19\x09\x81\x89\x20\x6a\xb1\x6c\x95\x44\x23\x58\xea\x3f\x5b\xc5\x52\x83\x85\x9a\x28\xc4\x63\x6e\xee\xde\x6d\x01\x50\x7b\x0b\x5a\xf9\x90\xc4\x30\x6c\x51\x84\x09\xce\xd9\xc8\x86\x9c\xb7\xc6\x90\x9e\x38\xa1\x71\x76\xa5\x64\x06\x65\x8d\x1b\x36\xd7\x10\x49\x92\xec\x3e\x65\x96\x63\x6b\x73\x6c\xa3\x89\x8b\x4c\x90\xa5\xd5\xda\xae\x79\xb3\xb0\x46\x2a\x16\xea\x07\xf4\x51\xe8\x20\xd0\xb3\x4b\x72\xea\x4a\x31\x0a\x2f\x66\x91\x9d\xd5\x02\x2e\xb5\x06\xb6\xb8\x49\xd9\xe3\x6d\x19\xd6\xe8\x46\x39\x7d\x92\x70\xa4\x1c\x14\x28\x1e\xdb\xc6\x9f\x97\xa8\x74\x4c\xed\xd3\x98\x4f\x03\xb4\x99\x23\x6d\xcd\xc8\x37\x86\xdc\x22\xcb\xb8\x2a\x81\xd9\xad\xa3\x40\x08\xf8\x48\x51\x27\xa6\x8e\x70\x80\xc3\xab\x8d\x8f\x71\xd1\x56\xc4\xd5\xf9\x16\x2f\x8d\xb3\x78\x70\x50\x59\x5a\x17\x7c\x52\xab\x4b\xb6\x82\x20\x38\x34\xbe\x24\xd7\x45\x15\x0d\xa0\x0e\xe4\x0c\x06\x82\xb5\x75\x8f\xe0\x55\x20\x38\xf1\xad\xa8\x3a\xdc\x28\x07\x15\x5b\x62\xcb\x52\x09\x3a\xdd\xa6\x65\xe5\x40\xb6\x41\x91\x1f\x6c\xe4\x80\x8c\x4b\xcc\x6d\xcf\x72\xef\xe2\x11\x8e\x13\x25\x03\x50\x53\x4a\x01\x6b\x9d\x54\x06\x43\x0c\x78\x64\x4c\x23\x5b\x91\xfe\x84\x42\x9d\xa1\x31\x2d\x6a\x38\xc1\x00\x9a\xd0\x87\x53\x70\x54\x11\x3a\x8f\x3a\x57\x8e\x11\x63\x72\x98\x17\x30\x22\x0e\x0e\xbb\xb4\xcc\xdb\x36\x24\x3a\x8e\xde\x1e\x80\x13\x36\x0d\x45\x02\xee\x83\xe3\x07\x64\x5c\xf5\x4b\x1d\x75\x96\x25\x89\x00\xa2\xf5\xc1\xd6\x8c\x5d\x86\x37\xba\x60\xf8\x7b\xd1\x06\xa8\x70\x45\x60\x2c\x07\x81\x37\xda\x5d\x9e\x8c\x91\xf5\x1b\x1f\xa8\xf6\x7f\x4e\xf4\xcb\x06\xa0\xa7\x79\xca\xa2\xee\xec\x04\xe2\x44\xe4\x9d\xbb\xed\xda\x4c\x2c\x1d\x53\x45\x8d\x8f\x04\x9e\xc9\x3a\x2a\x1a\x2a\xda\x80\xc0\xbc\x3f\xa2\x40\x99\x40\x8e\x33\x94\x29\x7c\x47\xb1\xe8\x7c\x15\xb2\x72\x8b\xa7\x9c\xa0\x6d\x2b\xb7\x2b\xc5\x50\x6a\x3a\x50\x0b\x1b\xab\xcd\x64\x53\xe4\xbf\x79\x57\x6c\x39\x02\xec\x39\x8e\x8e\x56\xb5\x0a\x09\x9f\x7f\xb5\x76\xa9\x69\x0e\x7f\x23\x67\x1f\xdb\x54\x4b\x2f\x6b\xfc\x68\x0d\xfc\x4a\x05\xdc\x76\xd5\xa8\x6b\x25\xc6\x66\x48\x6a\xc8\x48\xcf\xbe\x8f\x60\x1e\x55\x3f\xfb\x3c\x7f\xc6\x22\xd4\x75\x86\x8c\xa3\x17\x37\xd4\x67\xe0\x8b\x71\x3e\x1f\x68\xbc\x54\xcd\xbd\xc1\x90\x53\xca\x43\x6b\x32\x41\xcc\x0f\xa5\x9e\xaf\x38\xb9\x0b\x82\xd6\x93\x84\x22\x56\x26\xb5\xe4\x4d\x72\xa0\xd0\x5d\x7b\xa3\xd0\x7d\xe7\x29\x9f\x78\x83\x6b\x47\x80\x7b\xe6\xe9\xa8\x31\x4a\xe9\xb8\xd2\xd8\x64\xc0\xbe\x47\x7b\x08\x4d\x1d\xbb\x56\x5a\xb3\x72\x0f\xd3\x84\x1f\xe8\x7d\x5c\x35\x1c\x35\xd6\x05\xfe\x76\x48\xc8\x2e\xc7\xe7\xee\x6b\x50\x76\x38\x3c\xa8\xa0\x69\x97\x3e\x92\x9c\x98\xc6\x07\x45\x3d\xa4\x72\xf1\xe2\x75\x5f\x6d\x7b\xf4\xcc\xe1\xef\xb4\x81\x3b\xf4\x8f\xb1\x51\x9a\xf7\x3f\xc0\x2f\xb1\x42\x5f\x2e\x1d\xc5\x86\xca\xc3\xc9\xed\x2f\x97\xfe\xf4\xc5\xc4\xf0\x81\x41\x72\x6f\x94\xcb\x29\xf7\xd5\xdc\xd1\x30\x09\x0b\xd6\x57\x52\xe7\x98\x82\x86\xaa\xcf\x06\xe4\xca\x6f\x1b\x4a\x3d\x83\x4f\x68\x8f\xf9\xaf\xea\x9a\xa4\xc2\x40\x7a\x33\xe9\x06\x4e\x94\x61\xa4\xd9\xc8\xae\xd6\x49\x72\xa7\x3c\x80\x44\xf5\xf8\xf3\x72\x31\x72\x62\x96\x77\x58\xcf\x54\x83\x59\xa2\xa3\x1a\xb9\x9c\xb4\xc6\xb0\x69\x3b\x51\x7a\x38\xe0\x83\x47\xda\x40\xe8\x3d\x09\xec\xae\x5c\x86\x47\x0e\x78\xaa\xb0\x33\x2b\x00\x0a\x61\x5d\x6a\x44\xf6\x32\x55\x84\x7c\x1e\x1f\xd8\x5d\x39\x50\xc9\x75\x9a\x96\xa8\xe7\x31\xb7\x99\xef\xb8\x7c\x44\xa8\x2c\x5b\x8d\xc1\x72\x97\x5b\x68\xb5\x4c\xce\xee\xb8\x7e\xe2\x34\x5b\x8e\xcc\x39\x4f\xc6\x0c\x1e\x2a\x08\x1e\xa9\x09\x43\xc0\x58\x56\x54\x94\xc1\xd9\xf5\x87\x79\x46\x7a\xc8\xad\xca\x97\xf5\x96\x9c\xbf\xf7\xbe\x6b\xbd\x6a\xfc\xa0\xea\xb6\x06\xac\x6d\x6b\x42\x6e\x2a\x0b\x2a\x99\x5e\x73\x7c\x87\x8a\xd0\x2b\xda\x03\x66\x9e\xf8\x3f\xe6\x17\xb8\x4c\x67\xd1\x5b\x4b\x0a\x31\x81\x76\x60\x72\x3e\x81\x08\x77\x3e\x60\x0d\x1b\xf5\xb0\x80\x9b\xe9\x19\x49\x4e\x6e\x79\x9f\x81\xb8\x01\x1c\xa9\xa5\x90\xc3\x60\xc1\x23\x20\x9b\xd7\x90\x53\x56\x76\xc9\xfb\xd6\x06\x55\x2a\x91\x45\x5c\x73\x5d\x4f\x03\xf5\x94\x98\xa7\xb0\x1c\x08\xa4\x17\x68\xe2\x41\x2c\xaf\x22\x33\x1d\x3c\x55\x3a\x8e\x64\x06\x45\x86\x04\x73\xf3\x70\xd6\xc9\x50\xbb\x12\x2f\x9d\x4e\xf1\x91\x65\xd8\xb2\x3b\x3c\x2b\x0a\x43\x02\xdd\xa7\x03\x77\x38\xcd\x8c\x4c\xf5\xe3\x3a\x19\x65\x3d\xec\xeb\xa2\xe2\x23\x9b\xb8\x67\xc2\x9a\xac\x3b\x17\x80\xc2\xae\xa8\x73\x64\x74\xdc\x1b\x42\xc6\xda\x8e\xeb\x7a\x67\x44\x43\xac\x49\xac\x23\xfb\xcd\xa9\x2e\xf5\x6a\xe7\xd6\x3e\xcd\x84\x80\x6d\xa8\x12\x21\xc5\xbb\x89\xfe\x99\xa4\x91\x46\x33\xb6\xe4\xbd\x2d\x3a\x6b\xc6\x6d\x46\x62\xb2\xbd\x2d\xfb\x48\x87\xbe\x0b\x1e\x00\xc8\xbf\xa8\xa0\x3a\x98\x6d\x83\x0f\xce\x07\xe4\xf5\x49\xcd\x2d\xae\xa4\x80\x4a\xf7\xde\xc9\xb1\x2b\x5b\xad\x37\x23\x58\xa2\x91\xe7\x91\x3d\x52\x26\xed\xad\xfc\x1c\x12\x1f\xb8\x3e\x76\xa4\x60\x96\x91\xc8\x0c\xa8\xba\xc1\x58\x13\xbd\x20\x83\x4e\xd9\x79\xf6\x47\x9c\x79\xd2\x53\x3d\xa5\x60\x22\x80\xae\xfa\x4a\xe5\x85\xa3\x1e\xf0\xc7\x72\xfb\x57\xe4\xcb\x28\x28\xfb\x68\xf4\x2b\x73\x66\x40\xe8\x7e\x0e\xed\x3a\x64\x23\x09\x57\x8c\x18\xdb\xc7\x28\x76\xb1\x86\xa7\x10\xdd\xcd\xda\xe3\xdb\x95\xa1\xce\x32\x08\xbc\x65\x00\xf1\x48\xeb\x63\x36\xa5\x24\xb9\x84\xbe\x0e\x38\xd2\xb4\x42\x13\xa0\xb1\xca\xa4\x7b\x9a\x7e\xce\x2d\x6c\xa8\x52\x4f\x6d\x30\xcd\xb6\xf4\xa1\xfb\xa3\xeb\x45\xb6\x21\x42\x46\x68\xeb\xd3\xbc\x76\xd9\x70\x9b\xaa\x3e\xc0\x65\x12\xf9\xa6\xaf\x8c\xc2\x29\xaf\x38\xed\x03\x3f\xb2\x52\xa1\x6b\x14\x04\xb9\x80\xe3\x41\x2f\x21\x39\xa5\x47\x43\x8e\xc7\x2e\x92\xe0\xd4\xb2\x0a\x80\x6b\xdc\x6c\x87\x96\xfd\x19\xad\x76\x3c\x7d\xd3\x59\x07\x9c\x8f\xc3\x2d\x50\x2c\xc1\x5b\x17\x79\xd3\xfa\x3c\x4c\x46\xf7\x03\x96\x52\x20\x78\xa2\x8d\x21\xf7\x81\x1a\x0f\x5c\x09\x53\x3c\x7b\x4e\xdf\x3d\xb2\xa3\xc6\x2e\xed\x47\xf7\x94\x7b\x2b\xfe\xf8\x5e\x61\x94\xd0\x9f\xb9\xab\x5a\x44\x65\xf7\x69\xac\x3c\x8c\xcc\xef\xe7\xa7\xd6\x2d\xd9\x01\x4d\xeb\x44\x85\x3e\x0d\x85\xf9\x5e\x60\x7a\x0b\xd6\x36\xfc\xd4\x7d\x2a\xc8\x0f\x5f\x20\x48\xd8\xba\x6e\x0d\xb3\x7b\xd7\xdd\x08\xad\xb8\x73\xf9\x9a\x23\x62\xaa\xb7\x85\x56\xe2\x6b\x4e\x49\x27\x64\x3e\x61\x3a\xed\x6f\x03\x7c\xe0\x5d\xb1\xb5\xb2\xf1\xa2\x88\xa3\x64\x52\x6b\x1c\x57\x71\x0d\xc4\xe1\x11\xdd\x5d\xe5\x17\x68\x91\xf2\xb1\xd3\xe1\xde\x60\xbc\x71\xc8\x53\xdb\xb9\x6d\x43\xbc\xee\x88\xe4\x94\x46\xed\x6d\x77\xa3\x94\xe7\xb5\x95\xaa\xdc\x9c\x3b\xaa\x99\xcb\x7b\x19\x5d\x9e\xf7\x08\xf4\x24\x52\xc1\x18\x5d\xe2\x26\x6e\xb9\x4d\x68\x15\xe3\x3b\x44\x46\x31\x27\xde\xe4\xde\x36\xcd\xc6\x9f\xbd\x20\x4d\xf9\x15\x6f\xb7\xa6\x99\xfe\x6a\x31\x9b\xfd\xa3\xc1\x25\x15\x8e\xf0\x11\x78\xca\x1c\x58\xe0\x82\x5b\xaa\x44\x35\xd7\x99\x6a\x5e\x27\xaa\x99\xcd\xae\x7a\x9a\xe9\x56\xfc\x6c\xf6\xc3\xd9\xce\x67\xcf\xd2\xbe\xb5\x67\x2d\xfd\x30\xfb\xd4\xf1\xf9\x5b\x0e\x49\xfa\xa4\xa5\x9f\x6d\x01\x77\x1c\xcf\xbc\x76\x5d\x59\x43\xf0\xb6\xad\x0b\x72\x69\xe9\xa7\x1a\x95\x86\xcb\x6e\xd6\x04\xf8\x74\x99\xe7\xb2\xce\x84\x4f\xb3\x1f\xfe\xb2\xf3\xd9\xb3\xb4\x6f\xed\x59\x4b\x83\x05\xa3\xcf\x9e\xa5\x7d\x6b\xcf\x5a\xfa\x74\x7c\x01\xdf\x2c\xc8\x47\xb3\xe0\xb7\xef\xa2\xd9\x4f\x1f\xbe\x27\xdf\xf7\xe4\x3b\x62\x90\x8f\x66\xc1\x6f\xdf\x45\xa3\x82\x39\xa9\x97\xaf\x2e\x86\xeb\x93\xdb\x5c\xaa\x27\x2f\x1f\xba\x52\x7d\x35\x2d\xd5\xbf\xcf\xb7\x7f\xb3\xd9\x6e\xd5\x1f\xbd\x36\xe7\x32\xde\xbf\x1c\x8c\x75\x3c\xcd\x8b\x50\x90\xb6\xeb\x8b\xbd\x14\xf0\x79\x27\x1c\x5e\xde\x36\x3a\x79\x7a\xf8\x4f\x0a\xbd\x52\x7d\x18\xae\xd3\x24\x78\x33\xba\x04\x18\x96\x4f\xfc\x29\xbc\xed\x67\xa9\x5f\x79\x66\x7b\xdd\xbf\xb5\x81\xbd\x69\xff\x79\x02\x38\xbc\xbc\x9b\xf0\xc9\xe5\xf9\xcd\x65\x54\x9f\xe4\xc5\x21\xd8\x1d\x5e\x7e\x62\xd3\x3e\x5c\x3f\x79\xe6\xff\x26\x0a\x8e\x1b\x67\x9e\xe1\xe0\xe5\xc5\x11\xcc\x38\xa2\x8f\x8e\xe8\x92\xb1\x9c\xde\x3d\x3f\x1e\xc3\x3d\xdf\x0e\x43\xc7\x05\xd0\xf7\x94\xfb\x52\x8f\x1d\x2d\xe5\x8e\xec\xa2\xff\x8b\xac\xfb\x66\x38\x3a\xa2\xb7\xfe\x1b\x00\x00\xff\xff\xf3\xab\x76\xcb\x4c\x28\x00\x00")
|
||||
|
||||
func complySoc2PoliciesDisasterMdBytes() ([]byte, error) {
|
||||
return bindataRead(
|
||||
@@ -717,7 +717,7 @@ func complySoc2PoliciesDisasterMd() (*asset, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "comply-soc2/policies/disaster.md", size: 10315, mode: os.FileMode(420), modTime: time.Unix(1526687060, 0)}
|
||||
info := bindataFileInfo{name: "comply-soc2/policies/disaster.md", size: 10316, mode: os.FileMode(420), modTime: time.Unix(1545086630, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
@@ -737,7 +737,7 @@ func complySoc2PoliciesEncryptionMd() (*asset, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "comply-soc2/policies/encryption.md", size: 5381, mode: os.FileMode(420), modTime: time.Unix(1526687060, 0)}
|
||||
info := bindataFileInfo{name: "comply-soc2/policies/encryption.md", size: 5381, mode: os.FileMode(420), modTime: time.Unix(1545086630, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
@@ -757,7 +757,7 @@ func complySoc2PoliciesIncidentMd() (*asset, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "comply-soc2/policies/incident.md", size: 8552, mode: os.FileMode(420), modTime: time.Unix(1526687060, 0)}
|
||||
info := bindataFileInfo{name: "comply-soc2/policies/incident.md", size: 8552, mode: os.FileMode(420), modTime: time.Unix(1545086630, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
@@ -777,7 +777,7 @@ func complySoc2PoliciesInformationMd() (*asset, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "comply-soc2/policies/information.md", size: 5359, mode: os.FileMode(420), modTime: time.Unix(1526687060, 0)}
|
||||
info := bindataFileInfo{name: "comply-soc2/policies/information.md", size: 5359, mode: os.FileMode(420), modTime: time.Unix(1545086630, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
@@ -797,7 +797,7 @@ func complySoc2PoliciesLogMd() (*asset, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "comply-soc2/policies/log.md", size: 4307, mode: os.FileMode(420), modTime: time.Unix(1526687060, 0)}
|
||||
info := bindataFileInfo{name: "comply-soc2/policies/log.md", size: 4307, mode: os.FileMode(420), modTime: time.Unix(1545086630, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
@@ -817,7 +817,7 @@ func complySoc2PoliciesMediaMd() (*asset, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "comply-soc2/policies/media.md", size: 8819, mode: os.FileMode(420), modTime: time.Unix(1526687060, 0)}
|
||||
info := bindataFileInfo{name: "comply-soc2/policies/media.md", size: 8819, mode: os.FileMode(420), modTime: time.Unix(1545086630, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
@@ -837,7 +837,7 @@ func complySoc2PoliciesOfficeMd() (*asset, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "comply-soc2/policies/office.md", size: 3927, mode: os.FileMode(420), modTime: time.Unix(1526687060, 0)}
|
||||
info := bindataFileInfo{name: "comply-soc2/policies/office.md", size: 3927, mode: os.FileMode(420), modTime: time.Unix(1545086630, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
@@ -857,7 +857,7 @@ func complySoc2PoliciesPasswordMd() (*asset, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "comply-soc2/policies/password.md", size: 1796, mode: os.FileMode(420), modTime: time.Unix(1526687060, 0)}
|
||||
info := bindataFileInfo{name: "comply-soc2/policies/password.md", size: 1796, mode: os.FileMode(420), modTime: time.Unix(1545086630, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
@@ -877,7 +877,7 @@ func complySoc2PoliciesPolicyMd() (*asset, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "comply-soc2/policies/policy.md", size: 892, mode: os.FileMode(420), modTime: time.Unix(1526687060, 0)}
|
||||
info := bindataFileInfo{name: "comply-soc2/policies/policy.md", size: 892, mode: os.FileMode(420), modTime: time.Unix(1545086630, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
@@ -897,7 +897,7 @@ func complySoc2PoliciesPrivacyMd() (*asset, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "comply-soc2/policies/privacy.md", size: 346, mode: os.FileMode(420), modTime: time.Unix(1526687060, 0)}
|
||||
info := bindataFileInfo{name: "comply-soc2/policies/privacy.md", size: 346, mode: os.FileMode(420), modTime: time.Unix(1545086630, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
@@ -917,7 +917,7 @@ func complySoc2PoliciesProcessingMd() (*asset, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "comply-soc2/policies/processing.md", size: 210, mode: os.FileMode(420), modTime: time.Unix(1526687060, 0)}
|
||||
info := bindataFileInfo{name: "comply-soc2/policies/processing.md", size: 210, mode: os.FileMode(420), modTime: time.Unix(1545086630, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
@@ -937,7 +937,7 @@ func complySoc2PoliciesRemoteMd() (*asset, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "comply-soc2/policies/remote.md", size: 4119, mode: os.FileMode(420), modTime: time.Unix(1526687060, 0)}
|
||||
info := bindataFileInfo{name: "comply-soc2/policies/remote.md", size: 4119, mode: os.FileMode(420), modTime: time.Unix(1545086630, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
@@ -957,7 +957,7 @@ func complySoc2PoliciesRetentionMd() (*asset, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "comply-soc2/policies/retention.md", size: 6811, mode: os.FileMode(420), modTime: time.Unix(1526687060, 0)}
|
||||
info := bindataFileInfo{name: "comply-soc2/policies/retention.md", size: 6811, mode: os.FileMode(420), modTime: time.Unix(1545086630, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
@@ -977,7 +977,7 @@ func complySoc2PoliciesRiskMd() (*asset, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "comply-soc2/policies/risk.md", size: 10486, mode: os.FileMode(420), modTime: time.Unix(1526687060, 0)}
|
||||
info := bindataFileInfo{name: "comply-soc2/policies/risk.md", size: 10486, mode: os.FileMode(420), modTime: time.Unix(1545086630, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
@@ -997,7 +997,7 @@ func complySoc2PoliciesVendorMd() (*asset, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "comply-soc2/policies/vendor.md", size: 3139, mode: os.FileMode(420), modTime: time.Unix(1526687060, 0)}
|
||||
info := bindataFileInfo{name: "comply-soc2/policies/vendor.md", size: 3139, mode: os.FileMode(420), modTime: time.Unix(1545086630, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
@@ -1017,7 +1017,7 @@ func complySoc2PoliciesWorkstationMd() (*asset, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "comply-soc2/policies/workstation.md", size: 1791, mode: os.FileMode(420), modTime: time.Unix(1526687060, 0)}
|
||||
info := bindataFileInfo{name: "comply-soc2/policies/workstation.md", size: 1791, mode: os.FileMode(420), modTime: time.Unix(1545086630, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
@@ -1037,7 +1037,7 @@ func complySoc2ProceduresReadmeMd() (*asset, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "comply-soc2/procedures/README.md", size: 92, mode: os.FileMode(420), modTime: time.Unix(1526687060, 0)}
|
||||
info := bindataFileInfo{name: "comply-soc2/procedures/README.md", size: 92, mode: os.FileMode(420), modTime: time.Unix(1545086630, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
@@ -1057,7 +1057,7 @@ func complySoc2ProceduresOffboardingMd() (*asset, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "comply-soc2/procedures/offboarding.md", size: 358, mode: os.FileMode(420), modTime: time.Unix(1526687060, 0)}
|
||||
info := bindataFileInfo{name: "comply-soc2/procedures/offboarding.md", size: 358, mode: os.FileMode(420), modTime: time.Unix(1545086630, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
@@ -1077,7 +1077,7 @@ func complySoc2ProceduresOnboardingMd() (*asset, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "comply-soc2/procedures/onboarding.md", size: 495, mode: os.FileMode(420), modTime: time.Unix(1526687060, 0)}
|
||||
info := bindataFileInfo{name: "comply-soc2/procedures/onboarding.md", size: 495, mode: os.FileMode(420), modTime: time.Unix(1545086630, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
@@ -1097,7 +1097,7 @@ func complySoc2ProceduresPatchMd() (*asset, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "comply-soc2/procedures/patch.md", size: 380, mode: os.FileMode(420), modTime: time.Unix(1526687060, 0)}
|
||||
info := bindataFileInfo{name: "comply-soc2/procedures/patch.md", size: 380, mode: os.FileMode(420), modTime: time.Unix(1545086630, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
@@ -1117,7 +1117,7 @@ func complySoc2ProceduresWorkstationMd() (*asset, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "comply-soc2/procedures/workstation.md", size: 1081, mode: os.FileMode(420), modTime: time.Unix(1526687060, 0)}
|
||||
info := bindataFileInfo{name: "comply-soc2/procedures/workstation.md", size: 1081, mode: os.FileMode(420), modTime: time.Unix(1545086630, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
@@ -1137,7 +1137,7 @@ func complySoc2StandardsReadmeMd() (*asset, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "comply-soc2/standards/README.md", size: 282, mode: os.FileMode(420), modTime: time.Unix(1526687060, 0)}
|
||||
info := bindataFileInfo{name: "comply-soc2/standards/README.md", size: 282, mode: os.FileMode(420), modTime: time.Unix(1545086630, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
@@ -1157,7 +1157,7 @@ func complySoc2StandardsTsc2017Yml() (*asset, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "comply-soc2/standards/TSC-2017.yml", size: 16305, mode: os.FileMode(420), modTime: time.Unix(1526687060, 0)}
|
||||
info := bindataFileInfo{name: "comply-soc2/standards/TSC-2017.yml", size: 16305, mode: os.FileMode(420), modTime: time.Unix(1545086630, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
@@ -1177,7 +1177,7 @@ func complySoc2TemplatesDefaultLatex() (*asset, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "comply-soc2/templates/default.latex", size: 7649, mode: os.FileMode(420), modTime: time.Unix(1526687060, 0)}
|
||||
info := bindataFileInfo{name: "comply-soc2/templates/default.latex", size: 7649, mode: os.FileMode(420), modTime: time.Unix(1545086630, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
@@ -1197,7 +1197,7 @@ func complySoc2TemplatesIndexAce() (*asset, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "comply-soc2/templates/index.ace", size: 7596, mode: os.FileMode(420), modTime: time.Unix(1526687060, 0)}
|
||||
info := bindataFileInfo{name: "comply-soc2/templates/index.ace", size: 7596, mode: os.FileMode(420), modTime: time.Unix(1545086630, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
@@ -5,7 +5,9 @@ import (
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/robfig/cron"
|
||||
"github.com/strongdm/comply/internal/config"
|
||||
"github.com/strongdm/comply/internal/model"
|
||||
)
|
||||
|
||||
@@ -68,7 +70,10 @@ func TriggerScheduled() error {
|
||||
// in the future, nothing to do
|
||||
continue
|
||||
}
|
||||
trigger(procedure)
|
||||
err = trigger(procedure)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// don't go back further than 13 months
|
||||
tooOld := time.Now().Add(-1 * time.Hour * 24 * (365 + 30))
|
||||
@@ -88,7 +93,10 @@ func TriggerScheduled() error {
|
||||
}
|
||||
|
||||
// is in the past? then trigger.
|
||||
trigger(procedure)
|
||||
err = trigger(procedure)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
break SEARCH
|
||||
}
|
||||
}
|
||||
@@ -97,13 +105,18 @@ func TriggerScheduled() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func trigger(procedure *model.Procedure) {
|
||||
func trigger(procedure *model.Procedure) error {
|
||||
fmt.Printf("triggering procedure %s (cron expression: %s)\n", procedure.Name, procedure.Cron)
|
||||
|
||||
// TODO: don't hardcode GH
|
||||
tp := model.GetPlugin(model.GitHub)
|
||||
tp.Create(&model.Ticket{
|
||||
ts, err := config.Config().TicketSystem()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error in ticket system configuration")
|
||||
}
|
||||
|
||||
tp := model.GetPlugin(model.TicketSystem(ts))
|
||||
err = tp.Create(&model.Ticket{
|
||||
Name: procedure.Name,
|
||||
Body: fmt.Sprintf("%s\n\n\n---\nProcedure-ID: %s", procedure.Body, procedure.ID),
|
||||
}, []string{"comply", "comply-procedure"})
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -17,6 +17,78 @@ majorRevisions:
|
||||
|
||||
# Control Environment Narrative
|
||||
|
||||
Here we narrate why our control environment satisfies the control keys listed in the YML block
|
||||
The following provides a description of the control structure of {{.Name}}.
|
||||
|
||||
# Template Coming Soon
|
||||
The intent of this description is to enumerate the logical, policy, and procedural controls that serve to monitor {{.Name}}'s application and data security. Changes uncovered by these procedures in the logical, policy, procedural, or customer environment are addressed by remediations specific to the noted change.
|
||||
|
||||
# Logical Controls
|
||||
|
||||
{{.Name}} employs several logical controls to protect confidential data and ensure normal operation of its core product.
|
||||
|
||||
- Mandatory data encryption at rest and in motion
|
||||
- Multi-factor authentication for access to cloud infrastructure
|
||||
- Activity and anomaly monitoring on production systems
|
||||
- Vulnerability management program
|
||||
|
||||
# Policy Controls
|
||||
|
||||
{{.Name}} employs several policy controls to protect confidential data and ensure normal operation of its core product. These policies include, but are not limited to:
|
||||
|
||||
- Access Control Policy
|
||||
- Encryption Policy
|
||||
- Office Security Policy
|
||||
- Password Policy
|
||||
- Policy Training Policy
|
||||
- Vendor Policy
|
||||
- Workstation Policy
|
||||
|
||||
# Procedural Controls
|
||||
|
||||
{{.Name}} has numerous scheduled procedures to monitor and tune the effectiveness of ongoing security controls, and a series of event-driven procedures to respond to security-related events.
|
||||
|
||||
TODO: Finalize these lists
|
||||
|
||||
## Scheduled Security and Audit Procedures
|
||||
|
||||
- Review Access [quarterly]
|
||||
- Review Security Logs [weekly]
|
||||
- Review Cyber Risk Assessment (enumerate possible compromise scenarios) [quarterly]
|
||||
- Review Data Classification [quarterly]
|
||||
- Backup Testing [quarterly]
|
||||
- Disaster Recovery Testing [semi-annual]
|
||||
- Review Devices & Workstations [quarterly]
|
||||
- Review & Clear Low-Priority Alerts [weekly]
|
||||
- Apply OS Patches [monthly]
|
||||
- Verify Data Disposal per Retention Policy [quarterly]
|
||||
- Conduct Security Training [annual]
|
||||
- Review Security Monitoring and Alerting Configuration [quarterly]
|
||||
- Penetration Test [annual]
|
||||
- Whitebox Security Review [annual]
|
||||
- SOC2 Audit [annual]
|
||||
|
||||
## Event-Driven Security and Audit Procedures
|
||||
|
||||
- Onboard Employee
|
||||
- Offboard Employee
|
||||
- Investigate Security Alert
|
||||
- Investigate Security Incident
|
||||
|
||||
# Remediations
|
||||
|
||||
{{.Name}} uses the outcomes of the aforementioned controls and procedures to identify shortcomings in the existing control environment. Once identified, these shortcomes are remediated by improving existing controls and procedures, and creating new controls and procedures as needed.
|
||||
|
||||
# Communications
|
||||
|
||||
{{.Name}} communicates relevant information regarding the functioning of the above controls with internal and external parties on an as-needed basis and according to statutory requirements.
|
||||
|
||||
## Internal
|
||||
|
||||
{{.Name}} communicates control outcomes, anomalies, and remediations internally using the following channels:
|
||||
|
||||
- Slack
|
||||
- Email
|
||||
- Github ticketing
|
||||
|
||||
## External
|
||||
|
||||
{{.Name}} communicates relevant control-related information to external parties including shareholders, customers, contractors, regulators, and government entities as needed according to contractual and regulatory/statutory obligation.
|
||||
@@ -9,4 +9,39 @@ majorRevisions:
|
||||
|
||||
Here we describe the key products marketed by our organization
|
||||
|
||||
# Template Coming Soon
|
||||
# Products
|
||||
|
||||
## Product 1
|
||||
|
||||
Overview of product 1
|
||||
|
||||
### Architecture
|
||||
|
||||
Brief architectural discussion of product 1
|
||||
|
||||
### Security Considerations
|
||||
|
||||
Specific security considerations for product 1. Refer to policies, procedures here.
|
||||
|
||||
# References
|
||||
|
||||
## Narratives
|
||||
|
||||
List relevant narratives, probably including
|
||||
Organizational Narrative
|
||||
Security Narrative
|
||||
System Narrative
|
||||
|
||||
## Policies
|
||||
|
||||
List relevant policies, probably including
|
||||
Application Security Policy
|
||||
Datacenter Policy
|
||||
Log Management Policy
|
||||
Password Policy
|
||||
Security Incident Response Policy
|
||||
Risk Assessment Policy
|
||||
|
||||
## Procedures
|
||||
|
||||
List relevant procedures, probably including access review, patching, alert monitoring, log review, pen testing
|
||||
@@ -15,4 +15,99 @@ majorRevisions:
|
||||
|
||||
Here we narrate why our org satisfies the control keys listed in the YML block
|
||||
|
||||
# Template Coming Soon
|
||||
# {{.Name}} Product Architecture
|
||||
|
||||
Describe product architecture here, emphasizing security implications
|
||||
|
||||
# {{.Name}} Infrastructure
|
||||
|
||||
## Product Infrastructure
|
||||
|
||||
Describe product infrastructure, emphasizing security measures
|
||||
|
||||
### Authorized Personnel
|
||||
|
||||
- **AWS root account** access is granted only to the CTO and CEO
|
||||
- **AWS IAM** access is granted to to a limited group of **Operators**
|
||||
- **{{.Name}} SSH** access is granted to a limited group of **Operators**
|
||||
- **{{.Name}} DB** access is granted to a limited group of **Data Operators**
|
||||
|
||||
## IT Infrastructure
|
||||
|
||||
{{.Name}} uses the following cloud services for its internal infrastructure:
|
||||
|
||||
- List cloud services
|
||||
|
||||
Access to these cloud services is limited according to the role of the {{.Name}} employee and is reviewed quarterly as well as via regular onboarding/offboarding tasks for new and departing employees.
|
||||
|
||||
# {{.Name}} Workstations
|
||||
|
||||
{{.Name}} workstations are hardened against logical and physical attack by the following measures:
|
||||
|
||||
- operating system must be within one generation of current
|
||||
- full-disk encryption
|
||||
- onboard antivirus/antimalware software
|
||||
- OS and AV automatically updated
|
||||
|
||||
Workstation compliance with these measures is evaluated on a quarterly basis.
|
||||
|
||||
## Remote Access
|
||||
|
||||
Many {{.Name}} employees work remotely on a regular basis and connect to production and internal IT systems via the same methods as those employees connecting from the {{.Name}} physical office, i.e., direct encrypted access to cloud services. It is the employee's responsibility to ensure that only authorized personnel use {{.Name}} resources and access {{.Name}} systems.
|
||||
|
||||
# Access Review
|
||||
|
||||
Access to {{.Name}} infrastructure, both internal and product, is reviewed quarterly and inactive users are removed. Any anomalies are reported to the security team for further investigation. When employees start or depart, an onboarding/offboarding procedure is followed to provision or deprovision appropriate account access.
|
||||
|
||||
# Penetration Testing
|
||||
|
||||
{{.Name}} commissions an external penetration test on an annual basis. All findings are immediately reviewed and addressed to the satisfaction of the CTO/CEO.
|
||||
|
||||
# {{.Name}} Physical Security
|
||||
|
||||
{{.Name}} has one physical location, in San Francisco, CA. Key issuance is tracked by the Office Physical Security Policy Ledger. Office keys are additionally held by the lessor, property management, and custodial staff. These keys are not tracked by the Office Physical Security Policy Ledger. {{.Name}} managers regularly review physical access privileges.
|
||||
|
||||
{{.Name}} infrastructure is located within AWS. {{.Name}} does not have physical access to AWS infrastructure.
|
||||
|
||||
# Risk Assessment
|
||||
|
||||
{{.Name}} updates its Cyber Risk Assessment on an annual basis in order to keep pace with the evolving threat landscape. The following is an inventory of adversarial and non-adversarial threats assessed to be of importance to {{.Name}}.
|
||||
|
||||
## Adversarial Threats
|
||||
|
||||
The following represents the inventory of adversarial threats:
|
||||
|
||||
|Threat|Source|Vector|Target|Likelihood|Severity|
|
||||
|----------------------------+--------------+------------+-----------------+----------+------|
|
||||
| | | | | | |
|
||||
|
||||
## Non-Adversarial Threats
|
||||
|
||||
The following represents the inventory of non-adversarial threats:
|
||||
|
||||
|Threat|Vector|Target|Likelihood|Severity|
|
||||
|----------------------------+--------------+-------------+----------+------|
|
||||
| | | | | |
|
||||
|
||||
# References
|
||||
|
||||
## Narratives
|
||||
|
||||
Products and Services Narrative
|
||||
System Architecture Narrative
|
||||
|
||||
## Policies
|
||||
|
||||
Encryption Policy
|
||||
Log Management Policy
|
||||
Office Security Policy
|
||||
Remote Access Policy
|
||||
Security Incident Response Policy
|
||||
Workstation Policy
|
||||
|
||||
## Procedures
|
||||
|
||||
Apply OS Patches
|
||||
Review & Clear Low-Priority Alerts
|
||||
Review Access
|
||||
Review Devices & Workstations
|
||||
|
||||
@@ -9,5 +9,49 @@ majorRevisions:
|
||||
- date: Jun 1 2018
|
||||
comment: Initial document
|
||||
---
|
||||
# Purpose and Scope
|
||||
|
||||
a. The purpose of this policy to define procedures to onboard and offboard users to technical infrastructure in a manner that minimizes the risk of information loss or exposure.
|
||||
|
||||
a. This policy applies to all technical infrastructure within the organization.
|
||||
|
||||
a. This policy applies to all full-time and part-time employees and contractors.
|
||||
|
||||
# Background
|
||||
|
||||
a. In order to minimize the risk of information loss or exposure (from both inside and outside the organization), the organization is reliant on the principle of least privilege. Account creation and permission levels are restricted to only the resources absolutely needed to perform each person’s job duties. When a user’s role within the organization changes, those accounts and permission levels are changed/revoked to fit the new role and disabled when the user leaves the organization altogether.
|
||||
|
||||
# Policy
|
||||
|
||||
a. *During onboarding:*
|
||||
|
||||
i. Hiring Manager informs HR upon hire of a new employee.
|
||||
|
||||
i. HR emails IT to inform them of a new hire and their role.
|
||||
|
||||
i. IT creates a checklist of accounts and permission levels needed for that role.
|
||||
|
||||
i. The owner of each resource reviews and approves account creation and the
|
||||
associated permissions.
|
||||
|
||||
i. IT works with the owner of each resource to set up the user.
|
||||
|
||||
a. *During offboarding:*
|
||||
|
||||
i. Hiring Manager notifies HR when an employee has been terminated.
|
||||
|
||||
i. HR sends a weekly email report to IT summarizing list of users terminated and instructs IT to disable their access.
|
||||
|
||||
i. IT terminates access within five business days from receipt of notification.
|
||||
|
||||
a. *When an employee changes roles within the organization:*
|
||||
|
||||
i. Hiring Manager will inform HR of a change in role.
|
||||
|
||||
i. HR and IT will follow the same steps as outlined in the onboarding and offboarding procedures.
|
||||
|
||||
a. *Review of accounts and permissions:*
|
||||
|
||||
i. Each month, IT and HR will review accounts and permission levels for accuracy.
|
||||
|
||||
|
||||
# Coming Soon
|
||||
@@ -8,7 +8,8 @@ majorRevisions:
|
||||
- date: Jun 1 2018
|
||||
comment: Initial document
|
||||
---
|
||||
#Purpose and Scope
|
||||
|
||||
# Purpose and Scope
|
||||
|
||||
a. The purpose of this policy is to define the organization’s procedures to recover Information Technology (IT) infrastructure and IT services within set deadlines in the case of a disaster or other disruptive incident. The objective of this plan is to complete the recovery of IT infrastructure and IT services within a set Recovery Time Objective (RTO).
|
||||
|
||||
|
||||
29
vendor/github.com/andygrunwald/go-jira/.gitignore
generated
vendored
Normal file
29
vendor/github.com/andygrunwald/go-jira/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Don't check in vendor
|
||||
vendor/
|
||||
|
||||
# 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
|
||||
*.iml
|
||||
.idea
|
||||
17
vendor/github.com/andygrunwald/go-jira/.travis.yml
generated
vendored
Normal file
17
vendor/github.com/andygrunwald/go-jira/.travis.yml
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
language: go
|
||||
|
||||
sudo: false
|
||||
|
||||
go:
|
||||
- 1.4
|
||||
- 1.5
|
||||
- 1.6
|
||||
- 1.7
|
||||
- 1.8
|
||||
- 1.9
|
||||
|
||||
before_install:
|
||||
- go get -t ./...
|
||||
|
||||
script:
|
||||
- GOMAXPROCS=4 GORACE="halt_on_error=1" go test -race -v ./...
|
||||
36
vendor/github.com/andygrunwald/go-jira/Gopkg.lock
generated
vendored
Normal file
36
vendor/github.com/andygrunwald/go-jira/Gopkg.lock
generated
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/fatih/structs"
|
||||
packages = ["."]
|
||||
revision = "a720dfa8df582c51dee1b36feabb906bde1588bd"
|
||||
version = "v1.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/google/go-querystring"
|
||||
packages = ["query"]
|
||||
revision = "53e6ce116135b80d037921a7fdd5138cf32d7a8a"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/pkg/errors"
|
||||
packages = ["."]
|
||||
revision = "645ef00459ed84a119197bfb8d8205042c6df63d"
|
||||
version = "v0.8.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/trivago/tgo"
|
||||
packages = [
|
||||
"tcontainer",
|
||||
"treflect"
|
||||
]
|
||||
revision = "e4d1ddd28c17dd89ed26327cf69fded22060671b"
|
||||
version = "v1.0.1"
|
||||
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "e84ca9eea6d233e0947b0d760913db2983fd4cbf6fd0d8690c737a71affb635c"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
||||
46
vendor/github.com/andygrunwald/go-jira/Gopkg.toml
generated
vendored
Normal file
46
vendor/github.com/andygrunwald/go-jira/Gopkg.toml
generated
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
# Gopkg.toml example
|
||||
#
|
||||
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
|
||||
# for detailed Gopkg.toml documentation.
|
||||
#
|
||||
# required = ["github.com/user/thing/cmd/thing"]
|
||||
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project"
|
||||
# version = "1.0.0"
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project2"
|
||||
# branch = "dev"
|
||||
# source = "github.com/myfork/project2"
|
||||
#
|
||||
# [[override]]
|
||||
# name = "github.com/x/y"
|
||||
# version = "2.4.0"
|
||||
#
|
||||
# [prune]
|
||||
# non-go = false
|
||||
# go-tests = true
|
||||
# unused-packages = true
|
||||
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/fatih/structs"
|
||||
version = "1.0.0"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/google/go-querystring"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/pkg/errors"
|
||||
version = "0.8.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/trivago/tgo"
|
||||
version = "1.0.1"
|
||||
|
||||
[prune]
|
||||
go-tests = true
|
||||
unused-packages = true
|
||||
22
vendor/github.com/andygrunwald/go-jira/LICENSE
generated
vendored
Normal file
22
vendor/github.com/andygrunwald/go-jira/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Andy Grunwald
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
2
vendor/github.com/andygrunwald/go-jira/Makefile
generated
vendored
Normal file
2
vendor/github.com/andygrunwald/go-jira/Makefile
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
test:
|
||||
go test -v ./...
|
||||
271
vendor/github.com/andygrunwald/go-jira/README.md
generated
vendored
Normal file
271
vendor/github.com/andygrunwald/go-jira/README.md
generated
vendored
Normal file
@@ -0,0 +1,271 @@
|
||||
# go-jira
|
||||
|
||||
[](https://godoc.org/github.com/andygrunwald/go-jira)
|
||||
[](https://travis-ci.org/andygrunwald/go-jira)
|
||||
[](https://goreportcard.com/report/github.com/andygrunwald/go-jira)
|
||||
|
||||
[Go](https://golang.org/) client library for [Atlassian JIRA](https://www.atlassian.com/software/jira).
|
||||
|
||||

|
||||
|
||||
## Features
|
||||
|
||||
* Authentication (HTTP Basic, OAuth, Session Cookie)
|
||||
* Create and retrieve issues
|
||||
* Create and retrieve issue transitions (status updates)
|
||||
* Call every API endpoint of the JIRA, even if it is not directly implemented in this library
|
||||
|
||||
This package is not JIRA API complete (yet), but you can call every API endpoint you want. See [Call a not implemented API endpoint](#call-a-not-implemented-api-endpoint) how to do this. For all possible API endpoints of JIRA have a look at [latest JIRA REST API documentation](https://docs.atlassian.com/jira/REST/latest/).
|
||||
|
||||
## Compatible JIRA versions
|
||||
|
||||
This package was tested against JIRA v6.3.4 and v7.1.2.
|
||||
|
||||
## Installation
|
||||
|
||||
It is go gettable
|
||||
|
||||
$ go get github.com/andygrunwald/go-jira
|
||||
|
||||
For stable versions you can use one of our tags with [gopkg.in](http://labix.org/gopkg.in). E.g.
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
jira "gopkg.in/andygrunwald/go-jira.v1"
|
||||
)
|
||||
...
|
||||
```
|
||||
|
||||
(optional) to run unit / example tests:
|
||||
|
||||
$ cd $GOPATH/src/github.com/andygrunwald/go-jira
|
||||
$ go test -v ./...
|
||||
|
||||
## API
|
||||
|
||||
Please have a look at the [GoDoc documentation](https://godoc.org/github.com/andygrunwald/go-jira) for a detailed API description.
|
||||
|
||||
The [latest JIRA REST API documentation](https://docs.atlassian.com/jira/REST/latest/) was the base document for this package.
|
||||
|
||||
## Examples
|
||||
|
||||
Further a few examples how the API can be used.
|
||||
A few more examples are available in the [GoDoc examples section](https://godoc.org/github.com/andygrunwald/go-jira#pkg-examples).
|
||||
|
||||
### Get a single issue
|
||||
|
||||
Lets retrieve [MESOS-3325](https://issues.apache.org/jira/browse/MESOS-3325) from the [Apache Mesos](http://mesos.apache.org/) project.
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/andygrunwald/go-jira"
|
||||
)
|
||||
|
||||
func main() {
|
||||
jiraClient, _ := jira.NewClient(nil, "https://issues.apache.org/jira/")
|
||||
issue, _, _ := jiraClient.Issue.Get("MESOS-3325", nil)
|
||||
|
||||
fmt.Printf("%s: %+v\n", issue.Key, issue.Fields.Summary)
|
||||
fmt.Printf("Type: %s\n", issue.Fields.Type.Name)
|
||||
fmt.Printf("Priority: %s\n", issue.Fields.Priority.Name)
|
||||
|
||||
// MESOS-3325: Running mesos-slave@0.23 in a container causes slave to be lost after a restart
|
||||
// Type: Bug
|
||||
// Priority: Critical
|
||||
}
|
||||
```
|
||||
|
||||
### Authentication
|
||||
|
||||
The `go-jira` library does not handle most authentication directly. Instead, authentication should be handled within
|
||||
an `http.Client`. That client can then be passed into the `NewClient` function when creating a jira client.
|
||||
|
||||
For convenience, capability for basic and cookie-based authentication is included in the main library.
|
||||
|
||||
#### Basic auth example
|
||||
|
||||
A more thorough, [runnable example](examples/basicauth/main.go) is provided in the examples directory.
|
||||
|
||||
```go
|
||||
func main() {
|
||||
tp := jira.BasicAuthTransport{
|
||||
Username: "username",
|
||||
Password: "password",
|
||||
}
|
||||
|
||||
client, err := jira.NewClient(tp.Client(), "https://my.jira.com")
|
||||
|
||||
u, _, err := client.User.Get("some_user")
|
||||
|
||||
fmt.Printf("\nEmail: %v\nSuccess!\n", u.EmailAddress)
|
||||
}
|
||||
```
|
||||
|
||||
#### Authenticate with session cookie
|
||||
|
||||
A more thorough, [runnable example](examples/cookieauth/main.go) is provided in the examples directory.
|
||||
|
||||
Note: The `AuthURL` is almost always going to have the path `/rest/auth/1/session`
|
||||
|
||||
```go
|
||||
tp := jira.CookieAuthTransport{
|
||||
Username: "username",
|
||||
Password: "password",
|
||||
AuthURL: "https://my.jira.com/rest/auth/1/session",
|
||||
}
|
||||
|
||||
client, err := jira.NewClient(tp.Client(), "https://my.jira.com")
|
||||
u, _, err := client.User.Get("admin")
|
||||
|
||||
fmt.Printf("\nEmail: %v\nSuccess!\n", u.EmailAddress)
|
||||
}
|
||||
```
|
||||
|
||||
#### Authenticate with OAuth
|
||||
|
||||
If you want to connect via OAuth to your JIRA Cloud instance checkout the [example of using OAuth authentication with JIRA in Go](https://gist.github.com/Lupus/edafe9a7c5c6b13407293d795442fe67) by [@Lupus](https://github.com/Lupus).
|
||||
|
||||
For more details have a look at the [issue #56](https://github.com/andygrunwald/go-jira/issues/56).
|
||||
|
||||
### Create an issue
|
||||
|
||||
Example how to create an issue.
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/andygrunwald/go-jira"
|
||||
)
|
||||
|
||||
func main() {
|
||||
base := "https://my.jira.com"
|
||||
tp := jira.CookieAuthTransport{
|
||||
Username: "username",
|
||||
Password: "password",
|
||||
AuthURL: fmt.Sprintf("%s/rest/auth/1/session", base),
|
||||
}
|
||||
|
||||
jiraClient, err := jira.NewClient(tp.Client(), base)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
i := jira.Issue{
|
||||
Fields: &jira.IssueFields{
|
||||
Assignee: &jira.User{
|
||||
Name: "myuser",
|
||||
},
|
||||
Reporter: &jira.User{
|
||||
Name: "youruser",
|
||||
},
|
||||
Description: "Test Issue",
|
||||
Type: jira.IssueType{
|
||||
Name: "Bug",
|
||||
},
|
||||
Project: jira.Project{
|
||||
Key: "PROJ1",
|
||||
},
|
||||
Summary: "Just a demo issue",
|
||||
},
|
||||
}
|
||||
issue, _, err := jiraClient.Issue.Create(&i)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Printf("%s: %+v\n", issue.Key, issue.Fields.Summary)
|
||||
}
|
||||
```
|
||||
|
||||
### Call a not implemented API endpoint
|
||||
|
||||
Not all API endpoints of the JIRA API are implemented into *go-jira*.
|
||||
But you can call them anyway:
|
||||
Lets get all public projects of [Atlassian`s JIRA instance](https://jira.atlassian.com/).
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/andygrunwald/go-jira"
|
||||
)
|
||||
|
||||
func main() {
|
||||
base := "https://my.jira.com"
|
||||
tp := jira.CookieAuthTransport{
|
||||
Username: "username",
|
||||
Password: "password",
|
||||
AuthURL: fmt.Sprintf("%s/rest/auth/1/session", base),
|
||||
}
|
||||
|
||||
jiraClient, err := jira.NewClient(tp.Client(), base)
|
||||
req, _ := jiraClient.NewRequest("GET", "/rest/api/2/project", nil)
|
||||
|
||||
projects := new([]jira.Project)
|
||||
_, err := jiraClient.Do(req, projects)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
for _, project := range *projects {
|
||||
fmt.Printf("%s: %s\n", project.Key, project.Name)
|
||||
}
|
||||
|
||||
// ...
|
||||
// BAM: Bamboo
|
||||
// BAMJ: Bamboo JIRA Plugin
|
||||
// CLOV: Clover
|
||||
// CONF: Confluence
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
## Implementations
|
||||
|
||||
* [andygrunwald/jitic](https://github.com/andygrunwald/jitic) - The JIRA Ticket Checker
|
||||
|
||||
## Code structure
|
||||
|
||||
The code structure of this package was inspired by [google/go-github](https://github.com/google/go-github).
|
||||
|
||||
There is one main part (the client).
|
||||
Based on this main client the other endpoints, like Issues or Authentication are extracted in services. E.g. `IssueService` or `AuthenticationService`.
|
||||
These services own a responsibility of the single endpoints / usecases of JIRA.
|
||||
|
||||
## Contribution
|
||||
|
||||
Contribution, in any kind of way, is highly welcome!
|
||||
It doesn't matter if you are not able to write code.
|
||||
Creating issues or holding talks and help other people to use [go-jira](https://github.com/andygrunwald/go-jira) is contribution, too!
|
||||
A few examples:
|
||||
|
||||
* Correct typos in the README / documentation
|
||||
* Reporting bugs
|
||||
* Implement a new feature or endpoint
|
||||
* Sharing the love if [go-jira](https://github.com/andygrunwald/go-jira) and help people to get use to it
|
||||
|
||||
If you are new to pull requests, checkout [Collaborating on projects using issues and pull requests / Creating a pull request](https://help.github.com/articles/creating-a-pull-request/).
|
||||
|
||||
### Dependency management
|
||||
|
||||
`go-jira` uses `dep` for dependency management. After cloning the repo, it's easy to make sure you have the correct dependencies by running `dep ensure`.
|
||||
|
||||
For adding new dependencies, updating dependencies, and other operations, the [Daily Dep](https://golang.github.io/dep/docs/daily-dep.html) is a good place to start.
|
||||
|
||||
### Sandbox environment for testing
|
||||
|
||||
Jira offers sandbox test environments at http://go.atlassian.com/cloud-dev.
|
||||
|
||||
You can read more about them at https://developer.atlassian.com/blog/2016/04/cloud-ecosystem-dev-env/.
|
||||
|
||||
## License
|
||||
|
||||
This project is released under the terms of the [MIT license](http://en.wikipedia.org/wiki/MIT_License).
|
||||
187
vendor/github.com/andygrunwald/go-jira/authentication.go
generated
vendored
Normal file
187
vendor/github.com/andygrunwald/go-jira/authentication.go
generated
vendored
Normal file
@@ -0,0 +1,187 @@
|
||||
package jira
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
const (
|
||||
// HTTP Basic Authentication
|
||||
authTypeBasic = 1
|
||||
// HTTP Session Authentication
|
||||
authTypeSession = 2
|
||||
)
|
||||
|
||||
// AuthenticationService handles authentication for the JIRA instance / API.
|
||||
//
|
||||
// JIRA API docs: https://docs.atlassian.com/jira/REST/latest/#authentication
|
||||
type AuthenticationService struct {
|
||||
client *Client
|
||||
|
||||
// Authentication type
|
||||
authType int
|
||||
|
||||
// Basic auth username
|
||||
username string
|
||||
|
||||
// Basic auth password
|
||||
password string
|
||||
}
|
||||
|
||||
// Session represents a Session JSON response by the JIRA API.
|
||||
type Session struct {
|
||||
Self string `json:"self,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Session struct {
|
||||
Name string `json:"name"`
|
||||
Value string `json:"value"`
|
||||
} `json:"session,omitempty"`
|
||||
LoginInfo struct {
|
||||
FailedLoginCount int `json:"failedLoginCount"`
|
||||
LoginCount int `json:"loginCount"`
|
||||
LastFailedLoginTime string `json:"lastFailedLoginTime"`
|
||||
PreviousLoginTime string `json:"previousLoginTime"`
|
||||
} `json:"loginInfo"`
|
||||
Cookies []*http.Cookie
|
||||
}
|
||||
|
||||
// AcquireSessionCookie creates a new session for a user in JIRA.
|
||||
// Once a session has been successfully created it can be used to access any of JIRA's remote APIs and also the web UI by passing the appropriate HTTP Cookie header.
|
||||
// The header will by automatically applied to every API request.
|
||||
// Note that it is generally preferrable to use HTTP BASIC authentication with the REST API.
|
||||
// However, this resource may be used to mimic the behaviour of JIRA's log-in page (e.g. to display log-in errors to a user).
|
||||
//
|
||||
// JIRA API docs: https://docs.atlassian.com/jira/REST/latest/#auth/1/session
|
||||
//
|
||||
// Deprecated: Use CookieAuthTransport instead
|
||||
func (s *AuthenticationService) AcquireSessionCookie(username, password string) (bool, error) {
|
||||
apiEndpoint := "rest/auth/1/session"
|
||||
body := struct {
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
}{
|
||||
username,
|
||||
password,
|
||||
}
|
||||
|
||||
req, err := s.client.NewRequest("POST", apiEndpoint, body)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
session := new(Session)
|
||||
resp, err := s.client.Do(req, session)
|
||||
|
||||
if resp != nil {
|
||||
session.Cookies = resp.Cookies()
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("Auth at JIRA instance failed (HTTP(S) request). %s", err)
|
||||
}
|
||||
if resp != nil && resp.StatusCode != 200 {
|
||||
return false, fmt.Errorf("Auth at JIRA instance failed (HTTP(S) request). Status code: %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
s.client.session = session
|
||||
s.authType = authTypeSession
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// SetBasicAuth sets username and password for the basic auth against the JIRA instance.
|
||||
//
|
||||
// Deprecated: Use BasicAuthTransport instead
|
||||
func (s *AuthenticationService) SetBasicAuth(username, password string) {
|
||||
s.username = username
|
||||
s.password = password
|
||||
s.authType = authTypeBasic
|
||||
}
|
||||
|
||||
// Authenticated reports if the current Client has authentication details for JIRA
|
||||
func (s *AuthenticationService) Authenticated() bool {
|
||||
if s != nil {
|
||||
if s.authType == authTypeSession {
|
||||
return s.client.session != nil
|
||||
} else if s.authType == authTypeBasic {
|
||||
return s.username != ""
|
||||
}
|
||||
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Logout logs out the current user that has been authenticated and the session in the client is destroyed.
|
||||
//
|
||||
// JIRA API docs: https://docs.atlassian.com/jira/REST/latest/#auth/1/session
|
||||
//
|
||||
// Deprecated: Use CookieAuthTransport to create base client. Logging out is as simple as not using the
|
||||
// client anymore
|
||||
func (s *AuthenticationService) Logout() error {
|
||||
if s.authType != authTypeSession || s.client.session == nil {
|
||||
return fmt.Errorf("no user is authenticated")
|
||||
}
|
||||
|
||||
apiEndpoint := "rest/auth/1/session"
|
||||
req, err := s.client.NewRequest("DELETE", apiEndpoint, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Creating the request to log the user out failed : %s", err)
|
||||
}
|
||||
|
||||
resp, err := s.client.Do(req, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error sending the logout request: %s", err)
|
||||
}
|
||||
if resp.StatusCode != 204 {
|
||||
return fmt.Errorf("The logout was unsuccessful with status %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
// If logout successful, delete session
|
||||
s.client.session = nil
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
// GetCurrentUser gets the details of the current user.
|
||||
//
|
||||
// JIRA API docs: https://docs.atlassian.com/jira/REST/latest/#auth/1/session
|
||||
func (s *AuthenticationService) GetCurrentUser() (*Session, error) {
|
||||
if s == nil {
|
||||
return nil, fmt.Errorf("AUthenticaiton Service is not instantiated")
|
||||
}
|
||||
if s.authType != authTypeSession || s.client.session == nil {
|
||||
return nil, fmt.Errorf("No user is authenticated yet")
|
||||
}
|
||||
|
||||
apiEndpoint := "rest/auth/1/session"
|
||||
req, err := s.client.NewRequest("GET", apiEndpoint, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Could not create request for getting user info : %s", err)
|
||||
}
|
||||
|
||||
resp, err := s.client.Do(req, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error sending request to get user info : %s", err)
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
return nil, fmt.Errorf("Getting user info failed with status : %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
ret := new(Session)
|
||||
data, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Couldn't read body from the response : %s", err)
|
||||
}
|
||||
|
||||
err = json.Unmarshal(data, &ret)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Could not unmarshall received user info : %s", err)
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
166
vendor/github.com/andygrunwald/go-jira/board.go
generated
vendored
Normal file
166
vendor/github.com/andygrunwald/go-jira/board.go
generated
vendored
Normal file
@@ -0,0 +1,166 @@
|
||||
package jira
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// BoardService handles Agile Boards for the JIRA instance / API.
|
||||
//
|
||||
// JIRA API docs: https://docs.atlassian.com/jira-software/REST/server/
|
||||
type BoardService struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// BoardsList reflects a list of agile boards
|
||||
type BoardsList struct {
|
||||
MaxResults int `json:"maxResults" structs:"maxResults"`
|
||||
StartAt int `json:"startAt" structs:"startAt"`
|
||||
Total int `json:"total" structs:"total"`
|
||||
IsLast bool `json:"isLast" structs:"isLast"`
|
||||
Values []Board `json:"values" structs:"values"`
|
||||
}
|
||||
|
||||
// Board represents a JIRA agile board
|
||||
type Board struct {
|
||||
ID int `json:"id,omitempty" structs:"id,omitempty"`
|
||||
Self string `json:"self,omitempty" structs:"self,omitempty"`
|
||||
Name string `json:"name,omitempty" structs:"name,omitemtpy"`
|
||||
Type string `json:"type,omitempty" structs:"type,omitempty"`
|
||||
FilterID int `json:"filterId,omitempty" structs:"filterId,omitempty"`
|
||||
}
|
||||
|
||||
// BoardListOptions specifies the optional parameters to the BoardService.GetList
|
||||
type BoardListOptions struct {
|
||||
// BoardType filters results to boards of the specified type.
|
||||
// Valid values: scrum, kanban.
|
||||
BoardType string `url:"boardType,omitempty"`
|
||||
// Name filters results to boards that match or partially match the specified name.
|
||||
Name string `url:"name,omitempty"`
|
||||
// ProjectKeyOrID filters results to boards that are relevant to a project.
|
||||
// Relevance meaning that the JQL filter defined in board contains a reference to a project.
|
||||
ProjectKeyOrID string `url:"projectKeyOrId,omitempty"`
|
||||
|
||||
SearchOptions
|
||||
}
|
||||
|
||||
// Wrapper struct for search result
|
||||
type sprintsResult struct {
|
||||
Sprints []Sprint `json:"values" structs:"values"`
|
||||
}
|
||||
|
||||
// Sprint represents a sprint on JIRA agile board
|
||||
type Sprint struct {
|
||||
ID int `json:"id" structs:"id"`
|
||||
Name string `json:"name" structs:"name"`
|
||||
CompleteDate *time.Time `json:"completeDate" structs:"completeDate"`
|
||||
EndDate *time.Time `json:"endDate" structs:"endDate"`
|
||||
StartDate *time.Time `json:"startDate" structs:"startDate"`
|
||||
OriginBoardID int `json:"originBoardId" structs:"originBoardId"`
|
||||
Self string `json:"self" structs:"self"`
|
||||
State string `json:"state" structs:"state"`
|
||||
}
|
||||
|
||||
// GetAllBoards will returns all boards. This only includes boards that the user has permission to view.
|
||||
//
|
||||
// JIRA API docs: https://docs.atlassian.com/jira-software/REST/cloud/#agile/1.0/board-getAllBoards
|
||||
func (s *BoardService) GetAllBoards(opt *BoardListOptions) (*BoardsList, *Response, error) {
|
||||
apiEndpoint := "rest/agile/1.0/board"
|
||||
url, err := addOptions(apiEndpoint, opt)
|
||||
req, err := s.client.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
boards := new(BoardsList)
|
||||
resp, err := s.client.Do(req, boards)
|
||||
if err != nil {
|
||||
jerr := NewJiraError(resp, err)
|
||||
return nil, resp, jerr
|
||||
}
|
||||
|
||||
return boards, resp, err
|
||||
}
|
||||
|
||||
// GetBoard will returns the board for the given boardID.
|
||||
// This board will only be returned if the user has permission to view it.
|
||||
//
|
||||
// JIRA API docs: https://docs.atlassian.com/jira-software/REST/cloud/#agile/1.0/board-getBoard
|
||||
func (s *BoardService) GetBoard(boardID int) (*Board, *Response, error) {
|
||||
apiEndpoint := fmt.Sprintf("rest/agile/1.0/board/%v", boardID)
|
||||
req, err := s.client.NewRequest("GET", apiEndpoint, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
board := new(Board)
|
||||
resp, err := s.client.Do(req, board)
|
||||
if err != nil {
|
||||
jerr := NewJiraError(resp, err)
|
||||
return nil, resp, jerr
|
||||
}
|
||||
|
||||
return board, resp, nil
|
||||
}
|
||||
|
||||
// CreateBoard creates a new board. Board name, type and filter Id is required.
|
||||
// name - Must be less than 255 characters.
|
||||
// type - Valid values: scrum, kanban
|
||||
// filterId - Id of a filter that the user has permissions to view.
|
||||
// Note, if the user does not have the 'Create shared objects' permission and tries to create a shared board, a private
|
||||
// board will be created instead (remember that board sharing depends on the filter sharing).
|
||||
//
|
||||
// JIRA API docs: https://docs.atlassian.com/jira-software/REST/cloud/#agile/1.0/board-createBoard
|
||||
func (s *BoardService) CreateBoard(board *Board) (*Board, *Response, error) {
|
||||
apiEndpoint := "rest/agile/1.0/board"
|
||||
req, err := s.client.NewRequest("POST", apiEndpoint, board)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
responseBoard := new(Board)
|
||||
resp, err := s.client.Do(req, responseBoard)
|
||||
if err != nil {
|
||||
jerr := NewJiraError(resp, err)
|
||||
return nil, resp, jerr
|
||||
}
|
||||
|
||||
return responseBoard, resp, nil
|
||||
}
|
||||
|
||||
// DeleteBoard will delete an agile board.
|
||||
//
|
||||
// JIRA API docs: https://docs.atlassian.com/jira-software/REST/cloud/#agile/1.0/board-deleteBoard
|
||||
func (s *BoardService) DeleteBoard(boardID int) (*Board, *Response, error) {
|
||||
apiEndpoint := fmt.Sprintf("rest/agile/1.0/board/%v", boardID)
|
||||
req, err := s.client.NewRequest("DELETE", apiEndpoint, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
resp, err := s.client.Do(req, nil)
|
||||
if err != nil {
|
||||
err = NewJiraError(resp, err)
|
||||
}
|
||||
return nil, resp, err
|
||||
}
|
||||
|
||||
// GetAllSprints will returns all sprints from a board, for a given board Id.
|
||||
// This only includes sprints that the user has permission to view.
|
||||
//
|
||||
// JIRA API docs: https://docs.atlassian.com/jira-software/REST/cloud/#agile/1.0/board/{boardId}/sprint
|
||||
func (s *BoardService) GetAllSprints(boardID string) ([]Sprint, *Response, error) {
|
||||
apiEndpoint := fmt.Sprintf("rest/agile/1.0/board/%s/sprint", boardID)
|
||||
req, err := s.client.NewRequest("GET", apiEndpoint, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
result := new(sprintsResult)
|
||||
resp, err := s.client.Do(req, result)
|
||||
if err != nil {
|
||||
err = NewJiraError(resp, err)
|
||||
}
|
||||
|
||||
return result.Sprints, resp, err
|
||||
}
|
||||
82
vendor/github.com/andygrunwald/go-jira/error.go
generated
vendored
Normal file
82
vendor/github.com/andygrunwald/go-jira/error.go
generated
vendored
Normal file
@@ -0,0 +1,82 @@
|
||||
package jira
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// Error message from JIRA
|
||||
// See https://docs.atlassian.com/jira/REST/cloud/#error-responses
|
||||
type Error struct {
|
||||
HTTPError error
|
||||
ErrorMessages []string `json:"errorMessages"`
|
||||
Errors map[string]string `json:"errors"`
|
||||
}
|
||||
|
||||
// NewJiraError creates a new jira Error
|
||||
func NewJiraError(resp *Response, httpError error) error {
|
||||
if resp == nil {
|
||||
return errors.Wrap(httpError, "No response returned")
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, httpError.Error())
|
||||
}
|
||||
|
||||
jerr := Error{HTTPError: httpError}
|
||||
err = json.Unmarshal(body, &jerr)
|
||||
if err != nil {
|
||||
httpError = errors.Wrap(errors.New("Could not parse JSON"), httpError.Error())
|
||||
return errors.Wrap(err, httpError.Error())
|
||||
}
|
||||
|
||||
return &jerr
|
||||
}
|
||||
|
||||
// Error is a short string representing the error
|
||||
func (e *Error) Error() string {
|
||||
if len(e.ErrorMessages) > 0 {
|
||||
// return fmt.Sprintf("%v", e.HTTPError)
|
||||
return fmt.Sprintf("%s: %v", e.ErrorMessages[0], e.HTTPError)
|
||||
}
|
||||
if len(e.Errors) > 0 {
|
||||
for key, value := range e.Errors {
|
||||
return fmt.Sprintf("%s - %s: %v", key, value, e.HTTPError)
|
||||
}
|
||||
}
|
||||
return e.HTTPError.Error()
|
||||
}
|
||||
|
||||
// LongError is a full representation of the error as a string
|
||||
func (e *Error) LongError() string {
|
||||
var msg bytes.Buffer
|
||||
if e.HTTPError != nil {
|
||||
msg.WriteString("Original:\n")
|
||||
msg.WriteString(e.HTTPError.Error())
|
||||
msg.WriteString("\n")
|
||||
}
|
||||
if len(e.ErrorMessages) > 0 {
|
||||
msg.WriteString("Messages:\n")
|
||||
for _, v := range e.ErrorMessages {
|
||||
msg.WriteString(" - ")
|
||||
msg.WriteString(v)
|
||||
msg.WriteString("\n")
|
||||
}
|
||||
}
|
||||
if len(e.Errors) > 0 {
|
||||
for key, value := range e.Errors {
|
||||
msg.WriteString(" - ")
|
||||
msg.WriteString(key)
|
||||
msg.WriteString(" - ")
|
||||
msg.WriteString(value)
|
||||
msg.WriteString("\n")
|
||||
}
|
||||
}
|
||||
return msg.String()
|
||||
}
|
||||
153
vendor/github.com/andygrunwald/go-jira/group.go
generated
vendored
Normal file
153
vendor/github.com/andygrunwald/go-jira/group.go
generated
vendored
Normal file
@@ -0,0 +1,153 @@
|
||||
package jira
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
// GroupService handles Groups for the JIRA instance / API.
|
||||
//
|
||||
// JIRA API docs: https://docs.atlassian.com/jira/REST/server/#api/2/group
|
||||
type GroupService struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// groupMembersResult is only a small wrapper around the Group* methods
|
||||
// to be able to parse the results
|
||||
type groupMembersResult struct {
|
||||
StartAt int `json:"startAt"`
|
||||
MaxResults int `json:"maxResults"`
|
||||
Total int `json:"total"`
|
||||
Members []GroupMember `json:"values"`
|
||||
}
|
||||
|
||||
// Group represents a JIRA group
|
||||
type Group struct {
|
||||
ID string `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Type string `json:"type"`
|
||||
Properties groupProperties `json:"properties"`
|
||||
AdditionalProperties bool `json:"additionalProperties"`
|
||||
}
|
||||
|
||||
type groupProperties struct {
|
||||
Name groupPropertiesName `json:"name"`
|
||||
}
|
||||
|
||||
type groupPropertiesName struct {
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
// GroupMember reflects a single member of a group
|
||||
type GroupMember struct {
|
||||
Self string `json:"self,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Key string `json:"key,omitempty"`
|
||||
EmailAddress string `json:"emailAddress,omitempty"`
|
||||
DisplayName string `json:"displayName,omitempty"`
|
||||
Active bool `json:"active,omitempty"`
|
||||
TimeZone string `json:"timeZone,omitempty"`
|
||||
}
|
||||
|
||||
type GroupSearchOptions struct {
|
||||
StartAt int
|
||||
MaxResults int
|
||||
IncludeInactiveUsers bool
|
||||
}
|
||||
|
||||
// Get returns a paginated list of users who are members of the specified group and its subgroups.
|
||||
// Users in the page are ordered by user names.
|
||||
// User of this resource is required to have sysadmin or admin permissions.
|
||||
//
|
||||
// JIRA API docs: https://docs.atlassian.com/jira/REST/server/#api/2/group-getUsersFromGroup
|
||||
//
|
||||
// WARNING: This API only returns the first page of group members
|
||||
func (s *GroupService) Get(name string) ([]GroupMember, *Response, error) {
|
||||
apiEndpoint := fmt.Sprintf("/rest/api/2/group/member?groupname=%s", url.QueryEscape(name))
|
||||
req, err := s.client.NewRequest("GET", apiEndpoint, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
group := new(groupMembersResult)
|
||||
resp, err := s.client.Do(req, group)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
|
||||
return group.Members, resp, nil
|
||||
}
|
||||
|
||||
// Get returns a paginated list of members of the specified group and its subgroups.
|
||||
// Users in the page are ordered by user names.
|
||||
// User of this resource is required to have sysadmin or admin permissions.
|
||||
//
|
||||
// JIRA API docs: https://docs.atlassian.com/jira/REST/server/#api/2/group-getUsersFromGroup
|
||||
func (s *GroupService) GetWithOptions(name string, options *GroupSearchOptions) ([]GroupMember, *Response, error) {
|
||||
var apiEndpoint string
|
||||
if options == nil {
|
||||
apiEndpoint = fmt.Sprintf("/rest/api/2/group/member?groupname=%s", url.QueryEscape(name))
|
||||
} else {
|
||||
apiEndpoint = fmt.Sprintf(
|
||||
"/rest/api/2/group/member?groupname=%s&startAt=%d&maxResults=%d&includeInactiveUsers=%t",
|
||||
url.QueryEscape(name),
|
||||
options.StartAt,
|
||||
options.MaxResults,
|
||||
options.IncludeInactiveUsers,
|
||||
)
|
||||
}
|
||||
req, err := s.client.NewRequest("GET", apiEndpoint, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
group := new(groupMembersResult)
|
||||
resp, err := s.client.Do(req, group)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
return group.Members, resp, nil
|
||||
}
|
||||
|
||||
// Add adds user to group
|
||||
//
|
||||
// JIRA API docs: https://docs.atlassian.com/jira/REST/cloud/#api/2/group-addUserToGroup
|
||||
func (s *GroupService) Add(groupname string, username string) (*Group, *Response, error) {
|
||||
apiEndpoint := fmt.Sprintf("/rest/api/2/group/user?groupname=%s", groupname)
|
||||
var user struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
user.Name = username
|
||||
req, err := s.client.NewRequest("POST", apiEndpoint, &user)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
responseGroup := new(Group)
|
||||
resp, err := s.client.Do(req, responseGroup)
|
||||
if err != nil {
|
||||
jerr := NewJiraError(resp, err)
|
||||
return nil, resp, jerr
|
||||
}
|
||||
|
||||
return responseGroup, resp, nil
|
||||
}
|
||||
|
||||
// Remove removes user from group
|
||||
//
|
||||
// JIRA API docs: https://docs.atlassian.com/jira/REST/cloud/#api/2/group-removeUserFromGroup
|
||||
func (s *GroupService) Remove(groupname string, username string) (*Response, error) {
|
||||
apiEndpoint := fmt.Sprintf("/rest/api/2/group/user?groupname=%s&username=%s", groupname, username)
|
||||
req, err := s.client.NewRequest("DELETE", apiEndpoint, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := s.client.Do(req, nil)
|
||||
if err != nil {
|
||||
jerr := NewJiraError(resp, err)
|
||||
return resp, jerr
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
1100
vendor/github.com/andygrunwald/go-jira/issue.go
generated
vendored
Normal file
1100
vendor/github.com/andygrunwald/go-jira/issue.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
432
vendor/github.com/andygrunwald/go-jira/jira.go
generated
vendored
Normal file
432
vendor/github.com/andygrunwald/go-jira/jira.go
generated
vendored
Normal file
@@ -0,0 +1,432 @@
|
||||
package jira
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-querystring/query"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// A Client manages communication with the JIRA API.
|
||||
type Client struct {
|
||||
// HTTP client used to communicate with the API.
|
||||
client *http.Client
|
||||
|
||||
// Base URL for API requests.
|
||||
baseURL *url.URL
|
||||
|
||||
// Session storage if the user authentificate with a Session cookie
|
||||
session *Session
|
||||
|
||||
// Services used for talking to different parts of the JIRA API.
|
||||
Authentication *AuthenticationService
|
||||
Issue *IssueService
|
||||
Project *ProjectService
|
||||
Board *BoardService
|
||||
Sprint *SprintService
|
||||
User *UserService
|
||||
Group *GroupService
|
||||
Version *VersionService
|
||||
}
|
||||
|
||||
// NewClient returns a new JIRA API client.
|
||||
// If a nil httpClient is provided, http.DefaultClient will be used.
|
||||
// To use API methods which require authentication you can follow the preferred solution and
|
||||
// provide an http.Client that will perform the authentication for you with OAuth and HTTP Basic (such as that provided by the golang.org/x/oauth2 library).
|
||||
// As an alternative you can use Session Cookie based authentication provided by this package as well.
|
||||
// See https://docs.atlassian.com/jira/REST/latest/#authentication
|
||||
// baseURL is the HTTP endpoint of your JIRA instance and should always be specified with a trailing slash.
|
||||
func NewClient(httpClient *http.Client, baseURL string) (*Client, error) {
|
||||
if httpClient == nil {
|
||||
httpClient = http.DefaultClient
|
||||
}
|
||||
|
||||
parsedBaseURL, err := url.Parse(baseURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c := &Client{
|
||||
client: httpClient,
|
||||
baseURL: parsedBaseURL,
|
||||
}
|
||||
c.Authentication = &AuthenticationService{client: c}
|
||||
c.Issue = &IssueService{client: c}
|
||||
c.Project = &ProjectService{client: c}
|
||||
c.Board = &BoardService{client: c}
|
||||
c.Sprint = &SprintService{client: c}
|
||||
c.User = &UserService{client: c}
|
||||
c.Group = &GroupService{client: c}
|
||||
c.Version = &VersionService{client: c}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// NewRawRequest creates an API request.
|
||||
// A relative URL can be provided in urlStr, in which case it is resolved relative to the baseURL of the Client.
|
||||
// Relative URLs should always be specified without a preceding slash.
|
||||
// Allows using an optional native io.Reader for sourcing the request body.
|
||||
func (c *Client) NewRawRequest(method, urlStr string, body io.Reader) (*http.Request, error) {
|
||||
rel, err := url.Parse(urlStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
u := c.baseURL.ResolveReference(rel)
|
||||
|
||||
req, err := http.NewRequest(method, u.String(), body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
// Set authentication information
|
||||
if c.Authentication.authType == authTypeSession {
|
||||
// Set session cookie if there is one
|
||||
if c.session != nil {
|
||||
for _, cookie := range c.session.Cookies {
|
||||
req.AddCookie(cookie)
|
||||
}
|
||||
}
|
||||
} else if c.Authentication.authType == authTypeBasic {
|
||||
// Set basic auth information
|
||||
if c.Authentication.username != "" {
|
||||
req.SetBasicAuth(c.Authentication.username, c.Authentication.password)
|
||||
}
|
||||
}
|
||||
|
||||
return req, nil
|
||||
}
|
||||
|
||||
// NewRequest creates an API request.
|
||||
// A relative URL can be provided in urlStr, in which case it is resolved relative to the baseURL of the Client.
|
||||
// Relative URLs should always be specified without a preceding slash.
|
||||
// If specified, the value pointed to by body is JSON encoded and included as the request body.
|
||||
func (c *Client) NewRequest(method, urlStr string, body interface{}) (*http.Request, error) {
|
||||
rel, err := url.Parse(urlStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
u := c.baseURL.ResolveReference(rel)
|
||||
|
||||
var buf io.ReadWriter
|
||||
if body != nil {
|
||||
buf = new(bytes.Buffer)
|
||||
err = json.NewEncoder(buf).Encode(body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(method, u.String(), buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
// Set authentication information
|
||||
if c.Authentication.authType == authTypeSession {
|
||||
// Set session cookie if there is one
|
||||
if c.session != nil {
|
||||
for _, cookie := range c.session.Cookies {
|
||||
req.AddCookie(cookie)
|
||||
}
|
||||
}
|
||||
} else if c.Authentication.authType == authTypeBasic {
|
||||
// Set basic auth information
|
||||
if c.Authentication.username != "" {
|
||||
req.SetBasicAuth(c.Authentication.username, c.Authentication.password)
|
||||
}
|
||||
}
|
||||
|
||||
return req, nil
|
||||
}
|
||||
|
||||
// addOptions adds the parameters in opt as URL query parameters to s. opt
|
||||
// must be a struct whose fields may contain "url" tags.
|
||||
func addOptions(s string, opt interface{}) (string, error) {
|
||||
v := reflect.ValueOf(opt)
|
||||
if v.Kind() == reflect.Ptr && v.IsNil() {
|
||||
return s, nil
|
||||
}
|
||||
|
||||
u, err := url.Parse(s)
|
||||
if err != nil {
|
||||
return s, err
|
||||
}
|
||||
|
||||
qs, err := query.Values(opt)
|
||||
if err != nil {
|
||||
return s, err
|
||||
}
|
||||
|
||||
u.RawQuery = qs.Encode()
|
||||
return u.String(), nil
|
||||
}
|
||||
|
||||
// NewMultiPartRequest creates an API request including a multi-part file.
|
||||
// A relative URL can be provided in urlStr, in which case it is resolved relative to the baseURL of the Client.
|
||||
// Relative URLs should always be specified without a preceding slash.
|
||||
// If specified, the value pointed to by buf is a multipart form.
|
||||
func (c *Client) NewMultiPartRequest(method, urlStr string, buf *bytes.Buffer) (*http.Request, error) {
|
||||
rel, err := url.Parse(urlStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
u := c.baseURL.ResolveReference(rel)
|
||||
|
||||
req, err := http.NewRequest(method, u.String(), buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Set required headers
|
||||
req.Header.Set("X-Atlassian-Token", "nocheck")
|
||||
|
||||
// Set authentication information
|
||||
if c.Authentication.authType == authTypeSession {
|
||||
// Set session cookie if there is one
|
||||
if c.session != nil {
|
||||
for _, cookie := range c.session.Cookies {
|
||||
req.AddCookie(cookie)
|
||||
}
|
||||
}
|
||||
} else if c.Authentication.authType == authTypeBasic {
|
||||
// Set basic auth information
|
||||
if c.Authentication.username != "" {
|
||||
req.SetBasicAuth(c.Authentication.username, c.Authentication.password)
|
||||
}
|
||||
}
|
||||
|
||||
return req, nil
|
||||
}
|
||||
|
||||
// Do sends an API request and returns the API response.
|
||||
// The API response is JSON decoded and stored in the value pointed to by v, or returned as an error if an API error has occurred.
|
||||
func (c *Client) Do(req *http.Request, v interface{}) (*Response, error) {
|
||||
httpResp, err := c.client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = CheckResponse(httpResp)
|
||||
if err != nil {
|
||||
// Even though there was an error, we still return the response
|
||||
// in case the caller wants to inspect it further
|
||||
return newResponse(httpResp, nil), err
|
||||
}
|
||||
|
||||
if v != nil {
|
||||
// Open a NewDecoder and defer closing the reader only if there is a provided interface to decode to
|
||||
defer httpResp.Body.Close()
|
||||
err = json.NewDecoder(httpResp.Body).Decode(v)
|
||||
}
|
||||
|
||||
resp := newResponse(httpResp, v)
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// CheckResponse checks the API response for errors, and returns them if present.
|
||||
// A response is considered an error if it has a status code outside the 200 range.
|
||||
// The caller is responsible to analyze the response body.
|
||||
// The body can contain JSON (if the error is intended) or xml (sometimes JIRA just failes).
|
||||
func CheckResponse(r *http.Response) error {
|
||||
if c := r.StatusCode; 200 <= c && c <= 299 {
|
||||
return nil
|
||||
}
|
||||
|
||||
err := fmt.Errorf("Request failed. Please analyze the request body for more details. Status code: %d", r.StatusCode)
|
||||
return err
|
||||
}
|
||||
|
||||
// GetBaseURL will return you the Base URL.
|
||||
// This is the same URL as in the NewClient constructor
|
||||
func (c *Client) GetBaseURL() url.URL {
|
||||
return *c.baseURL
|
||||
}
|
||||
|
||||
// Response represents JIRA API response. It wraps http.Response returned from
|
||||
// API and provides information about paging.
|
||||
type Response struct {
|
||||
*http.Response
|
||||
|
||||
StartAt int
|
||||
MaxResults int
|
||||
Total int
|
||||
}
|
||||
|
||||
func newResponse(r *http.Response, v interface{}) *Response {
|
||||
resp := &Response{Response: r}
|
||||
resp.populatePageValues(v)
|
||||
return resp
|
||||
}
|
||||
|
||||
// Sets paging values if response json was parsed to searchResult type
|
||||
// (can be extended with other types if they also need paging info)
|
||||
func (r *Response) populatePageValues(v interface{}) {
|
||||
switch value := v.(type) {
|
||||
case *searchResult:
|
||||
r.StartAt = value.StartAt
|
||||
r.MaxResults = value.MaxResults
|
||||
r.Total = value.Total
|
||||
case *groupMembersResult:
|
||||
r.StartAt = value.StartAt
|
||||
r.MaxResults = value.MaxResults
|
||||
r.Total = value.Total
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// BasicAuthTransport is an http.RoundTripper that authenticates all requests
|
||||
// using HTTP Basic Authentication with the provided username and password.
|
||||
type BasicAuthTransport struct {
|
||||
Username string
|
||||
Password string
|
||||
|
||||
// Transport is the underlying HTTP transport to use when making requests.
|
||||
// It will default to http.DefaultTransport if nil.
|
||||
Transport http.RoundTripper
|
||||
}
|
||||
|
||||
// RoundTrip implements the RoundTripper interface. We just add the
|
||||
// basic auth and return the RoundTripper for this transport type.
|
||||
func (t *BasicAuthTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
req2 := cloneRequest(req) // per RoundTripper contract
|
||||
|
||||
req2.SetBasicAuth(t.Username, t.Password)
|
||||
return t.transport().RoundTrip(req2)
|
||||
}
|
||||
|
||||
// Client returns an *http.Client that makes requests that are authenticated
|
||||
// using HTTP Basic Authentication. This is a nice little bit of sugar
|
||||
// so we can just get the client instead of creating the client in the calling code.
|
||||
// If it's necessary to send more information on client init, the calling code can
|
||||
// always skip this and set the transport itself.
|
||||
func (t *BasicAuthTransport) Client() *http.Client {
|
||||
return &http.Client{Transport: t}
|
||||
}
|
||||
|
||||
func (t *BasicAuthTransport) transport() http.RoundTripper {
|
||||
if t.Transport != nil {
|
||||
return t.Transport
|
||||
}
|
||||
return http.DefaultTransport
|
||||
}
|
||||
|
||||
// CookieAuthTransport is an http.RoundTripper that authenticates all requests
|
||||
// using Jira's cookie-based authentication.
|
||||
//
|
||||
// Note that it is generally preferrable to use HTTP BASIC authentication with the REST API.
|
||||
// However, this resource may be used to mimic the behaviour of JIRA's log-in page (e.g. to display log-in errors to a user).
|
||||
//
|
||||
// JIRA API docs: https://docs.atlassian.com/jira/REST/latest/#auth/1/session
|
||||
type CookieAuthTransport struct {
|
||||
Username string
|
||||
Password string
|
||||
AuthURL string
|
||||
|
||||
// SessionObject is the authenticated cookie string.s
|
||||
// It's passed in each call to prove the client is authenticated.
|
||||
SessionObject []*http.Cookie
|
||||
|
||||
// Transport is the underlying HTTP transport to use when making requests.
|
||||
// It will default to http.DefaultTransport if nil.
|
||||
Transport http.RoundTripper
|
||||
}
|
||||
|
||||
// RoundTrip adds the session object to the request.
|
||||
func (t *CookieAuthTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
if t.SessionObject == nil {
|
||||
err := t.setSessionObject()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "cookieauth: no session object has been set")
|
||||
}
|
||||
}
|
||||
|
||||
req2 := cloneRequest(req) // per RoundTripper contract
|
||||
for _, cookie := range t.SessionObject {
|
||||
req2.AddCookie(cookie)
|
||||
}
|
||||
|
||||
return t.transport().RoundTrip(req2)
|
||||
}
|
||||
|
||||
// Client returns an *http.Client that makes requests that are authenticated
|
||||
// using cookie authentication
|
||||
func (t *CookieAuthTransport) Client() *http.Client {
|
||||
return &http.Client{Transport: t}
|
||||
}
|
||||
|
||||
// setSessionObject attempts to authenticate the user and set
|
||||
// the session object (e.g. cookie)
|
||||
func (t *CookieAuthTransport) setSessionObject() error {
|
||||
req, err := t.buildAuthRequest()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var authClient = &http.Client{
|
||||
Timeout: time.Second * 60,
|
||||
}
|
||||
resp, err := authClient.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
t.SessionObject = resp.Cookies()
|
||||
return nil
|
||||
}
|
||||
|
||||
// getAuthRequest assembles the request to get the authenticated cookie
|
||||
func (t *CookieAuthTransport) buildAuthRequest() (*http.Request, error) {
|
||||
body := struct {
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
}{
|
||||
t.Username,
|
||||
t.Password,
|
||||
}
|
||||
|
||||
b := new(bytes.Buffer)
|
||||
json.NewEncoder(b).Encode(body)
|
||||
|
||||
req, err := http.NewRequest("POST", t.AuthURL, b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
return req, nil
|
||||
}
|
||||
|
||||
func (t *CookieAuthTransport) transport() http.RoundTripper {
|
||||
if t.Transport != nil {
|
||||
return t.Transport
|
||||
}
|
||||
return http.DefaultTransport
|
||||
}
|
||||
|
||||
// cloneRequest returns a clone of the provided *http.Request.
|
||||
// The clone is a shallow copy of the struct and its Header map.
|
||||
func cloneRequest(r *http.Request) *http.Request {
|
||||
// shallow copy of the struct
|
||||
r2 := new(http.Request)
|
||||
*r2 = *r
|
||||
// deep copy of the Header
|
||||
r2.Header = make(http.Header, len(r.Header))
|
||||
for k, s := range r.Header {
|
||||
r2.Header[k] = append([]string(nil), s...)
|
||||
}
|
||||
return r2
|
||||
}
|
||||
194
vendor/github.com/andygrunwald/go-jira/metaissue.go
generated
vendored
Normal file
194
vendor/github.com/andygrunwald/go-jira/metaissue.go
generated
vendored
Normal file
@@ -0,0 +1,194 @@
|
||||
package jira
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/google/go-querystring/query"
|
||||
"github.com/trivago/tgo/tcontainer"
|
||||
)
|
||||
|
||||
// CreateMetaInfo contains information about fields and their attributed to create a ticket.
|
||||
type CreateMetaInfo struct {
|
||||
Expand string `json:"expand,omitempty"`
|
||||
Projects []*MetaProject `json:"projects,omitempty"`
|
||||
}
|
||||
|
||||
// MetaProject is the meta information about a project returned from createmeta api
|
||||
type MetaProject struct {
|
||||
Expand string `json:"expand,omitempty"`
|
||||
Self string `json:"self,omitempty"`
|
||||
Id string `json:"id,omitempty"`
|
||||
Key string `json:"key,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
// omitted avatarUrls
|
||||
IssueTypes []*MetaIssueType `json:"issuetypes,omitempty"`
|
||||
}
|
||||
|
||||
// MetaIssueType represents the different issue types a project has.
|
||||
//
|
||||
// Note: Fields is interface because this is an object which can
|
||||
// have arbitraty keys related to customfields. It is not possible to
|
||||
// expect these for a general way. This will be returning a map.
|
||||
// Further processing must be done depending on what is required.
|
||||
type MetaIssueType struct {
|
||||
Self string `json:"self,omitempty"`
|
||||
Id string `json:"id,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
IconUrl string `json:"iconurl,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Subtasks bool `json:"subtask,omitempty"`
|
||||
Expand string `json:"expand,omitempty"`
|
||||
Fields tcontainer.MarshalMap `json:"fields,omitempty"`
|
||||
}
|
||||
|
||||
// GetCreateMeta makes the api call to get the meta information required to create a ticket
|
||||
func (s *IssueService) GetCreateMeta(projectkeys string) (*CreateMetaInfo, *Response, error) {
|
||||
return s.GetCreateMetaWithOptions(&GetQueryOptions{ProjectKeys: projectkeys, Expand: "projects.issuetypes.fields"})
|
||||
}
|
||||
|
||||
// GetCreateMetaWithOptions makes the api call to get the meta information without requiring to have a projectKey
|
||||
func (s *IssueService) GetCreateMetaWithOptions(options *GetQueryOptions) (*CreateMetaInfo, *Response, error) {
|
||||
apiEndpoint := "rest/api/2/issue/createmeta"
|
||||
|
||||
req, err := s.client.NewRequest("GET", apiEndpoint, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if options != nil {
|
||||
q, err := query.Values(options)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
req.URL.RawQuery = q.Encode()
|
||||
}
|
||||
|
||||
meta := new(CreateMetaInfo)
|
||||
resp, err := s.client.Do(req, meta)
|
||||
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
|
||||
return meta, resp, nil
|
||||
}
|
||||
|
||||
// GetProjectWithName returns a project with "name" from the meta information received. If not found, this returns nil.
|
||||
// The comparison of the name is case insensitive.
|
||||
func (m *CreateMetaInfo) GetProjectWithName(name string) *MetaProject {
|
||||
for _, m := range m.Projects {
|
||||
if strings.ToLower(m.Name) == strings.ToLower(name) {
|
||||
return m
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetProjectWithKey returns a project with "name" from the meta information received. If not found, this returns nil.
|
||||
// The comparison of the name is case insensitive.
|
||||
func (m *CreateMetaInfo) GetProjectWithKey(key string) *MetaProject {
|
||||
for _, m := range m.Projects {
|
||||
if strings.ToLower(m.Key) == strings.ToLower(key) {
|
||||
return m
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetIssueTypeWithName returns an IssueType with name from a given MetaProject. If not found, this returns nil.
|
||||
// The comparison of the name is case insensitive
|
||||
func (p *MetaProject) GetIssueTypeWithName(name string) *MetaIssueType {
|
||||
for _, m := range p.IssueTypes {
|
||||
if strings.ToLower(m.Name) == strings.ToLower(name) {
|
||||
return m
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetMandatoryFields returns a map of all the required fields from the MetaIssueTypes.
|
||||
// if a field returned by the api was:
|
||||
// "customfield_10806": {
|
||||
// "required": true,
|
||||
// "schema": {
|
||||
// "type": "any",
|
||||
// "custom": "com.pyxis.greenhopper.jira:gh-epic-link",
|
||||
// "customId": 10806
|
||||
// },
|
||||
// "name": "Epic Link",
|
||||
// "hasDefaultValue": false,
|
||||
// "operations": [
|
||||
// "set"
|
||||
// ]
|
||||
// }
|
||||
// the returned map would have "Epic Link" as the key and "customfield_10806" as value.
|
||||
// This choice has been made so that the it is easier to generate the create api request later.
|
||||
func (t *MetaIssueType) GetMandatoryFields() (map[string]string, error) {
|
||||
ret := make(map[string]string)
|
||||
for key := range t.Fields {
|
||||
required, err := t.Fields.Bool(key + "/required")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if required {
|
||||
name, err := t.Fields.String(key + "/name")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret[name] = key
|
||||
}
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// GetAllFields returns a map of all the fields for an IssueType. This includes all required and not required.
|
||||
// The key of the returned map is what you see in the form and the value is how it is representated in the jira schema.
|
||||
func (t *MetaIssueType) GetAllFields() (map[string]string, error) {
|
||||
ret := make(map[string]string)
|
||||
for key := range t.Fields {
|
||||
|
||||
name, err := t.Fields.String(key + "/name")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret[name] = key
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// CheckCompleteAndAvailable checks if the given fields satisfies the mandatory field required to create a issue for the given type
|
||||
// And also if the given fields are available.
|
||||
func (t *MetaIssueType) CheckCompleteAndAvailable(config map[string]string) (bool, error) {
|
||||
mandatory, err := t.GetMandatoryFields()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
all, err := t.GetAllFields()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// check templateconfig against mandatory fields
|
||||
for key := range mandatory {
|
||||
if _, okay := config[key]; !okay {
|
||||
var requiredFields []string
|
||||
for name := range mandatory {
|
||||
requiredFields = append(requiredFields, name)
|
||||
}
|
||||
return false, fmt.Errorf("Required field not found in provided jira.fields. Required are: %#v", requiredFields)
|
||||
}
|
||||
}
|
||||
|
||||
// check templateConfig against all fields to verify they are available
|
||||
for key := range config {
|
||||
if _, okay := all[key]; !okay {
|
||||
var availableFields []string
|
||||
for name := range all {
|
||||
availableFields = append(availableFields, name)
|
||||
}
|
||||
return false, fmt.Errorf("Fields in jira.fields are not available in jira. Available are: %#v", availableFields)
|
||||
}
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
162
vendor/github.com/andygrunwald/go-jira/project.go
generated
vendored
Normal file
162
vendor/github.com/andygrunwald/go-jira/project.go
generated
vendored
Normal file
@@ -0,0 +1,162 @@
|
||||
package jira
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/google/go-querystring/query"
|
||||
)
|
||||
|
||||
// ProjectService handles projects for the JIRA instance / API.
|
||||
//
|
||||
// JIRA API docs: https://docs.atlassian.com/jira/REST/latest/#api/2/project
|
||||
type ProjectService struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// ProjectList represent a list of Projects
|
||||
type ProjectList []struct {
|
||||
Expand string `json:"expand" structs:"expand"`
|
||||
Self string `json:"self" structs:"self"`
|
||||
ID string `json:"id" structs:"id"`
|
||||
Key string `json:"key" structs:"key"`
|
||||
Name string `json:"name" structs:"name"`
|
||||
AvatarUrls AvatarUrls `json:"avatarUrls" structs:"avatarUrls"`
|
||||
ProjectTypeKey string `json:"projectTypeKey" structs:"projectTypeKey"`
|
||||
ProjectCategory ProjectCategory `json:"projectCategory,omitempty" structs:"projectsCategory,omitempty"`
|
||||
IssueTypes []IssueType `json:"issueTypes,omitempty" structs:"issueTypes,omitempty"`
|
||||
}
|
||||
|
||||
// ProjectCategory represents a single project category
|
||||
type ProjectCategory struct {
|
||||
Self string `json:"self" structs:"self,omitempty"`
|
||||
ID string `json:"id" structs:"id,omitempty"`
|
||||
Name string `json:"name" structs:"name,omitempty"`
|
||||
Description string `json:"description" structs:"description,omitempty"`
|
||||
}
|
||||
|
||||
// Project represents a JIRA Project.
|
||||
type Project struct {
|
||||
Expand string `json:"expand,omitempty" structs:"expand,omitempty"`
|
||||
Self string `json:"self,omitempty" structs:"self,omitempty"`
|
||||
ID string `json:"id,omitempty" structs:"id,omitempty"`
|
||||
Key string `json:"key,omitempty" structs:"key,omitempty"`
|
||||
Description string `json:"description,omitempty" structs:"description,omitempty"`
|
||||
Lead User `json:"lead,omitempty" structs:"lead,omitempty"`
|
||||
Components []ProjectComponent `json:"components,omitempty" structs:"components,omitempty"`
|
||||
IssueTypes []IssueType `json:"issueTypes,omitempty" structs:"issueTypes,omitempty"`
|
||||
URL string `json:"url,omitempty" structs:"url,omitempty"`
|
||||
Email string `json:"email,omitempty" structs:"email,omitempty"`
|
||||
AssigneeType string `json:"assigneeType,omitempty" structs:"assigneeType,omitempty"`
|
||||
Versions []Version `json:"versions,omitempty" structs:"versions,omitempty"`
|
||||
Name string `json:"name,omitempty" structs:"name,omitempty"`
|
||||
Roles struct {
|
||||
Developers string `json:"Developers,omitempty" structs:"Developers,omitempty"`
|
||||
} `json:"roles,omitempty" structs:"roles,omitempty"`
|
||||
AvatarUrls AvatarUrls `json:"avatarUrls,omitempty" structs:"avatarUrls,omitempty"`
|
||||
ProjectCategory ProjectCategory `json:"projectCategory,omitempty" structs:"projectCategory,omitempty"`
|
||||
}
|
||||
|
||||
// ProjectComponent represents a single component of a project
|
||||
type ProjectComponent struct {
|
||||
Self string `json:"self" structs:"self,omitempty"`
|
||||
ID string `json:"id" structs:"id,omitempty"`
|
||||
Name string `json:"name" structs:"name,omitempty"`
|
||||
Description string `json:"description" structs:"description,omitempty"`
|
||||
Lead User `json:"lead,omitempty" structs:"lead,omitempty"`
|
||||
AssigneeType string `json:"assigneeType" structs:"assigneeType,omitempty"`
|
||||
Assignee User `json:"assignee" structs:"assignee,omitempty"`
|
||||
RealAssigneeType string `json:"realAssigneeType" structs:"realAssigneeType,omitempty"`
|
||||
RealAssignee User `json:"realAssignee" structs:"realAssignee,omitempty"`
|
||||
IsAssigneeTypeValid bool `json:"isAssigneeTypeValid" structs:"isAssigneeTypeValid,omitempty"`
|
||||
Project string `json:"project" structs:"project,omitempty"`
|
||||
ProjectID int `json:"projectId" structs:"projectId,omitempty"`
|
||||
}
|
||||
|
||||
// PermissionScheme represents the permission scheme for the project
|
||||
type PermissionScheme struct {
|
||||
Expand string `json:"expand" structs:"expand,omitempty"`
|
||||
Self string `json:"self" structs:"self,omitempty"`
|
||||
ID int `json:"id" structs:"id,omitempty"`
|
||||
Name string `json:"name" structs:"name,omitempty"`
|
||||
Description string `json:"description" structs:"description,omitempty"`
|
||||
}
|
||||
|
||||
// GetList gets all projects form JIRA
|
||||
//
|
||||
// JIRA API docs: https://docs.atlassian.com/jira/REST/latest/#api/2/project-getAllProjects
|
||||
func (s *ProjectService) GetList() (*ProjectList, *Response, error) {
|
||||
return s.ListWithOptions(&GetQueryOptions{})
|
||||
}
|
||||
|
||||
// ListWithOptions gets all projects form JIRA with optional query params, like &GetQueryOptions{Expand: "issueTypes"} to get
|
||||
// a list of all projects and their supported issuetypes
|
||||
//
|
||||
// JIRA API docs: https://docs.atlassian.com/jira/REST/latest/#api/2/project-getAllProjects
|
||||
func (s *ProjectService) ListWithOptions(options *GetQueryOptions) (*ProjectList, *Response, error) {
|
||||
apiEndpoint := "rest/api/2/project"
|
||||
req, err := s.client.NewRequest("GET", apiEndpoint, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if options != nil {
|
||||
q, err := query.Values(options)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
req.URL.RawQuery = q.Encode()
|
||||
}
|
||||
|
||||
projectList := new(ProjectList)
|
||||
resp, err := s.client.Do(req, projectList)
|
||||
if err != nil {
|
||||
jerr := NewJiraError(resp, err)
|
||||
return nil, resp, jerr
|
||||
}
|
||||
|
||||
return projectList, resp, nil
|
||||
}
|
||||
|
||||
// Get returns a full representation of the project for the given issue key.
|
||||
// JIRA will attempt to identify the project by the projectIdOrKey path parameter.
|
||||
// This can be an project id, or an project key.
|
||||
//
|
||||
// JIRA API docs: https://docs.atlassian.com/jira/REST/latest/#api/2/project-getProject
|
||||
func (s *ProjectService) Get(projectID string) (*Project, *Response, error) {
|
||||
apiEndpoint := fmt.Sprintf("rest/api/2/project/%s", projectID)
|
||||
req, err := s.client.NewRequest("GET", apiEndpoint, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
project := new(Project)
|
||||
resp, err := s.client.Do(req, project)
|
||||
if err != nil {
|
||||
jerr := NewJiraError(resp, err)
|
||||
return nil, resp, jerr
|
||||
}
|
||||
|
||||
return project, resp, nil
|
||||
}
|
||||
|
||||
// GetPermissionScheme returns a full representation of the permission scheme for the project
|
||||
// JIRA will attempt to identify the project by the projectIdOrKey path parameter.
|
||||
// This can be an project id, or an project key.
|
||||
//
|
||||
// JIRA API docs: https://docs.atlassian.com/jira/REST/latest/#api/2/project-getProject
|
||||
func (s *ProjectService) GetPermissionScheme(projectID string) (*PermissionScheme, *Response, error) {
|
||||
apiEndpoint := fmt.Sprintf("/rest/api/2/project/%s/permissionscheme", projectID)
|
||||
req, err := s.client.NewRequest("GET", apiEndpoint, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
ps := new(PermissionScheme)
|
||||
resp, err := s.client.Do(req, ps)
|
||||
if err != nil {
|
||||
jerr := NewJiraError(resp, err)
|
||||
return nil, resp, jerr
|
||||
}
|
||||
|
||||
return ps, resp, nil
|
||||
}
|
||||
106
vendor/github.com/andygrunwald/go-jira/sprint.go
generated
vendored
Normal file
106
vendor/github.com/andygrunwald/go-jira/sprint.go
generated
vendored
Normal file
@@ -0,0 +1,106 @@
|
||||
package jira
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/google/go-querystring/query"
|
||||
)
|
||||
|
||||
// SprintService handles sprints in JIRA Agile API.
|
||||
// See https://docs.atlassian.com/jira-software/REST/cloud/
|
||||
type SprintService struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// IssuesWrapper represents a wrapper struct for moving issues to sprint
|
||||
type IssuesWrapper struct {
|
||||
Issues []string `json:"issues"`
|
||||
}
|
||||
|
||||
// IssuesInSprintResult represents a wrapper struct for search result
|
||||
type IssuesInSprintResult struct {
|
||||
Issues []Issue `json:"issues"`
|
||||
}
|
||||
|
||||
// MoveIssuesToSprint moves issues to a sprint, for a given sprint Id.
|
||||
// Issues can only be moved to open or active sprints.
|
||||
// The maximum number of issues that can be moved in one operation is 50.
|
||||
//
|
||||
// JIRA API docs: https://docs.atlassian.com/jira-software/REST/cloud/#agile/1.0/sprint-moveIssuesToSprint
|
||||
func (s *SprintService) MoveIssuesToSprint(sprintID int, issueIDs []string) (*Response, error) {
|
||||
apiEndpoint := fmt.Sprintf("rest/agile/1.0/sprint/%d/issue", sprintID)
|
||||
|
||||
payload := IssuesWrapper{Issues: issueIDs}
|
||||
|
||||
req, err := s.client.NewRequest("POST", apiEndpoint, payload)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := s.client.Do(req, nil)
|
||||
if err != nil {
|
||||
err = NewJiraError(resp, err)
|
||||
}
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// GetIssuesForSprint returns all issues in a sprint, for a given sprint Id.
|
||||
// This only includes issues that the user has permission to view.
|
||||
// By default, the returned issues are ordered by rank.
|
||||
//
|
||||
// JIRA API Docs: https://docs.atlassian.com/jira-software/REST/cloud/#agile/1.0/sprint-getIssuesForSprint
|
||||
func (s *SprintService) GetIssuesForSprint(sprintID int) ([]Issue, *Response, error) {
|
||||
apiEndpoint := fmt.Sprintf("rest/agile/1.0/sprint/%d/issue", sprintID)
|
||||
|
||||
req, err := s.client.NewRequest("GET", apiEndpoint, nil)
|
||||
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
result := new(IssuesInSprintResult)
|
||||
resp, err := s.client.Do(req, result)
|
||||
if err != nil {
|
||||
err = NewJiraError(resp, err)
|
||||
}
|
||||
|
||||
return result.Issues, resp, err
|
||||
}
|
||||
|
||||
// Get returns a full representation of the issue for the given issue key.
|
||||
// JIRA will attempt to identify the issue by the issueIdOrKey path parameter.
|
||||
// This can be an issue id, or an issue key.
|
||||
// If the issue cannot be found via an exact match, JIRA will also look for the issue in a case-insensitive way, or by looking to see if the issue was moved.
|
||||
//
|
||||
// The given options will be appended to the query string
|
||||
//
|
||||
// JIRA API docs: https://docs.atlassian.com/jira-software/REST/7.3.1/#agile/1.0/issue-getIssue
|
||||
//
|
||||
// TODO: create agile service for holding all agile apis' implementation
|
||||
func (s *SprintService) GetIssue(issueID string, options *GetQueryOptions) (*Issue, *Response, error) {
|
||||
apiEndpoint := fmt.Sprintf("rest/agile/1.0/issue/%s", issueID)
|
||||
|
||||
req, err := s.client.NewRequest("GET", apiEndpoint, nil)
|
||||
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if options != nil {
|
||||
q, err := query.Values(options)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
req.URL.RawQuery = q.Encode()
|
||||
}
|
||||
|
||||
issue := new(Issue)
|
||||
resp, err := s.client.Do(req, issue)
|
||||
|
||||
if err != nil {
|
||||
jerr := NewJiraError(resp, err)
|
||||
return nil, resp, jerr
|
||||
}
|
||||
|
||||
return issue, resp, nil
|
||||
}
|
||||
119
vendor/github.com/andygrunwald/go-jira/user.go
generated
vendored
Normal file
119
vendor/github.com/andygrunwald/go-jira/user.go
generated
vendored
Normal file
@@ -0,0 +1,119 @@
|
||||
package jira
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
// UserService handles users for the JIRA instance / API.
|
||||
//
|
||||
// JIRA API docs: https://docs.atlassian.com/jira/REST/cloud/#api/2/user
|
||||
type UserService struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// User represents a JIRA user.
|
||||
type User struct {
|
||||
Self string `json:"self,omitempty" structs:"self,omitempty"`
|
||||
Name string `json:"name,omitempty" structs:"name,omitempty"`
|
||||
Password string `json:"-"`
|
||||
Key string `json:"key,omitempty" structs:"key,omitempty"`
|
||||
EmailAddress string `json:"emailAddress,omitempty" structs:"emailAddress,omitempty"`
|
||||
AvatarUrls AvatarUrls `json:"avatarUrls,omitempty" structs:"avatarUrls,omitempty"`
|
||||
DisplayName string `json:"displayName,omitempty" structs:"displayName,omitempty"`
|
||||
Active bool `json:"active,omitempty" structs:"active,omitempty"`
|
||||
TimeZone string `json:"timeZone,omitempty" structs:"timeZone,omitempty"`
|
||||
ApplicationKeys []string `json:"applicationKeys,omitempty" structs:"applicationKeys,omitempty"`
|
||||
}
|
||||
|
||||
// UserGroup represents the group list
|
||||
type UserGroup struct {
|
||||
Self string `json:"self,omitempty" structs:"self,omitempty"`
|
||||
Name string `json:"name,omitempty" structs:"name,omitempty"`
|
||||
}
|
||||
|
||||
// Get gets user info from JIRA
|
||||
//
|
||||
// JIRA API docs: https://docs.atlassian.com/jira/REST/cloud/#api/2/user-getUser
|
||||
func (s *UserService) Get(username string) (*User, *Response, error) {
|
||||
apiEndpoint := fmt.Sprintf("/rest/api/2/user?username=%s", username)
|
||||
req, err := s.client.NewRequest("GET", apiEndpoint, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
user := new(User)
|
||||
resp, err := s.client.Do(req, user)
|
||||
if err != nil {
|
||||
return nil, resp, NewJiraError(resp, err)
|
||||
}
|
||||
return user, resp, nil
|
||||
}
|
||||
|
||||
// Create creates an user in JIRA.
|
||||
//
|
||||
// JIRA API docs: https://docs.atlassian.com/jira/REST/cloud/#api/2/user-createUser
|
||||
func (s *UserService) Create(user *User) (*User, *Response, error) {
|
||||
apiEndpoint := "/rest/api/2/user"
|
||||
req, err := s.client.NewRequest("POST", apiEndpoint, user)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
resp, err := s.client.Do(req, nil)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
|
||||
responseUser := new(User)
|
||||
defer resp.Body.Close()
|
||||
data, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
e := fmt.Errorf("Could not read the returned data")
|
||||
return nil, resp, NewJiraError(resp, e)
|
||||
}
|
||||
err = json.Unmarshal(data, responseUser)
|
||||
if err != nil {
|
||||
e := fmt.Errorf("Could not unmarshall the data into struct")
|
||||
return nil, resp, NewJiraError(resp, e)
|
||||
}
|
||||
return responseUser, resp, nil
|
||||
}
|
||||
|
||||
// GetGroups returns the groups which the user belongs to
|
||||
//
|
||||
// JIRA API docs: https://docs.atlassian.com/jira/REST/cloud/#api/2/user-getUserGroups
|
||||
func (s *UserService) GetGroups(username string) (*[]UserGroup, *Response, error) {
|
||||
apiEndpoint := fmt.Sprintf("/rest/api/2/user/groups?username=%s", username)
|
||||
req, err := s.client.NewRequest("GET", apiEndpoint, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
userGroups := new([]UserGroup)
|
||||
resp, err := s.client.Do(req, userGroups)
|
||||
if err != nil {
|
||||
return nil, resp, NewJiraError(resp, err)
|
||||
}
|
||||
return userGroups, resp, nil
|
||||
}
|
||||
|
||||
// Find searches for user info from JIRA:
|
||||
// It can find users by email, username or name
|
||||
//
|
||||
// JIRA API docs: https://docs.atlassian.com/jira/REST/cloud/#api/2/user-findUsers
|
||||
func (s *UserService) Find(property string) ([]User, *Response, error) {
|
||||
apiEndpoint := fmt.Sprintf("/rest/api/2/user/search?username=%s", property)
|
||||
req, err := s.client.NewRequest("GET", apiEndpoint, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
users := []User{}
|
||||
resp, err := s.client.Do(req, &users)
|
||||
if err != nil {
|
||||
return nil, resp, NewJiraError(resp, err)
|
||||
}
|
||||
return users, resp, nil
|
||||
}
|
||||
96
vendor/github.com/andygrunwald/go-jira/version.go
generated
vendored
Normal file
96
vendor/github.com/andygrunwald/go-jira/version.go
generated
vendored
Normal file
@@ -0,0 +1,96 @@
|
||||
package jira
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
// VersionService handles Versions for the JIRA instance / API.
|
||||
//
|
||||
// JIRA API docs: https://docs.atlassian.com/jira/REST/latest/#api/2/version
|
||||
type VersionService struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// Version represents a single release version of a project
|
||||
type Version struct {
|
||||
Self string `json:"self,omitempty" structs:"self,omitempty"`
|
||||
ID string `json:"id,omitempty" structs:"id,omitempty"`
|
||||
Name string `json:"name,omitempty" structs:"name,omitempty"`
|
||||
Description string `json:"description,omitempty" structs:"name,omitempty"`
|
||||
Archived bool `json:"archived,omitempty" structs:"archived,omitempty"`
|
||||
Released bool `json:"released,omitempty" structs:"released,omitempty"`
|
||||
ReleaseDate string `json:"releaseDate,omitempty" structs:"releaseDate,omitempty"`
|
||||
UserReleaseDate string `json:"userReleaseDate,omitempty" structs:"userReleaseDate,omitempty"`
|
||||
ProjectID int `json:"projectId,omitempty" structs:"projectId,omitempty"` // Unlike other IDs, this is returned as a number
|
||||
}
|
||||
|
||||
// Get gets version info from JIRA
|
||||
//
|
||||
// JIRA API docs: https://developer.atlassian.com/cloud/jira/platform/rest/#api-api-2-version-id-get
|
||||
func (s *VersionService) Get(versionID int) (*Version, *Response, error) {
|
||||
apiEndpoint := fmt.Sprintf("/rest/api/2/version/%v", versionID)
|
||||
req, err := s.client.NewRequest("GET", apiEndpoint, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
version := new(Version)
|
||||
resp, err := s.client.Do(req, version)
|
||||
if err != nil {
|
||||
return nil, resp, NewJiraError(resp, err)
|
||||
}
|
||||
return version, resp, nil
|
||||
}
|
||||
|
||||
// Create creates a version in JIRA.
|
||||
//
|
||||
// JIRA API docs: https://developer.atlassian.com/cloud/jira/platform/rest/#api-api-2-version-post
|
||||
func (s *VersionService) Create(version *Version) (*Version, *Response, error) {
|
||||
apiEndpoint := "/rest/api/2/version"
|
||||
req, err := s.client.NewRequest("POST", apiEndpoint, version)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
resp, err := s.client.Do(req, nil)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
|
||||
responseVersion := new(Version)
|
||||
defer resp.Body.Close()
|
||||
data, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
e := fmt.Errorf("Could not read the returned data")
|
||||
return nil, resp, NewJiraError(resp, e)
|
||||
}
|
||||
err = json.Unmarshal(data, responseVersion)
|
||||
if err != nil {
|
||||
e := fmt.Errorf("Could not unmarshall the data into struct")
|
||||
return nil, resp, NewJiraError(resp, e)
|
||||
}
|
||||
return responseVersion, resp, nil
|
||||
}
|
||||
|
||||
// Update updates a version from a JSON representation.
|
||||
//
|
||||
// JIRA API docs: https://developer.atlassian.com/cloud/jira/platform/rest/#api-api-2-version-id-put
|
||||
func (s *VersionService) Update(version *Version) (*Version, *Response, error) {
|
||||
apiEndpoint := fmt.Sprintf("rest/api/2/version/%v", version.ID)
|
||||
req, err := s.client.NewRequest("PUT", apiEndpoint, version)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
resp, err := s.client.Do(req, nil)
|
||||
if err != nil {
|
||||
jerr := NewJiraError(resp, err)
|
||||
return nil, resp, jerr
|
||||
}
|
||||
|
||||
// This is just to follow the rest of the API's convention of returning a version.
|
||||
// Returning the same pointer here is pointless, so we return a copy instead.
|
||||
ret := *version
|
||||
return &ret, resp, nil
|
||||
}
|
||||
23
vendor/github.com/fatih/structs/.gitignore
generated
vendored
Normal file
23
vendor/github.com/fatih/structs/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
# 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
|
||||
11
vendor/github.com/fatih/structs/.travis.yml
generated
vendored
Normal file
11
vendor/github.com/fatih/structs/.travis.yml
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
language: go
|
||||
go:
|
||||
- 1.7.x
|
||||
- tip
|
||||
sudo: false
|
||||
before_install:
|
||||
- go get github.com/axw/gocov/gocov
|
||||
- go get github.com/mattn/goveralls
|
||||
- if ! go get github.com/golang/tools/cmd/cover; then go get golang.org/x/tools/cmd/cover; fi
|
||||
script:
|
||||
- $HOME/gopath/bin/goveralls -service=travis-ci
|
||||
21
vendor/github.com/fatih/structs/LICENSE
generated
vendored
Normal file
21
vendor/github.com/fatih/structs/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Fatih Arslan
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
163
vendor/github.com/fatih/structs/README.md
generated
vendored
Normal file
163
vendor/github.com/fatih/structs/README.md
generated
vendored
Normal file
@@ -0,0 +1,163 @@
|
||||
# Structs [](http://godoc.org/github.com/fatih/structs) [](https://travis-ci.org/fatih/structs) [](https://coveralls.io/r/fatih/structs)
|
||||
|
||||
Structs contains various utilities to work with Go (Golang) structs. It was
|
||||
initially used by me to convert a struct into a `map[string]interface{}`. With
|
||||
time I've added other utilities for structs. It's basically a high level
|
||||
package based on primitives from the reflect package. Feel free to add new
|
||||
functions or improve the existing code.
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
go get github.com/fatih/structs
|
||||
```
|
||||
|
||||
## Usage and Examples
|
||||
|
||||
Just like the standard lib `strings`, `bytes` and co packages, `structs` has
|
||||
many global functions to manipulate or organize your struct data. Lets define
|
||||
and declare a struct:
|
||||
|
||||
```go
|
||||
type Server struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
ID int
|
||||
Enabled bool
|
||||
users []string // not exported
|
||||
http.Server // embedded
|
||||
}
|
||||
|
||||
server := &Server{
|
||||
Name: "gopher",
|
||||
ID: 123456,
|
||||
Enabled: true,
|
||||
}
|
||||
```
|
||||
|
||||
```go
|
||||
// Convert a struct to a map[string]interface{}
|
||||
// => {"Name":"gopher", "ID":123456, "Enabled":true}
|
||||
m := structs.Map(server)
|
||||
|
||||
// Convert the values of a struct to a []interface{}
|
||||
// => ["gopher", 123456, true]
|
||||
v := structs.Values(server)
|
||||
|
||||
// Convert the names of a struct to a []string
|
||||
// (see "Names methods" for more info about fields)
|
||||
n := structs.Names(server)
|
||||
|
||||
// Convert the values of a struct to a []*Field
|
||||
// (see "Field methods" for more info about fields)
|
||||
f := structs.Fields(server)
|
||||
|
||||
// Return the struct name => "Server"
|
||||
n := structs.Name(server)
|
||||
|
||||
// Check if any field of a struct is initialized or not.
|
||||
h := structs.HasZero(server)
|
||||
|
||||
// Check if all fields of a struct is initialized or not.
|
||||
z := structs.IsZero(server)
|
||||
|
||||
// Check if server is a struct or a pointer to struct
|
||||
i := structs.IsStruct(server)
|
||||
```
|
||||
|
||||
### Struct methods
|
||||
|
||||
The structs functions can be also used as independent methods by creating a new
|
||||
`*structs.Struct`. This is handy if you want to have more control over the
|
||||
structs (such as retrieving a single Field).
|
||||
|
||||
```go
|
||||
// Create a new struct type:
|
||||
s := structs.New(server)
|
||||
|
||||
m := s.Map() // Get a map[string]interface{}
|
||||
v := s.Values() // Get a []interface{}
|
||||
f := s.Fields() // Get a []*Field
|
||||
n := s.Names() // Get a []string
|
||||
f := s.Field(name) // Get a *Field based on the given field name
|
||||
f, ok := s.FieldOk(name) // Get a *Field based on the given field name
|
||||
n := s.Name() // Get the struct name
|
||||
h := s.HasZero() // Check if any field is initialized
|
||||
z := s.IsZero() // Check if all fields are initialized
|
||||
```
|
||||
|
||||
### Field methods
|
||||
|
||||
We can easily examine a single Field for more detail. Below you can see how we
|
||||
get and interact with various field methods:
|
||||
|
||||
|
||||
```go
|
||||
s := structs.New(server)
|
||||
|
||||
// Get the Field struct for the "Name" field
|
||||
name := s.Field("Name")
|
||||
|
||||
// Get the underlying value, value => "gopher"
|
||||
value := name.Value().(string)
|
||||
|
||||
// Set the field's value
|
||||
name.Set("another gopher")
|
||||
|
||||
// Get the field's kind, kind => "string"
|
||||
name.Kind()
|
||||
|
||||
// Check if the field is exported or not
|
||||
if name.IsExported() {
|
||||
fmt.Println("Name field is exported")
|
||||
}
|
||||
|
||||
// Check if the value is a zero value, such as "" for string, 0 for int
|
||||
if !name.IsZero() {
|
||||
fmt.Println("Name is initialized")
|
||||
}
|
||||
|
||||
// Check if the field is an anonymous (embedded) field
|
||||
if !name.IsEmbedded() {
|
||||
fmt.Println("Name is not an embedded field")
|
||||
}
|
||||
|
||||
// Get the Field's tag value for tag name "json", tag value => "name,omitempty"
|
||||
tagValue := name.Tag("json")
|
||||
```
|
||||
|
||||
Nested structs are supported too:
|
||||
|
||||
```go
|
||||
addrField := s.Field("Server").Field("Addr")
|
||||
|
||||
// Get the value for addr
|
||||
a := addrField.Value().(string)
|
||||
|
||||
// Or get all fields
|
||||
httpServer := s.Field("Server").Fields()
|
||||
```
|
||||
|
||||
We can also get a slice of Fields from the Struct type to iterate over all
|
||||
fields. This is handy if you wish to examine all fields:
|
||||
|
||||
```go
|
||||
s := structs.New(server)
|
||||
|
||||
for _, f := range s.Fields() {
|
||||
fmt.Printf("field name: %+v\n", f.Name())
|
||||
|
||||
if f.IsExported() {
|
||||
fmt.Printf("value : %+v\n", f.Value())
|
||||
fmt.Printf("is zero : %+v\n", f.IsZero())
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Credits
|
||||
|
||||
* [Fatih Arslan](https://github.com/fatih)
|
||||
* [Cihangir Savas](https://github.com/cihangir)
|
||||
|
||||
## License
|
||||
|
||||
The MIT License (MIT) - see LICENSE.md for more details
|
||||
141
vendor/github.com/fatih/structs/field.go
generated
vendored
Normal file
141
vendor/github.com/fatih/structs/field.go
generated
vendored
Normal file
@@ -0,0 +1,141 @@
|
||||
package structs
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
var (
|
||||
errNotExported = errors.New("field is not exported")
|
||||
errNotSettable = errors.New("field is not settable")
|
||||
)
|
||||
|
||||
// Field represents a single struct field that encapsulates high level
|
||||
// functions around the field.
|
||||
type Field struct {
|
||||
value reflect.Value
|
||||
field reflect.StructField
|
||||
defaultTag string
|
||||
}
|
||||
|
||||
// Tag returns the value associated with key in the tag string. If there is no
|
||||
// such key in the tag, Tag returns the empty string.
|
||||
func (f *Field) Tag(key string) string {
|
||||
return f.field.Tag.Get(key)
|
||||
}
|
||||
|
||||
// Value returns the underlying value of the field. It panics if the field
|
||||
// is not exported.
|
||||
func (f *Field) Value() interface{} {
|
||||
return f.value.Interface()
|
||||
}
|
||||
|
||||
// IsEmbedded returns true if the given field is an anonymous field (embedded)
|
||||
func (f *Field) IsEmbedded() bool {
|
||||
return f.field.Anonymous
|
||||
}
|
||||
|
||||
// IsExported returns true if the given field is exported.
|
||||
func (f *Field) IsExported() bool {
|
||||
return f.field.PkgPath == ""
|
||||
}
|
||||
|
||||
// IsZero returns true if the given field is not initialized (has a zero value).
|
||||
// It panics if the field is not exported.
|
||||
func (f *Field) IsZero() bool {
|
||||
zero := reflect.Zero(f.value.Type()).Interface()
|
||||
current := f.Value()
|
||||
|
||||
return reflect.DeepEqual(current, zero)
|
||||
}
|
||||
|
||||
// Name returns the name of the given field
|
||||
func (f *Field) Name() string {
|
||||
return f.field.Name
|
||||
}
|
||||
|
||||
// Kind returns the fields kind, such as "string", "map", "bool", etc ..
|
||||
func (f *Field) Kind() reflect.Kind {
|
||||
return f.value.Kind()
|
||||
}
|
||||
|
||||
// Set sets the field to given value v. It returns an error if the field is not
|
||||
// settable (not addressable or not exported) or if the given value's type
|
||||
// doesn't match the fields type.
|
||||
func (f *Field) Set(val interface{}) error {
|
||||
// we can't set unexported fields, so be sure this field is exported
|
||||
if !f.IsExported() {
|
||||
return errNotExported
|
||||
}
|
||||
|
||||
// do we get here? not sure...
|
||||
if !f.value.CanSet() {
|
||||
return errNotSettable
|
||||
}
|
||||
|
||||
given := reflect.ValueOf(val)
|
||||
|
||||
if f.value.Kind() != given.Kind() {
|
||||
return fmt.Errorf("wrong kind. got: %s want: %s", given.Kind(), f.value.Kind())
|
||||
}
|
||||
|
||||
f.value.Set(given)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Zero sets the field to its zero value. It returns an error if the field is not
|
||||
// settable (not addressable or not exported).
|
||||
func (f *Field) Zero() error {
|
||||
zero := reflect.Zero(f.value.Type()).Interface()
|
||||
return f.Set(zero)
|
||||
}
|
||||
|
||||
// Fields returns a slice of Fields. This is particular handy to get the fields
|
||||
// of a nested struct . A struct tag with the content of "-" ignores the
|
||||
// checking of that particular field. Example:
|
||||
//
|
||||
// // Field is ignored by this package.
|
||||
// Field *http.Request `structs:"-"`
|
||||
//
|
||||
// It panics if field is not exported or if field's kind is not struct
|
||||
func (f *Field) Fields() []*Field {
|
||||
return getFields(f.value, f.defaultTag)
|
||||
}
|
||||
|
||||
// Field returns the field from a nested struct. It panics if the nested struct
|
||||
// is not exported or if the field was not found.
|
||||
func (f *Field) Field(name string) *Field {
|
||||
field, ok := f.FieldOk(name)
|
||||
if !ok {
|
||||
panic("field not found")
|
||||
}
|
||||
|
||||
return field
|
||||
}
|
||||
|
||||
// FieldOk returns the field from a nested struct. The boolean returns whether
|
||||
// the field was found (true) or not (false).
|
||||
func (f *Field) FieldOk(name string) (*Field, bool) {
|
||||
value := &f.value
|
||||
// value must be settable so we need to make sure it holds the address of the
|
||||
// variable and not a copy, so we can pass the pointer to strctVal instead of a
|
||||
// copy (which is not assigned to any variable, hence not settable).
|
||||
// see "https://blog.golang.org/laws-of-reflection#TOC_8."
|
||||
if f.value.Kind() != reflect.Ptr {
|
||||
a := f.value.Addr()
|
||||
value = &a
|
||||
}
|
||||
v := strctVal(value.Interface())
|
||||
t := v.Type()
|
||||
|
||||
field, ok := t.FieldByName(name)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
return &Field{
|
||||
field: field,
|
||||
value: v.FieldByName(name),
|
||||
}, true
|
||||
}
|
||||
586
vendor/github.com/fatih/structs/structs.go
generated
vendored
Normal file
586
vendor/github.com/fatih/structs/structs.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
32
vendor/github.com/fatih/structs/tags.go
generated
vendored
Normal file
32
vendor/github.com/fatih/structs/tags.go
generated
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
package structs
|
||||
|
||||
import "strings"
|
||||
|
||||
// tagOptions contains a slice of tag options
|
||||
type tagOptions []string
|
||||
|
||||
// Has returns true if the given optiton is available in tagOptions
|
||||
func (t tagOptions) Has(opt string) bool {
|
||||
for _, tagOpt := range t {
|
||||
if tagOpt == opt {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// parseTag splits a struct field's tag into its name and a list of options
|
||||
// which comes after a name. A tag is in the form of: "name,option1,option2".
|
||||
// The name can be neglectected.
|
||||
func parseTag(tag string) (string, tagOptions) {
|
||||
// tag is one of followings:
|
||||
// ""
|
||||
// "name"
|
||||
// "name,opt"
|
||||
// "name,opt,opt2"
|
||||
// ",opt"
|
||||
|
||||
res := strings.Split(tag, ",")
|
||||
return res[0], res[1:]
|
||||
}
|
||||
202
vendor/github.com/trivago/tgo/LICENSE
generated
vendored
Normal file
202
vendor/github.com/trivago/tgo/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,202 @@
|
||||
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.
|
||||
|
||||
113
vendor/github.com/trivago/tgo/tcontainer/arrays.go
generated
vendored
Normal file
113
vendor/github.com/trivago/tgo/tcontainer/arrays.go
generated
vendored
Normal file
@@ -0,0 +1,113 @@
|
||||
// Copyright 2015-2016 trivago GmbH
|
||||
//
|
||||
// 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 tcontainer
|
||||
|
||||
import "sort"
|
||||
|
||||
// Int64Slice is a typedef to allow sortable int64 slices
|
||||
type Int64Slice []int64
|
||||
|
||||
func (s Int64Slice) Len() int {
|
||||
return len(s)
|
||||
}
|
||||
|
||||
func (s Int64Slice) Less(i, j int) bool {
|
||||
return s[i] < s[j]
|
||||
}
|
||||
|
||||
func (s Int64Slice) Swap(i, j int) {
|
||||
s[i], s[j] = s[j], s[i]
|
||||
}
|
||||
|
||||
// Sort is a shortcut for sort.Sort(s)
|
||||
func (s Int64Slice) Sort() {
|
||||
sort.Sort(s)
|
||||
}
|
||||
|
||||
// IsSorted is a shortcut for sort.IsSorted(s)
|
||||
func (s Int64Slice) IsSorted() bool {
|
||||
return sort.IsSorted(s)
|
||||
}
|
||||
|
||||
// Set sets all values in this slice to the given value
|
||||
func (s Int64Slice) Set(v int64) {
|
||||
for i := range s {
|
||||
s[i] = v
|
||||
}
|
||||
}
|
||||
|
||||
// Uint64Slice is a typedef to allow sortable uint64 slices
|
||||
type Uint64Slice []uint64
|
||||
|
||||
func (s Uint64Slice) Len() int {
|
||||
return len(s)
|
||||
}
|
||||
|
||||
func (s Uint64Slice) Less(i, j int) bool {
|
||||
return s[i] < s[j]
|
||||
}
|
||||
|
||||
func (s Uint64Slice) Swap(i, j int) {
|
||||
s[i], s[j] = s[j], s[i]
|
||||
}
|
||||
|
||||
// Sort is a shortcut for sort.Sort(s)
|
||||
func (s Uint64Slice) Sort() {
|
||||
sort.Sort(s)
|
||||
}
|
||||
|
||||
// IsSorted is a shortcut for sort.IsSorted(s)
|
||||
func (s Uint64Slice) IsSorted() bool {
|
||||
return sort.IsSorted(s)
|
||||
}
|
||||
|
||||
// Set sets all values in this slice to the given value
|
||||
func (s Uint64Slice) Set(v uint64) {
|
||||
for i := range s {
|
||||
s[i] = v
|
||||
}
|
||||
}
|
||||
|
||||
// Float32Slice is a typedef to allow sortable float32 slices
|
||||
type Float32Slice []float32
|
||||
|
||||
func (s Float32Slice) Len() int {
|
||||
return len(s)
|
||||
}
|
||||
|
||||
func (s Float32Slice) Less(i, j int) bool {
|
||||
return s[i] < s[j]
|
||||
}
|
||||
|
||||
func (s Float32Slice) Swap(i, j int) {
|
||||
s[i], s[j] = s[j], s[i]
|
||||
}
|
||||
|
||||
// Sort is a shortcut for sort.Sort(s)
|
||||
func (s Float32Slice) Sort() {
|
||||
sort.Sort(s)
|
||||
}
|
||||
|
||||
// IsSorted is a shortcut for sort.IsSorted(s)
|
||||
func (s Float32Slice) IsSorted() bool {
|
||||
return sort.IsSorted(s)
|
||||
}
|
||||
|
||||
// Set sets all values in this slice to the given value
|
||||
func (s Float32Slice) Set(v float32) {
|
||||
for i := range s {
|
||||
s[i] = v
|
||||
}
|
||||
}
|
||||
157
vendor/github.com/trivago/tgo/tcontainer/bytepool.go
generated
vendored
Normal file
157
vendor/github.com/trivago/tgo/tcontainer/bytepool.go
generated
vendored
Normal file
@@ -0,0 +1,157 @@
|
||||
// Copyright 2015-2016 trivago GmbH
|
||||
//
|
||||
// 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 tcontainer
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"runtime"
|
||||
"sync/atomic"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
tiny = 64
|
||||
small = 512
|
||||
medium = 1024
|
||||
large = 1024 * 10
|
||||
huge = 1024 * 100
|
||||
|
||||
tinyCount = 16384 // 1 MB
|
||||
smallCount = 2048 // 1 MB
|
||||
mediumCount = 1024 // 1 MB
|
||||
largeCount = 102 // ~1 MB
|
||||
hugeCount = 10 // ~1 MB
|
||||
)
|
||||
|
||||
type byteSlab struct {
|
||||
buffer []byte
|
||||
bufferSize uintptr
|
||||
stride uintptr
|
||||
basePtr *uintptr
|
||||
nextPtr *uintptr
|
||||
}
|
||||
|
||||
// BytePool is a fragmentation friendly way to allocated byte slices.
|
||||
type BytePool struct {
|
||||
tinySlab byteSlab
|
||||
smallSlab byteSlab
|
||||
mediumSlab byteSlab
|
||||
largeSlab byteSlab
|
||||
hugeSlab byteSlab
|
||||
}
|
||||
|
||||
func newByteSlab(size, count int) byteSlab {
|
||||
bufferSize := count * size
|
||||
buffer := make([]byte, bufferSize)
|
||||
basePtr := (*reflect.SliceHeader)(unsafe.Pointer(&buffer)).Data
|
||||
nextPtr := basePtr + uintptr(bufferSize)
|
||||
|
||||
return byteSlab{
|
||||
buffer: buffer,
|
||||
bufferSize: uintptr(bufferSize),
|
||||
stride: uintptr(size),
|
||||
basePtr: &basePtr,
|
||||
nextPtr: &nextPtr,
|
||||
}
|
||||
}
|
||||
|
||||
func (slab *byteSlab) getSlice(size int) (chunk []byte) {
|
||||
chunkHeader := (*reflect.SliceHeader)(unsafe.Pointer(&chunk))
|
||||
chunkHeader.Len = size
|
||||
chunkHeader.Cap = int(slab.stride)
|
||||
|
||||
for {
|
||||
// WARNING: The following two lines are order sensitive
|
||||
basePtr := atomic.LoadUintptr(slab.basePtr)
|
||||
nextPtr := atomic.AddUintptr(slab.nextPtr, -slab.stride)
|
||||
lastPtr := basePtr + slab.bufferSize
|
||||
|
||||
switch {
|
||||
case nextPtr < basePtr || nextPtr >= lastPtr:
|
||||
// out of range either means alloc while realloc or race between
|
||||
// base and next during realloc. In the latter case we lose a chunk.
|
||||
runtime.Gosched()
|
||||
|
||||
case nextPtr == basePtr:
|
||||
// Last item: realloc
|
||||
slab.buffer = make([]byte, slab.bufferSize)
|
||||
dataPtr := (*reflect.SliceHeader)(unsafe.Pointer(&slab.buffer)).Data
|
||||
|
||||
// WARNING: The following two lines are order sensitive
|
||||
atomic.StoreUintptr(slab.nextPtr, dataPtr+slab.bufferSize)
|
||||
atomic.StoreUintptr(slab.basePtr, dataPtr)
|
||||
fallthrough
|
||||
|
||||
default:
|
||||
chunkHeader.Data = nextPtr
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NewBytePool creates a new BytePool with each slab using 1 MB of storage.
|
||||
// The pool contains 5 slabs of different sizes: 64B, 512B, 1KB, 10KB and 100KB.
|
||||
// Allocations above 100KB will be allocated directly.
|
||||
func NewBytePool() BytePool {
|
||||
return BytePool{
|
||||
tinySlab: newByteSlab(tiny, tinyCount),
|
||||
smallSlab: newByteSlab(small, smallCount),
|
||||
mediumSlab: newByteSlab(medium, mediumCount),
|
||||
largeSlab: newByteSlab(large, largeCount),
|
||||
hugeSlab: newByteSlab(huge, hugeCount),
|
||||
}
|
||||
}
|
||||
|
||||
// NewBytePoolWithSize creates a new BytePool with each slab size using n MB of
|
||||
// storage. See NewBytePool() for slab size details.
|
||||
func NewBytePoolWithSize(n int) BytePool {
|
||||
if n <= 0 {
|
||||
n = 1
|
||||
}
|
||||
return BytePool{
|
||||
tinySlab: newByteSlab(tiny, tinyCount*n),
|
||||
smallSlab: newByteSlab(small, smallCount*n),
|
||||
mediumSlab: newByteSlab(medium, mediumCount*n),
|
||||
largeSlab: newByteSlab(large, largeCount*n),
|
||||
hugeSlab: newByteSlab(huge, hugeCount*n),
|
||||
}
|
||||
}
|
||||
|
||||
// Get returns a slice allocated to a normalized size.
|
||||
// Sizes are organized in evenly sized buckets so that fragmentation is kept low.
|
||||
func (b *BytePool) Get(size int) []byte {
|
||||
switch {
|
||||
case size == 0:
|
||||
return []byte{}
|
||||
|
||||
case size <= tiny:
|
||||
return b.tinySlab.getSlice(size)
|
||||
|
||||
case size <= small:
|
||||
return b.smallSlab.getSlice(size)
|
||||
|
||||
case size <= medium:
|
||||
return b.mediumSlab.getSlice(size)
|
||||
|
||||
case size <= large:
|
||||
return b.largeSlab.getSlice(size)
|
||||
|
||||
case size <= huge:
|
||||
return b.hugeSlab.getSlice(size)
|
||||
|
||||
default:
|
||||
return make([]byte, size)
|
||||
}
|
||||
}
|
||||
464
vendor/github.com/trivago/tgo/tcontainer/marshalmap.go
generated
vendored
Normal file
464
vendor/github.com/trivago/tgo/tcontainer/marshalmap.go
generated
vendored
Normal file
@@ -0,0 +1,464 @@
|
||||
// Copyright 2015-2016 trivago GmbH
|
||||
//
|
||||
// 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 tcontainer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/trivago/tgo/treflect"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// MarshalMap is a wrapper type to attach converter methods to maps normally
|
||||
// returned by marshalling methods, i.e. key/value parsers.
|
||||
// All methods that do a conversion will return an error if the value stored
|
||||
// behind key is not of the expected type or if the key is not existing in the
|
||||
// map.
|
||||
type MarshalMap map[string]interface{}
|
||||
|
||||
const (
|
||||
// MarshalMapSeparator defines the rune used for path separation
|
||||
MarshalMapSeparator = '/'
|
||||
// MarshalMapArrayBegin defines the rune starting array index notation
|
||||
MarshalMapArrayBegin = '['
|
||||
// MarshalMapArrayEnd defines the rune ending array index notation
|
||||
MarshalMapArrayEnd = ']'
|
||||
)
|
||||
|
||||
// NewMarshalMap creates a new marshal map (string -> interface{})
|
||||
func NewMarshalMap() MarshalMap {
|
||||
return make(map[string]interface{})
|
||||
}
|
||||
|
||||
// TryConvertToMarshalMap converts collections to MarshalMap if possible.
|
||||
// This is a deep conversion, i.e. each element in the collection will be
|
||||
// traversed. You can pass a formatKey function that will be applied to all
|
||||
// string keys that are detected.
|
||||
func TryConvertToMarshalMap(value interface{}, formatKey func(string) string) interface{} {
|
||||
valueMeta := reflect.ValueOf(value)
|
||||
switch valueMeta.Kind() {
|
||||
default:
|
||||
return value
|
||||
|
||||
case reflect.Array, reflect.Slice:
|
||||
arrayLen := valueMeta.Len()
|
||||
converted := make([]interface{}, arrayLen)
|
||||
for i := 0; i < arrayLen; i++ {
|
||||
converted[i] = TryConvertToMarshalMap(valueMeta.Index(i).Interface(), formatKey)
|
||||
}
|
||||
return converted
|
||||
|
||||
case reflect.Map:
|
||||
converted := NewMarshalMap()
|
||||
keys := valueMeta.MapKeys()
|
||||
|
||||
for _, keyMeta := range keys {
|
||||
strKey, isString := keyMeta.Interface().(string)
|
||||
if !isString {
|
||||
continue
|
||||
}
|
||||
if formatKey != nil {
|
||||
strKey = formatKey(strKey)
|
||||
}
|
||||
val := valueMeta.MapIndex(keyMeta).Interface()
|
||||
converted[strKey] = TryConvertToMarshalMap(val, formatKey)
|
||||
}
|
||||
return converted // ### return, converted MarshalMap ###
|
||||
}
|
||||
}
|
||||
|
||||
// ConvertToMarshalMap tries to convert a compatible map type to a marshal map.
|
||||
// Compatible types are map[interface{}]interface{}, map[string]interface{} and of
|
||||
// course MarshalMap. The same rules as for ConvertValueToMarshalMap apply.
|
||||
func ConvertToMarshalMap(value interface{}, formatKey func(string) string) (MarshalMap, error) {
|
||||
converted := TryConvertToMarshalMap(value, formatKey)
|
||||
if result, isMap := converted.(MarshalMap); isMap {
|
||||
return result, nil
|
||||
}
|
||||
return nil, fmt.Errorf("Root value cannot be converted to MarshalMap")
|
||||
}
|
||||
|
||||
// Bool returns a value at key that is expected to be a boolean
|
||||
func (mmap MarshalMap) Bool(key string) (bool, error) {
|
||||
val, exists := mmap.Value(key)
|
||||
if !exists {
|
||||
return false, fmt.Errorf(`"%s" is not set`, key)
|
||||
}
|
||||
|
||||
boolValue, isBool := val.(bool)
|
||||
if !isBool {
|
||||
return false, fmt.Errorf(`"%s" is expected to be a boolean`, key)
|
||||
}
|
||||
return boolValue, nil
|
||||
}
|
||||
|
||||
// Uint returns a value at key that is expected to be an uint64 or compatible
|
||||
// integer value.
|
||||
func (mmap MarshalMap) Uint(key string) (uint64, error) {
|
||||
val, exists := mmap.Value(key)
|
||||
if !exists {
|
||||
return 0, fmt.Errorf(`"%s" is not set`, key)
|
||||
}
|
||||
|
||||
if intVal, isNumber := treflect.Uint64(val); isNumber {
|
||||
return intVal, nil
|
||||
}
|
||||
|
||||
return 0, fmt.Errorf(`"%s" is expected to be an unsigned number type`, key)
|
||||
}
|
||||
|
||||
// Int returns a value at key that is expected to be an int64 or compatible
|
||||
// integer value.
|
||||
func (mmap MarshalMap) Int(key string) (int64, error) {
|
||||
val, exists := mmap.Value(key)
|
||||
if !exists {
|
||||
return 0, fmt.Errorf(`"%s" is not set`, key)
|
||||
}
|
||||
|
||||
if intVal, isNumber := treflect.Int64(val); isNumber {
|
||||
return intVal, nil
|
||||
}
|
||||
|
||||
return 0, fmt.Errorf(`"%s" is expected to be a signed number type`, key)
|
||||
}
|
||||
|
||||
// Float returns a value at key that is expected to be a float64 or compatible
|
||||
// float value.
|
||||
func (mmap MarshalMap) Float(key string) (float64, error) {
|
||||
val, exists := mmap.Value(key)
|
||||
if !exists {
|
||||
return 0, fmt.Errorf(`"%s" is not set`, key)
|
||||
}
|
||||
|
||||
if floatVal, isNumber := treflect.Float64(val); isNumber {
|
||||
return floatVal, nil
|
||||
}
|
||||
|
||||
return 0, fmt.Errorf(`"%s" is expected to be a signed number type`, key)
|
||||
}
|
||||
|
||||
// Duration returns a value at key that is expected to be a string
|
||||
func (mmap MarshalMap) Duration(key string) (time.Duration, error) {
|
||||
val, exists := mmap.Value(key)
|
||||
if !exists {
|
||||
return time.Duration(0), fmt.Errorf(`"%s" is not set`, key)
|
||||
}
|
||||
|
||||
switch val.(type) {
|
||||
case time.Duration:
|
||||
return val.(time.Duration), nil
|
||||
case string:
|
||||
return time.ParseDuration(val.(string))
|
||||
}
|
||||
|
||||
return time.Duration(0), fmt.Errorf(`"%s" is expected to be a duration or string`, key)
|
||||
}
|
||||
|
||||
// String returns a value at key that is expected to be a string
|
||||
func (mmap MarshalMap) String(key string) (string, error) {
|
||||
val, exists := mmap.Value(key)
|
||||
if !exists {
|
||||
return "", fmt.Errorf(`"%s" is not set`, key)
|
||||
}
|
||||
|
||||
strValue, isString := val.(string)
|
||||
if !isString {
|
||||
return "", fmt.Errorf(`"%s" is expected to be a string`, key)
|
||||
}
|
||||
return strValue, nil
|
||||
}
|
||||
|
||||
// Array returns a value at key that is expected to be a []interface{}
|
||||
func (mmap MarshalMap) Array(key string) ([]interface{}, error) {
|
||||
val, exists := mmap.Value(key)
|
||||
if !exists {
|
||||
return nil, fmt.Errorf(`"%s" is not set`, key)
|
||||
}
|
||||
|
||||
arrayValue, isArray := val.([]interface{})
|
||||
if !isArray {
|
||||
return nil, fmt.Errorf(`"%s" is expected to be an array`, key)
|
||||
}
|
||||
return arrayValue, nil
|
||||
}
|
||||
|
||||
// Map returns a value at key that is expected to be a
|
||||
// map[interface{}]interface{}.
|
||||
func (mmap MarshalMap) Map(key string) (map[interface{}]interface{}, error) {
|
||||
val, exists := mmap.Value(key)
|
||||
if !exists {
|
||||
return nil, fmt.Errorf(`"%s" is not set`, key)
|
||||
}
|
||||
|
||||
mapValue, isMap := val.(map[interface{}]interface{})
|
||||
if !isMap {
|
||||
return nil, fmt.Errorf(`"%s" is expected to be a map`, key)
|
||||
}
|
||||
return mapValue, nil
|
||||
}
|
||||
|
||||
func castToStringArray(key string, value interface{}) ([]string, error) {
|
||||
switch value.(type) {
|
||||
case string:
|
||||
return []string{value.(string)}, nil
|
||||
|
||||
case []interface{}:
|
||||
arrayVal := value.([]interface{})
|
||||
stringArray := make([]string, 0, len(arrayVal))
|
||||
|
||||
for _, val := range arrayVal {
|
||||
strValue, isString := val.(string)
|
||||
if !isString {
|
||||
return nil, fmt.Errorf(`"%s" does not contain string keys`, key)
|
||||
}
|
||||
stringArray = append(stringArray, strValue)
|
||||
}
|
||||
return stringArray, nil
|
||||
|
||||
case []string:
|
||||
return value.([]string), nil
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf(`"%s" is not a valid string array type`, key)
|
||||
}
|
||||
}
|
||||
|
||||
// StringArray returns a value at key that is expected to be a []string
|
||||
// This function supports conversion (by copy) from
|
||||
// * []interface{}
|
||||
func (mmap MarshalMap) StringArray(key string) ([]string, error) {
|
||||
val, exists := mmap.Value(key)
|
||||
if !exists {
|
||||
return nil, fmt.Errorf(`"%s" is not set`, key)
|
||||
}
|
||||
|
||||
return castToStringArray(key, val)
|
||||
}
|
||||
|
||||
func castToInt64Array(key string, value interface{}) ([]int64, error) {
|
||||
switch value.(type) {
|
||||
case int:
|
||||
return []int64{value.(int64)}, nil
|
||||
|
||||
case []interface{}:
|
||||
arrayVal := value.([]interface{})
|
||||
intArray := make([]int64, 0, len(arrayVal))
|
||||
|
||||
for _, val := range arrayVal {
|
||||
intValue, isInt := val.(int64)
|
||||
if !isInt {
|
||||
return nil, fmt.Errorf(`"%s" does not contain int keys`, key)
|
||||
}
|
||||
intArray = append(intArray, intValue)
|
||||
}
|
||||
return intArray, nil
|
||||
|
||||
case []int64:
|
||||
return value.([]int64), nil
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf(`"%s" is not a valid string array type`, key)
|
||||
}
|
||||
}
|
||||
|
||||
// IntArray returns a value at key that is expected to be a []int64
|
||||
// This function supports conversion (by copy) from
|
||||
// * []interface{}
|
||||
func (mmap MarshalMap) Int64Array(key string) ([]int64, error) {
|
||||
val, exists := mmap.Value(key)
|
||||
if !exists {
|
||||
return nil, fmt.Errorf(`"%s" is not set`, key)
|
||||
}
|
||||
|
||||
return castToInt64Array(key, val)
|
||||
}
|
||||
|
||||
// StringMap returns a value at key that is expected to be a map[string]string.
|
||||
// This function supports conversion (by copy) from
|
||||
// * map[interface{}]interface{}
|
||||
// * map[string]interface{}
|
||||
func (mmap MarshalMap) StringMap(key string) (map[string]string, error) {
|
||||
val, exists := mmap.Value(key)
|
||||
if !exists {
|
||||
return nil, fmt.Errorf(`"%s" is not set`, key)
|
||||
}
|
||||
|
||||
switch val.(type) {
|
||||
case map[string]string:
|
||||
return val.(map[string]string), nil
|
||||
|
||||
default:
|
||||
valueMeta := reflect.ValueOf(val)
|
||||
if valueMeta.Kind() != reflect.Map {
|
||||
return nil, fmt.Errorf(`"%s" is expected to be a map[string]string but is %T`, key, val)
|
||||
}
|
||||
|
||||
result := make(map[string]string)
|
||||
for _, keyMeta := range valueMeta.MapKeys() {
|
||||
strKey, isString := keyMeta.Interface().(string)
|
||||
if !isString {
|
||||
return nil, fmt.Errorf(`"%s" is expected to be a map[string]string. Key is not a string`, key)
|
||||
}
|
||||
|
||||
value := valueMeta.MapIndex(keyMeta)
|
||||
strValue, isString := value.Interface().(string)
|
||||
if !isString {
|
||||
return nil, fmt.Errorf(`"%s" is expected to be a map[string]string. Value is not a string`, key)
|
||||
}
|
||||
|
||||
result[strKey] = strValue
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
}
|
||||
|
||||
// StringArrayMap returns a value at key that is expected to be a
|
||||
// map[string][]string. This function supports conversion (by copy) from
|
||||
// * map[interface{}][]interface{}
|
||||
// * map[interface{}]interface{}
|
||||
// * map[string]interface{}
|
||||
func (mmap MarshalMap) StringArrayMap(key string) (map[string][]string, error) {
|
||||
val, exists := mmap.Value(key)
|
||||
if !exists {
|
||||
return nil, fmt.Errorf(`"%s" is not set`, key)
|
||||
}
|
||||
|
||||
switch val.(type) {
|
||||
case map[string][]string:
|
||||
return val.(map[string][]string), nil
|
||||
|
||||
default:
|
||||
valueMeta := reflect.ValueOf(val)
|
||||
if valueMeta.Kind() != reflect.Map {
|
||||
return nil, fmt.Errorf(`"%s" is expected to be a map[string][]string but is %T`, key, val)
|
||||
}
|
||||
|
||||
result := make(map[string][]string)
|
||||
for _, keyMeta := range valueMeta.MapKeys() {
|
||||
strKey, isString := keyMeta.Interface().(string)
|
||||
if !isString {
|
||||
return nil, fmt.Errorf(`"%s" is expected to be a map[string][]string. Key is not a string`, key)
|
||||
}
|
||||
|
||||
value := valueMeta.MapIndex(keyMeta)
|
||||
arrayValue, err := castToStringArray(strKey, value.Interface())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`"%s" is expected to be a map[string][]string. Value is not a []string`, key)
|
||||
}
|
||||
|
||||
result[strKey] = arrayValue
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
}
|
||||
|
||||
// MarshalMap returns a value at key that is expected to be another MarshalMap
|
||||
// This function supports conversion (by copy) from
|
||||
// * map[interface{}]interface{}
|
||||
func (mmap MarshalMap) MarshalMap(key string) (MarshalMap, error) {
|
||||
val, exists := mmap.Value(key)
|
||||
if !exists {
|
||||
return nil, fmt.Errorf(`"%s" is not set`, key)
|
||||
}
|
||||
|
||||
return ConvertToMarshalMap(val, nil)
|
||||
}
|
||||
|
||||
// Value returns a value from a given value path.
|
||||
// Fields can be accessed by their name. Nested fields can be accessed by using
|
||||
// "/" as a separator. Arrays can be addressed using the standard array
|
||||
// notation "[<index>]".
|
||||
// Examples:
|
||||
// "key" -> mmap["key"] single value
|
||||
// "key1/key2" -> mmap["key1"]["key2"] nested map
|
||||
// "key1[0]" -> mmap["key1"][0] nested array
|
||||
// "key1[0]key2" -> mmap["key1"][0]["key2"] nested array, nested map
|
||||
func (mmap MarshalMap) Value(key string) (interface{}, bool) {
|
||||
return mmap.resolvePath(key, mmap)
|
||||
}
|
||||
|
||||
func (mmap MarshalMap) resolvePathKey(key string) (int, int) {
|
||||
keyEnd := len(key)
|
||||
nextKeyStart := keyEnd
|
||||
pathIdx := strings.IndexRune(key, MarshalMapSeparator)
|
||||
arrayIdx := strings.IndexRune(key, MarshalMapArrayBegin)
|
||||
|
||||
if pathIdx > -1 && pathIdx < keyEnd {
|
||||
keyEnd = pathIdx
|
||||
nextKeyStart = pathIdx + 1 // don't include slash
|
||||
}
|
||||
if arrayIdx > -1 && arrayIdx < keyEnd {
|
||||
keyEnd = arrayIdx
|
||||
nextKeyStart = arrayIdx // include bracket because of multidimensional arrays
|
||||
}
|
||||
|
||||
// a -> key: "a", remain: "" -- value
|
||||
// a/b/c -> key: "a", remain: "b/c" -- nested map
|
||||
// a[1]b/c -> key: "a", remain: "[1]b/c" -- nested array
|
||||
|
||||
return keyEnd, nextKeyStart
|
||||
}
|
||||
|
||||
func (mmap MarshalMap) resolvePath(key string, value interface{}) (interface{}, bool) {
|
||||
if len(key) == 0 {
|
||||
return value, true // ### return, found requested value ###
|
||||
}
|
||||
|
||||
valueMeta := reflect.ValueOf(value)
|
||||
switch valueMeta.Kind() {
|
||||
case reflect.Array, reflect.Slice:
|
||||
startIdx := strings.IndexRune(key, MarshalMapArrayBegin) // Must be first char, otherwise malformed
|
||||
endIdx := strings.IndexRune(key, MarshalMapArrayEnd) // Must be > startIdx, otherwise malformed
|
||||
|
||||
if startIdx == -1 || endIdx == -1 {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
if startIdx == 0 && endIdx > startIdx {
|
||||
index, err := strconv.Atoi(key[startIdx+1 : endIdx])
|
||||
|
||||
// [1] -> index: "1", remain: "" -- value
|
||||
// [1]a/b -> index: "1", remain: "a/b" -- nested map
|
||||
// [1][2] -> index: "1", remain: "[2]" -- nested array
|
||||
|
||||
if err == nil && index < valueMeta.Len() {
|
||||
item := valueMeta.Index(index).Interface()
|
||||
key := key[endIdx+1:]
|
||||
return mmap.resolvePath(key, item) // ### return, nested array ###
|
||||
}
|
||||
}
|
||||
|
||||
case reflect.Map:
|
||||
keyMeta := reflect.ValueOf(key)
|
||||
if storedValue := valueMeta.MapIndex(keyMeta); storedValue.IsValid() {
|
||||
return storedValue.Interface(), true
|
||||
}
|
||||
|
||||
keyEnd, nextKeyStart := mmap.resolvePathKey(key)
|
||||
pathKey := key[:keyEnd]
|
||||
keyMeta = reflect.ValueOf(pathKey)
|
||||
|
||||
if storedValue := valueMeta.MapIndex(keyMeta); storedValue.IsValid() {
|
||||
remain := key[nextKeyStart:]
|
||||
return mmap.resolvePath(remain, storedValue.Interface()) // ### return, nested map ###
|
||||
}
|
||||
}
|
||||
|
||||
return nil, false
|
||||
}
|
||||
227
vendor/github.com/trivago/tgo/tcontainer/trie.go
generated
vendored
Normal file
227
vendor/github.com/trivago/tgo/tcontainer/trie.go
generated
vendored
Normal file
@@ -0,0 +1,227 @@
|
||||
// Copyright 2015-2016 trivago GmbH
|
||||
//
|
||||
// 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 tcontainer
|
||||
|
||||
// TrieNode represents a single node inside a trie.
|
||||
// Each node can contain a payload which can be retrieved after a successfull
|
||||
// match. In addition to that PathLen will contain the length of the match.
|
||||
type TrieNode struct {
|
||||
suffix []byte
|
||||
children []*TrieNode
|
||||
longestPath int
|
||||
PathLen int
|
||||
Payload interface{}
|
||||
}
|
||||
|
||||
// NewTrie creates a new root TrieNode
|
||||
func NewTrie(data []byte, payload interface{}) *TrieNode {
|
||||
return &TrieNode{
|
||||
suffix: data,
|
||||
children: []*TrieNode{},
|
||||
longestPath: len(data),
|
||||
PathLen: len(data),
|
||||
Payload: payload,
|
||||
}
|
||||
}
|
||||
|
||||
func (node *TrieNode) addNewChild(data []byte, payload interface{}, pathLen int) {
|
||||
if node.longestPath < pathLen {
|
||||
node.longestPath = pathLen
|
||||
}
|
||||
|
||||
idx := len(node.children)
|
||||
node.children = append(node.children, nil)
|
||||
|
||||
for idx > 0 {
|
||||
nextIdx := idx - 1
|
||||
if node.children[nextIdx].longestPath > pathLen {
|
||||
break
|
||||
}
|
||||
node.children[idx] = node.children[nextIdx]
|
||||
idx = nextIdx
|
||||
}
|
||||
|
||||
node.children[idx] = &TrieNode{
|
||||
suffix: data,
|
||||
children: []*TrieNode{},
|
||||
longestPath: pathLen,
|
||||
PathLen: pathLen,
|
||||
Payload: payload,
|
||||
}
|
||||
}
|
||||
|
||||
func (node *TrieNode) replace(oldChild *TrieNode, newChild *TrieNode) {
|
||||
for i, child := range node.children {
|
||||
if child == oldChild {
|
||||
node.children[i] = newChild
|
||||
return // ### return, replaced ###
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ForEach applies a function to each node in the tree including and below the
|
||||
// passed node.
|
||||
func (node *TrieNode) ForEach(callback func(*TrieNode)) {
|
||||
callback(node)
|
||||
for _, child := range node.children {
|
||||
child.ForEach(callback)
|
||||
}
|
||||
}
|
||||
|
||||
// Add adds a new data path to the trie.
|
||||
// The TrieNode returned is the (new) root node so you should always reassign
|
||||
// the root with the return value of Add.
|
||||
func (node *TrieNode) Add(data []byte, payload interface{}) *TrieNode {
|
||||
return node.addPath(data, payload, len(data), nil)
|
||||
}
|
||||
|
||||
func (node *TrieNode) addPath(data []byte, payload interface{}, pathLen int, parent *TrieNode) *TrieNode {
|
||||
dataLen := len(data)
|
||||
suffixLen := len(node.suffix)
|
||||
testLen := suffixLen
|
||||
if dataLen < suffixLen {
|
||||
testLen = dataLen
|
||||
}
|
||||
|
||||
var splitIdx int
|
||||
for splitIdx = 0; splitIdx < testLen; splitIdx++ {
|
||||
if data[splitIdx] != node.suffix[splitIdx] {
|
||||
break // ### break, split found ###
|
||||
}
|
||||
}
|
||||
|
||||
if splitIdx == suffixLen {
|
||||
// Continue down or stop here (full suffix match)
|
||||
|
||||
if splitIdx == dataLen {
|
||||
node.Payload = payload // may overwrite
|
||||
return node // ### return, path already stored ###
|
||||
}
|
||||
|
||||
data = data[splitIdx:]
|
||||
if suffixLen > 0 {
|
||||
for _, child := range node.children {
|
||||
if child.suffix[0] == data[0] {
|
||||
child.addPath(data, payload, pathLen, node)
|
||||
return node // ### return, continue on path ###
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
node.addNewChild(data, payload, pathLen)
|
||||
return node // ### return, new leaf ###
|
||||
}
|
||||
|
||||
if splitIdx == dataLen {
|
||||
// Make current node a subpath of new data node (full data match)
|
||||
// This case implies that dataLen < suffixLen as splitIdx == suffixLen
|
||||
// did not match.
|
||||
|
||||
node.suffix = node.suffix[splitIdx:]
|
||||
|
||||
newParent := NewTrie(data, payload)
|
||||
newParent.PathLen = pathLen
|
||||
newParent.longestPath = node.longestPath
|
||||
newParent.children = []*TrieNode{node}
|
||||
|
||||
if parent != nil {
|
||||
parent.replace(node, newParent)
|
||||
}
|
||||
return newParent // ### return, rotation ###
|
||||
}
|
||||
|
||||
// New parent required with both nodes as children (partial match)
|
||||
|
||||
node.suffix = node.suffix[splitIdx:]
|
||||
|
||||
newParent := NewTrie(data[:splitIdx], nil)
|
||||
newParent.PathLen = 0
|
||||
newParent.longestPath = node.longestPath
|
||||
newParent.children = []*TrieNode{node}
|
||||
newParent.addNewChild(data[splitIdx:], payload, pathLen)
|
||||
|
||||
if parent != nil {
|
||||
parent.replace(node, newParent)
|
||||
}
|
||||
return newParent // ### return, new parent ###
|
||||
}
|
||||
|
||||
// Match compares the trie to the given data stream.
|
||||
// Match returns true if data can be completely matched to the trie.
|
||||
func (node *TrieNode) Match(data []byte) *TrieNode {
|
||||
dataLen := len(data)
|
||||
suffixLen := len(node.suffix)
|
||||
if dataLen < suffixLen {
|
||||
return nil // ### return, cannot be fully matched ###
|
||||
}
|
||||
|
||||
for i := 0; i < suffixLen; i++ {
|
||||
if data[i] != node.suffix[i] {
|
||||
return nil // ### return, no match ###
|
||||
}
|
||||
}
|
||||
|
||||
if dataLen == suffixLen {
|
||||
if node.PathLen > 0 {
|
||||
return node // ### return, full match ###
|
||||
}
|
||||
return nil // ### return, invalid match ###
|
||||
}
|
||||
|
||||
data = data[suffixLen:]
|
||||
numChildren := len(node.children)
|
||||
for i := 0; i < numChildren; i++ {
|
||||
matchedNode := node.children[i].Match(data)
|
||||
if matchedNode != nil {
|
||||
return matchedNode // ### return, match found ###
|
||||
}
|
||||
}
|
||||
|
||||
return nil // ### return, no valid path ###
|
||||
}
|
||||
|
||||
// MatchStart compares the trie to the beginning of the given data stream.
|
||||
// MatchStart returns true if the beginning of data can be matched to the trie.
|
||||
func (node *TrieNode) MatchStart(data []byte) *TrieNode {
|
||||
dataLen := len(data)
|
||||
suffixLen := len(node.suffix)
|
||||
if dataLen < suffixLen {
|
||||
return nil // ### return, cannot be fully matched ###
|
||||
}
|
||||
|
||||
for i := 0; i < suffixLen; i++ {
|
||||
if data[i] != node.suffix[i] {
|
||||
return nil // ### return, no match ###
|
||||
}
|
||||
}
|
||||
|
||||
// Match longest path first
|
||||
|
||||
data = data[suffixLen:]
|
||||
numChildren := len(node.children)
|
||||
for i := 0; i < numChildren; i++ {
|
||||
matchedNode := node.children[i].MatchStart(data)
|
||||
if matchedNode != nil {
|
||||
return matchedNode // ### return, match found ###
|
||||
}
|
||||
}
|
||||
|
||||
// May be only a part of data but we have a valid match
|
||||
|
||||
if node.PathLen > 0 {
|
||||
return node // ### return, full match ###
|
||||
}
|
||||
return nil // ### return, no valid path ###
|
||||
}
|
||||
373
vendor/github.com/trivago/tgo/treflect/reflection.go
generated
vendored
Normal file
373
vendor/github.com/trivago/tgo/treflect/reflection.go
generated
vendored
Normal file
@@ -0,0 +1,373 @@
|
||||
// Copyright 2015-2016 trivago GmbH
|
||||
//
|
||||
// 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 treflect
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// GetMissingMethods checks if a given object implements all methods of a
|
||||
// given interface. It returns the interface coverage [0..1] as well as an array
|
||||
// of error messages. If the interface is correctly implemented the coverage is
|
||||
// 1 and the error message array is empty.
|
||||
func GetMissingMethods(objType reflect.Type, ifaceType reflect.Type) (float32, []string) {
|
||||
missing := []string{}
|
||||
if objType.Implements(ifaceType) {
|
||||
return 1.0, missing
|
||||
}
|
||||
|
||||
methodCount := ifaceType.NumMethod()
|
||||
for mIdx := 0; mIdx < methodCount; mIdx++ {
|
||||
ifaceMethod := ifaceType.Method(mIdx)
|
||||
objMethod, exists := objType.MethodByName(ifaceMethod.Name)
|
||||
signatureMismatch := false
|
||||
|
||||
switch {
|
||||
case !exists:
|
||||
missing = append(missing, fmt.Sprintf("Missing: \"%s\" %v", ifaceMethod.Name, ifaceMethod.Type))
|
||||
continue // ### continue, error found ###
|
||||
|
||||
case ifaceMethod.Type.NumOut() != objMethod.Type.NumOut():
|
||||
signatureMismatch = true
|
||||
|
||||
case ifaceMethod.Type.NumIn()+1 != objMethod.Type.NumIn():
|
||||
signatureMismatch = true
|
||||
|
||||
default:
|
||||
for oIdx := 0; !signatureMismatch && oIdx < ifaceMethod.Type.NumOut(); oIdx++ {
|
||||
signatureMismatch = ifaceMethod.Type.Out(oIdx) != objMethod.Type.Out(oIdx)
|
||||
}
|
||||
for iIdx := 0; !signatureMismatch && iIdx < ifaceMethod.Type.NumIn(); iIdx++ {
|
||||
signatureMismatch = ifaceMethod.Type.In(iIdx) != objMethod.Type.In(iIdx+1)
|
||||
}
|
||||
}
|
||||
|
||||
if signatureMismatch {
|
||||
missing = append(missing, fmt.Sprintf("Invalid: \"%s\" %v is not %v", ifaceMethod.Name, objMethod.Type, ifaceMethod.Type))
|
||||
}
|
||||
}
|
||||
|
||||
return float32(methodCount-len(missing)) / float32(methodCount), missing
|
||||
}
|
||||
|
||||
// Int64 converts any signed number type to an int64.
|
||||
// The second parameter is returned as false if a non-number type was given.
|
||||
func Int64(v interface{}) (int64, bool) {
|
||||
|
||||
switch reflect.TypeOf(v).Kind() {
|
||||
case reflect.Int:
|
||||
return int64(v.(int)), true
|
||||
case reflect.Int8:
|
||||
return int64(v.(int8)), true
|
||||
case reflect.Int16:
|
||||
return int64(v.(int16)), true
|
||||
case reflect.Int32:
|
||||
return int64(v.(int32)), true
|
||||
case reflect.Int64:
|
||||
return v.(int64), true
|
||||
case reflect.Float32:
|
||||
return int64(v.(float32)), true
|
||||
case reflect.Float64:
|
||||
return int64(v.(float64)), true
|
||||
}
|
||||
|
||||
fmt.Printf("%t\n%#v\n%#v\n", v, v, reflect.TypeOf(v).Kind())
|
||||
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// Uint64 converts any unsigned number type to an uint64.
|
||||
// The second parameter is returned as false if a non-number type was given.
|
||||
func Uint64(v interface{}) (uint64, bool) {
|
||||
|
||||
switch reflect.TypeOf(v).Kind() {
|
||||
case reflect.Uint:
|
||||
return uint64(v.(uint)), true
|
||||
case reflect.Uint8:
|
||||
return uint64(v.(uint8)), true
|
||||
case reflect.Uint16:
|
||||
return uint64(v.(uint16)), true
|
||||
case reflect.Uint32:
|
||||
return uint64(v.(uint32)), true
|
||||
case reflect.Uint64:
|
||||
return v.(uint64), true
|
||||
}
|
||||
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// Float32 converts any number type to an float32.
|
||||
// The second parameter is returned as false if a non-number type was given.
|
||||
func Float32(v interface{}) (float32, bool) {
|
||||
|
||||
switch reflect.TypeOf(v).Kind() {
|
||||
case reflect.Int:
|
||||
return float32(v.(int)), true
|
||||
case reflect.Uint:
|
||||
return float32(v.(uint)), true
|
||||
case reflect.Int8:
|
||||
return float32(v.(int8)), true
|
||||
case reflect.Uint8:
|
||||
return float32(v.(uint8)), true
|
||||
case reflect.Int16:
|
||||
return float32(v.(int16)), true
|
||||
case reflect.Uint16:
|
||||
return float32(v.(uint16)), true
|
||||
case reflect.Int32:
|
||||
return float32(v.(int32)), true
|
||||
case reflect.Uint32:
|
||||
return float32(v.(uint32)), true
|
||||
case reflect.Int64:
|
||||
return float32(v.(int64)), true
|
||||
case reflect.Uint64:
|
||||
return float32(v.(uint64)), true
|
||||
case reflect.Float32:
|
||||
return v.(float32), true
|
||||
case reflect.Float64:
|
||||
return float32(v.(float64)), true
|
||||
}
|
||||
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// Float64 converts any number type to an float64.
|
||||
// The second parameter is returned as false if a non-number type was given.
|
||||
func Float64(v interface{}) (float64, bool) {
|
||||
|
||||
switch reflect.TypeOf(v).Kind() {
|
||||
case reflect.Int:
|
||||
return float64(v.(int)), true
|
||||
case reflect.Uint:
|
||||
return float64(v.(uint)), true
|
||||
case reflect.Int8:
|
||||
return float64(v.(int8)), true
|
||||
case reflect.Uint8:
|
||||
return float64(v.(uint8)), true
|
||||
case reflect.Int16:
|
||||
return float64(v.(int16)), true
|
||||
case reflect.Uint16:
|
||||
return float64(v.(uint16)), true
|
||||
case reflect.Int32:
|
||||
return float64(v.(int32)), true
|
||||
case reflect.Uint32:
|
||||
return float64(v.(uint32)), true
|
||||
case reflect.Int64:
|
||||
return float64(v.(int64)), true
|
||||
case reflect.Uint64:
|
||||
return float64(v.(uint64)), true
|
||||
case reflect.Float32:
|
||||
return float64(v.(float32)), true
|
||||
case reflect.Float64:
|
||||
return v.(float64), true
|
||||
}
|
||||
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// RemovePtrFromType will return the type of t and strips away any pointer(s)
|
||||
// in front of the actual type.
|
||||
func RemovePtrFromType(t interface{}) reflect.Type {
|
||||
var v reflect.Type
|
||||
if rt, isType := t.(reflect.Type); isType {
|
||||
v = rt
|
||||
} else {
|
||||
v = reflect.TypeOf(t)
|
||||
}
|
||||
for v.Kind() == reflect.Ptr {
|
||||
v = v.Elem()
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// RemovePtrFromValue will return the value of t and strips away any pointer(s)
|
||||
// in front of the actual type.
|
||||
func RemovePtrFromValue(t interface{}) reflect.Value {
|
||||
var v reflect.Value
|
||||
if rv, isValue := t.(reflect.Value); isValue {
|
||||
v = rv
|
||||
} else {
|
||||
v = reflect.ValueOf(t)
|
||||
}
|
||||
for v.Type().Kind() == reflect.Ptr {
|
||||
v = v.Elem()
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// UnsafeCopy will copy data from src to dst while ignoring type information.
|
||||
// Both types need to be of the same size and dst and src have to be pointers.
|
||||
// UnsafeCopy will panic if these requirements are not met.
|
||||
func UnsafeCopy(dst, src interface{}) {
|
||||
dstValue := reflect.ValueOf(dst)
|
||||
srcValue := reflect.ValueOf(src)
|
||||
UnsafeCopyValue(dstValue, srcValue)
|
||||
}
|
||||
|
||||
// UnsafeCopyValue will copy data from src to dst while ignoring type
|
||||
// information. Both types need to be of the same size or this function will
|
||||
// panic. Also both types must support dereferencing via reflect.Elem()
|
||||
func UnsafeCopyValue(dstValue reflect.Value, srcValue reflect.Value) {
|
||||
dstType := dstValue.Elem().Type()
|
||||
srcType := srcValue.Type()
|
||||
|
||||
var srcPtr uintptr
|
||||
if srcValue.Kind() != reflect.Ptr {
|
||||
// If we don't get a pointer to our source data we need to forcefully
|
||||
// retrieve it by accessing the interface pointer. This is ok as we
|
||||
// only read from it.
|
||||
iface := srcValue.Interface()
|
||||
srcPtr = reflect.ValueOf(&iface).Elem().InterfaceData()[1] // Pointer to data
|
||||
} else {
|
||||
srcType = srcValue.Elem().Type()
|
||||
srcPtr = srcValue.Pointer()
|
||||
}
|
||||
|
||||
if dstType.Size() != srcType.Size() {
|
||||
panic("Type size mismatch between " + dstType.String() + " and " + srcType.String())
|
||||
}
|
||||
|
||||
dstAsSlice := *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{
|
||||
Data: dstValue.Pointer(),
|
||||
Len: int(dstType.Size()),
|
||||
Cap: int(dstType.Size()),
|
||||
}))
|
||||
|
||||
srcAsSlice := *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{
|
||||
Data: srcPtr,
|
||||
Len: int(srcType.Size()),
|
||||
Cap: int(srcType.Size()),
|
||||
}))
|
||||
|
||||
copy(dstAsSlice, srcAsSlice)
|
||||
}
|
||||
|
||||
// SetMemberByName sets member name of the given pointer-to-struct to the data
|
||||
// passed to this function. The member may be private, too.
|
||||
func SetMemberByName(ptrToStruct interface{}, name string, data interface{}) {
|
||||
structVal := reflect.Indirect(reflect.ValueOf(ptrToStruct))
|
||||
member := structVal.FieldByName(name)
|
||||
|
||||
SetValue(member, data)
|
||||
}
|
||||
|
||||
// SetMemberByIndex sets member idx of the given pointer-to-struct to the data
|
||||
// passed to this function. The member may be private, too.
|
||||
func SetMemberByIndex(ptrToStruct interface{}, idx int, data interface{}) {
|
||||
structVal := reflect.Indirect(reflect.ValueOf(ptrToStruct))
|
||||
member := structVal.Field(idx)
|
||||
|
||||
SetValue(member, data)
|
||||
}
|
||||
|
||||
// SetValue sets an addressable value to the data passed to this function.
|
||||
// In contrast to golangs reflect package this will also work with private
|
||||
// variables. Please note that this function may not support all types, yet.
|
||||
func SetValue(member reflect.Value, data interface{}) {
|
||||
if member.CanSet() {
|
||||
member.Set(reflect.ValueOf(data).Convert(member.Type()))
|
||||
return // ### return, easy way ###
|
||||
}
|
||||
|
||||
if !member.CanAddr() {
|
||||
panic("SetValue requires addressable member type")
|
||||
}
|
||||
|
||||
ptrToMember := unsafe.Pointer(member.UnsafeAddr())
|
||||
dataValue := reflect.ValueOf(data)
|
||||
|
||||
switch member.Kind() {
|
||||
case reflect.Bool:
|
||||
*(*bool)(ptrToMember) = dataValue.Bool()
|
||||
|
||||
case reflect.Uint:
|
||||
*(*uint)(ptrToMember) = uint(dataValue.Uint())
|
||||
|
||||
case reflect.Uint8:
|
||||
*(*uint8)(ptrToMember) = uint8(dataValue.Uint())
|
||||
|
||||
case reflect.Uint16:
|
||||
*(*uint16)(ptrToMember) = uint16(dataValue.Uint())
|
||||
|
||||
case reflect.Uint32:
|
||||
*(*uint32)(ptrToMember) = uint32(dataValue.Uint())
|
||||
|
||||
case reflect.Uint64:
|
||||
*(*uint64)(ptrToMember) = dataValue.Uint()
|
||||
|
||||
case reflect.Int:
|
||||
*(*int)(ptrToMember) = int(dataValue.Int())
|
||||
|
||||
case reflect.Int8:
|
||||
*(*int8)(ptrToMember) = int8(dataValue.Int())
|
||||
|
||||
case reflect.Int16:
|
||||
*(*int16)(ptrToMember) = int16(dataValue.Int())
|
||||
|
||||
case reflect.Int32:
|
||||
*(*int32)(ptrToMember) = int32(dataValue.Int())
|
||||
|
||||
case reflect.Int64:
|
||||
*(*int64)(ptrToMember) = dataValue.Int()
|
||||
|
||||
case reflect.Float32:
|
||||
*(*float32)(ptrToMember) = float32(dataValue.Float())
|
||||
|
||||
case reflect.Float64:
|
||||
*(*float64)(ptrToMember) = dataValue.Float()
|
||||
|
||||
case reflect.Complex64:
|
||||
*(*complex64)(ptrToMember) = complex64(dataValue.Complex())
|
||||
|
||||
case reflect.Complex128:
|
||||
*(*complex128)(ptrToMember) = dataValue.Complex()
|
||||
|
||||
case reflect.String:
|
||||
*(*string)(ptrToMember) = dataValue.String()
|
||||
|
||||
case reflect.Map, reflect.Chan:
|
||||
// Exploit the fact that "map" is actually "*runtime.hmap" and force
|
||||
// overwrite that pointer in the passed struct.
|
||||
// Same foes for "chan" which is actually "*runtime.hchan".
|
||||
|
||||
// Note: Assigning a map or channel to another variable does NOT copy
|
||||
// the contents so copying the pointer follows go's standard behavior.
|
||||
dataAsPtr := unsafe.Pointer(dataValue.Pointer())
|
||||
*(**uintptr)(ptrToMember) = (*uintptr)(dataAsPtr)
|
||||
|
||||
case reflect.Interface:
|
||||
// Interfaces are basically two pointers, see runtime.iface.
|
||||
// We want to modify exactly that data, which is returned by
|
||||
// the InterfaceData() method.
|
||||
|
||||
if dataValue.Kind() != reflect.Interface {
|
||||
// A type reference was passed. In order to overwrite the memory
|
||||
// Representation of an interface we need to generate it first.
|
||||
// Reflect does not allow us to do that unless we use the
|
||||
// InterfaceData method which exposes the internal representation
|
||||
// of an interface.
|
||||
interfaceData := reflect.ValueOf(&data).Elem().InterfaceData()
|
||||
dataValue = reflect.ValueOf(interfaceData)
|
||||
}
|
||||
fallthrough
|
||||
|
||||
default:
|
||||
// Complex types are assigned memcpy style.
|
||||
// Note: This should not break the garbage collector although we cannot
|
||||
// be 100% sure on this.
|
||||
UnsafeCopyValue(member.Addr(), dataValue)
|
||||
}
|
||||
}
|
||||
97
vendor/github.com/trivago/tgo/treflect/typeregistry.go
generated
vendored
Normal file
97
vendor/github.com/trivago/tgo/treflect/typeregistry.go
generated
vendored
Normal file
@@ -0,0 +1,97 @@
|
||||
// Copyright 2015-2016 trivago GmbH
|
||||
//
|
||||
// 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 treflect
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// TypeRegistry is a name to type registry used to create objects by name.
|
||||
type TypeRegistry struct {
|
||||
namedType map[string]reflect.Type
|
||||
}
|
||||
|
||||
// NewTypeRegistry creates a new TypeRegistry. Note that there is a global type
|
||||
// registry available in the main tgo package (tgo.TypeRegistry).
|
||||
func NewTypeRegistry() TypeRegistry {
|
||||
return TypeRegistry{
|
||||
namedType: make(map[string]reflect.Type),
|
||||
}
|
||||
}
|
||||
|
||||
// Register a plugin to the TypeRegistry by passing an uninitialized object.
|
||||
func (registry TypeRegistry) Register(typeInstance interface{}) {
|
||||
registry.RegisterWithDepth(typeInstance, 1)
|
||||
}
|
||||
|
||||
// RegisterWithDepth to register a plugin to the TypeRegistry by passing an uninitialized object.
|
||||
func (registry TypeRegistry) RegisterWithDepth(typeInstance interface{}, depth int) {
|
||||
structType := reflect.TypeOf(typeInstance)
|
||||
packageName := structType.PkgPath()
|
||||
typeName := structType.Name()
|
||||
|
||||
pathTokens := strings.Split(packageName, "/")
|
||||
maxDepth := 3
|
||||
if len(pathTokens) < maxDepth {
|
||||
maxDepth = len(pathTokens)
|
||||
}
|
||||
|
||||
for n := depth; n <= maxDepth; n++ {
|
||||
shortTypeName := strings.Join(pathTokens[len(pathTokens)-n:], ".") + "." + typeName
|
||||
registry.namedType[shortTypeName] = structType
|
||||
}
|
||||
}
|
||||
|
||||
// New creates an uninitialized object by class name.
|
||||
// The class name has to be "package.class" or "package/subpackage.class".
|
||||
// The gollum package is omitted from the package path.
|
||||
func (registry TypeRegistry) New(typeName string) (interface{}, error) {
|
||||
structType, exists := registry.namedType[typeName]
|
||||
if exists {
|
||||
return reflect.New(structType).Interface(), nil
|
||||
}
|
||||
return nil, fmt.Errorf("Unknown class: %s", typeName)
|
||||
}
|
||||
|
||||
// GetTypeOf returns only the type asscociated with the given name.
|
||||
// If the name is not registered, nil is returned.
|
||||
// The type returned will be a pointer type.
|
||||
func (registry TypeRegistry) GetTypeOf(typeName string) reflect.Type {
|
||||
if structType, exists := registry.namedType[typeName]; exists {
|
||||
return reflect.PtrTo(structType)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsTypeRegistered returns true if a type is registered to this registry.
|
||||
// Note that GetTypeOf can do the same thing by checking for nil but also
|
||||
// returns the type, so in many cases you will want to call this function.
|
||||
func (registry TypeRegistry) IsTypeRegistered(typeName string) bool {
|
||||
_, exists := registry.namedType[typeName]
|
||||
return exists
|
||||
}
|
||||
|
||||
// GetRegistered returns the names of all registered types for a given package
|
||||
func (registry TypeRegistry) GetRegistered(packageName string) []string {
|
||||
var result []string
|
||||
for key := range registry.namedType {
|
||||
if strings.HasPrefix(key, packageName) {
|
||||
result = append(result, key)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
24
vendor/github.com/xanzy/go-gitlab/.gitignore
generated
vendored
Normal file
24
vendor/github.com/xanzy/go-gitlab/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
# 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
|
||||
27
vendor/github.com/xanzy/go-gitlab/.travis.yml
generated
vendored
Normal file
27
vendor/github.com/xanzy/go-gitlab/.travis.yml
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.8.x
|
||||
- 1.9.x
|
||||
- 1.10.x
|
||||
- master
|
||||
|
||||
stages:
|
||||
- lint
|
||||
- test
|
||||
|
||||
jobs:
|
||||
include:
|
||||
- stage: lint
|
||||
script:
|
||||
- go get github.com/golang/lint/golint
|
||||
- golint -set_exit_status
|
||||
- go vet -v
|
||||
- stage: test
|
||||
script:
|
||||
- go test -v
|
||||
|
||||
matrix:
|
||||
allow_failures:
|
||||
- go: master
|
||||
fast_finish: true
|
||||
27
vendor/github.com/xanzy/go-gitlab/CHANGELOG.md
generated
vendored
Normal file
27
vendor/github.com/xanzy/go-gitlab/CHANGELOG.md
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
go-github CHANGELOG
|
||||
===================
|
||||
|
||||
0.6.0
|
||||
-----
|
||||
- Add support for the V4 Gitlab API. This means the older V3 API is no longer fully supported
|
||||
with this version. If you still need that version, please use the `f-api-v3` branch.
|
||||
|
||||
0.4.0
|
||||
-----
|
||||
- Add support to use [`sudo`](https://docs.gitlab.com/ce/api/README.html#sudo) for all API calls.
|
||||
- Add support for the Notification Settings API.
|
||||
- Add support for the Time Tracking API.
|
||||
- Make sure that the error response correctly outputs any returned errors.
|
||||
- And a reasonable number of smaller enhanchements and bugfixes.
|
||||
|
||||
0.3.0
|
||||
-----
|
||||
- Moved the tags related API calls to their own service, following the Gitlab API structure.
|
||||
|
||||
0.2.0
|
||||
-----
|
||||
- Convert all Option structs to use pointers for their fields.
|
||||
|
||||
0.1.0
|
||||
-----
|
||||
- Initial release.
|
||||
202
vendor/github.com/xanzy/go-gitlab/LICENSE
generated
vendored
Normal file
202
vendor/github.com/xanzy/go-gitlab/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,202 @@
|
||||
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.
|
||||
|
||||
171
vendor/github.com/xanzy/go-gitlab/README.md
generated
vendored
Normal file
171
vendor/github.com/xanzy/go-gitlab/README.md
generated
vendored
Normal file
@@ -0,0 +1,171 @@
|
||||
# go-gitlab
|
||||
|
||||
A GitLab API client enabling Go programs to interact with GitLab in a simple and uniform way
|
||||
|
||||
[](https://travis-ci.org/xanzy/go-gitlab)
|
||||
[](https://github.com/xanzy/go-gitlab/blob/master/LICENSE)
|
||||
[](https://sourcegraph.com/github.com/xanzy/go-gitlab?badge)
|
||||
[](https://godoc.org/github.com/xanzy/go-gitlab)
|
||||
[](https://goreportcard.com/report/github.com/xanzy/go-gitlab)
|
||||
[](https://github.com/xanzy/go-gitlab/issues)
|
||||
|
||||
## NOTE
|
||||
|
||||
Release v0.6.0 (released on 25-08-2017) no longer supports the older V3 Gitlab API. If
|
||||
you need V3 support, please use the `f-api-v3` branch. This release contains some backwards
|
||||
incompatible changes that were needed to fully support the V4 Gitlab API.
|
||||
|
||||
## Coverage
|
||||
|
||||
This API client package covers most of the existing Gitlab API calls and is updated regularly
|
||||
to add new and/or missing endpoints. Currently the following services are supported:
|
||||
|
||||
- [x] Award Emojis
|
||||
- [x] Branches
|
||||
- [x] Broadcast Messages
|
||||
- [ ] Project-level Variables
|
||||
- [ ] Group-level Variables
|
||||
- [x] Commits
|
||||
- [ ] Custom Attributes
|
||||
- [x] Deployments
|
||||
- [x] Deploy Keys
|
||||
- [x] Environments
|
||||
- [ ] Epics
|
||||
- [ ] Epic Issues
|
||||
- [x] Events
|
||||
- [x] Feature flags
|
||||
- [ ] Geo Nodes
|
||||
- [x] Gitignores templates
|
||||
- [ ] GitLab CI Config templates
|
||||
- [x] Groups
|
||||
- [ ] Group Access Requests
|
||||
- [x] Group Members
|
||||
- [x] Issues
|
||||
- [x] Issue Boards
|
||||
- [x] Jobs
|
||||
- [ ] Keys
|
||||
- [x] Labels
|
||||
- [ ] License
|
||||
- [x] Merge Requests
|
||||
- [x] Merge Request Approvals
|
||||
- [x] Project Milestones
|
||||
- [ ] Group Milestones
|
||||
- [x] Namespaces
|
||||
- [x] Notes (comments)
|
||||
- [ ] Discussions (threaded comments)
|
||||
- [x] Notification settings
|
||||
- [ ] Open source license templates
|
||||
- [x] Pages Domains
|
||||
- [x] Pipelines
|
||||
- [x] Pipeline Triggers
|
||||
- [x] Pipeline Schedules
|
||||
- [x] Projects (including setting Webhooks)
|
||||
- [ ] Project Access Requests
|
||||
- [ ] Project badges
|
||||
- [ ] Project import/export
|
||||
- [x] Project Members
|
||||
- [x] Project Snippets
|
||||
- [x] Protected Branches
|
||||
- [x] Repositories
|
||||
- [x] Repository Files
|
||||
- [x] Runners
|
||||
- [ ] Search
|
||||
- [x] Services
|
||||
- [x] Settings
|
||||
- [x] Sidekiq metrics
|
||||
- [x] Session
|
||||
- [x] System Hooks
|
||||
- [x] Tags
|
||||
- [x] Todos
|
||||
- [x] Users
|
||||
- [x] Validate CI configuration
|
||||
- [x] Version
|
||||
- [x] Wikis
|
||||
|
||||
## Usage
|
||||
|
||||
```go
|
||||
import "github.com/xanzy/go-gitlab"
|
||||
```
|
||||
|
||||
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 := gitlab.NewClient(nil, "yourtokengoeshere")
|
||||
//git.SetBaseURL("https://git.mydomain.com/api/v3")
|
||||
users, _, err := git.Users.ListUsers()
|
||||
```
|
||||
|
||||
Some API methods have optional parameters that can be passed. For example,
|
||||
to list all projects for user "svanharmelen":
|
||||
|
||||
```go
|
||||
git := gitlab.NewClient(nil)
|
||||
opt := &ListProjectsOptions{Search: gitlab.String("svanharmelen")}
|
||||
projects, _, err := git.Projects.ListProjects(opt)
|
||||
```
|
||||
|
||||
### Examples
|
||||
|
||||
The [examples](https://github.com/xanzy/go-gitlab/tree/master/examples) directory
|
||||
contains a couple for clear examples, of which one is partially listed here as well:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/xanzy/go-gitlab"
|
||||
)
|
||||
|
||||
func main() {
|
||||
git := gitlab.NewClient(nil, "yourtokengoeshere")
|
||||
|
||||
// Create new project
|
||||
p := &gitlab.CreateProjectOptions{
|
||||
Name: gitlab.String("My Project"),
|
||||
Description: gitlab.String("Just a test project to play with"),
|
||||
MergeRequestsEnabled: gitlab.Bool(true),
|
||||
SnippetsEnabled: gitlab.Bool(true),
|
||||
Visibility: gitlab.Visibility(gitlab.PublicVisibility),
|
||||
}
|
||||
project, _, err := git.Projects.CreateProject(p)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Add a new snippet
|
||||
s := &gitlab.CreateProjectSnippetOptions{
|
||||
Title: gitlab.String("Dummy Snippet"),
|
||||
FileName: gitlab.String("snippet.go"),
|
||||
Code: gitlab.String("package main...."),
|
||||
Visibility: gitlab.Visibility(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/github.com/xanzy/go-gitlab).
|
||||
|
||||
## ToDo
|
||||
|
||||
- The biggest thing this package still needs is tests :disappointed:
|
||||
|
||||
## Issues
|
||||
|
||||
- If you have an issue: report it on the [issue tracker](https://github.com/xanzy/go-gitlab/issues)
|
||||
|
||||
## Author
|
||||
|
||||
Sander van Harmelen (<sander@xanzy.io>)
|
||||
|
||||
## License
|
||||
|
||||
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>
|
||||
468
vendor/github.com/xanzy/go-gitlab/award_emojis.go
generated
vendored
Normal file
468
vendor/github.com/xanzy/go-gitlab/award_emojis.go
generated
vendored
Normal file
@@ -0,0 +1,468 @@
|
||||
//
|
||||
// Copyright 2017, 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/url"
|
||||
"time"
|
||||
)
|
||||
|
||||
// AwardEmojiService handles communication with the emoji awards related methods
|
||||
// of the GitLab API.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/api/award_emoji.html
|
||||
type AwardEmojiService struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// AwardEmoji represents a GitLab Award Emoji.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/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/ce/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/ce/api/award_emoji.html#list-an-awardable-39-s-award-emoji
|
||||
func (s *AwardEmojiService) ListMergeRequestAwardEmoji(pid interface{}, mergeRequestIID int, opt *ListAwardEmojiOptions, options ...OptionFunc) ([]*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/ce/api/award_emoji.html#list-an-awardable-39-s-award-emoji
|
||||
func (s *AwardEmojiService) ListIssueAwardEmoji(pid interface{}, issueIID int, opt *ListAwardEmojiOptions, options ...OptionFunc) ([]*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/ce/api/award_emoji.html#list-an-awardable-39-s-award-emoji
|
||||
func (s *AwardEmojiService) ListSnippetAwardEmoji(pid interface{}, snippetID int, opt *ListAwardEmojiOptions, options ...OptionFunc) ([]*AwardEmoji, *Response, error) {
|
||||
return s.listAwardEmoji(pid, awardSnippets, snippetID, opt, options...)
|
||||
}
|
||||
|
||||
func (s *AwardEmojiService) listAwardEmoji(pid interface{}, resource string, resourceID int, opt *ListAwardEmojiOptions, options ...OptionFunc) ([]*AwardEmoji, *Response, error) {
|
||||
project, err := parseID(pid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("projects/%s/%s/%d/award_emoji",
|
||||
url.QueryEscape(project),
|
||||
resource,
|
||||
resourceID,
|
||||
)
|
||||
|
||||
req, err := s.client.NewRequest("GET", 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, err
|
||||
}
|
||||
|
||||
// GetMergeRequestAwardEmoji get an award emoji from merge request.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/award_emoji.html#list-an-awardable-39-s-award-emoji
|
||||
func (s *AwardEmojiService) GetMergeRequestAwardEmoji(pid interface{}, mergeRequestIID, awardID int, options ...OptionFunc) (*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/ce/api/award_emoji.html#list-an-awardable-39-s-award-emoji
|
||||
func (s *AwardEmojiService) GetIssueAwardEmoji(pid interface{}, issueIID, awardID int, options ...OptionFunc) (*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/ce/api/award_emoji.html#list-an-awardable-39-s-award-emoji
|
||||
func (s *AwardEmojiService) GetSnippetAwardEmoji(pid interface{}, snippetID, awardID int, options ...OptionFunc) (*AwardEmoji, *Response, error) {
|
||||
return s.getAwardEmoji(pid, awardSnippets, snippetID, awardID, options...)
|
||||
}
|
||||
|
||||
func (s *AwardEmojiService) getAwardEmoji(pid interface{}, resource string, resourceID, awardID int, options ...OptionFunc) (*AwardEmoji, *Response, error) {
|
||||
project, err := parseID(pid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("projects/%s/%s/%d/award_emoji/%d",
|
||||
url.QueryEscape(project),
|
||||
resource,
|
||||
resourceID,
|
||||
awardID,
|
||||
)
|
||||
|
||||
req, err := s.client.NewRequest("GET", 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, err
|
||||
}
|
||||
|
||||
// CreateAwardEmojiOptions represents the available options for awarding emoji
|
||||
// for a resource
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/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/ce/api/award_emoji.html#award-a-new-emoji
|
||||
func (s *AwardEmojiService) CreateMergeRequestAwardEmoji(pid interface{}, mergeRequestIID int, opt *CreateAwardEmojiOptions, options ...OptionFunc) (*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/ce/api/award_emoji.html#award-a-new-emoji
|
||||
func (s *AwardEmojiService) CreateIssueAwardEmoji(pid interface{}, issueIID int, opt *CreateAwardEmojiOptions, options ...OptionFunc) (*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/ce/api/award_emoji.html#award-a-new-emoji
|
||||
func (s *AwardEmojiService) CreateSnippetAwardEmoji(pid interface{}, snippetID int, opt *CreateAwardEmojiOptions, options ...OptionFunc) (*AwardEmoji, *Response, error) {
|
||||
return s.createAwardEmoji(pid, awardSnippets, snippetID, opt, options...)
|
||||
}
|
||||
|
||||
func (s *AwardEmojiService) createAwardEmoji(pid interface{}, resource string, resourceID int, opt *CreateAwardEmojiOptions, options ...OptionFunc) (*AwardEmoji, *Response, error) {
|
||||
project, err := parseID(pid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("projects/%s/%s/%d/award_emoji",
|
||||
url.QueryEscape(project),
|
||||
resource,
|
||||
resourceID,
|
||||
)
|
||||
|
||||
req, err := s.client.NewRequest("POST", 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, err
|
||||
}
|
||||
|
||||
// DeleteIssueAwardEmoji delete award emoji on an issue.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/award_emoji.html#award-a-new-emoji-on-a-note
|
||||
func (s *AwardEmojiService) DeleteIssueAwardEmoji(pid interface{}, issueIID, awardID int, options ...OptionFunc) (*Response, error) {
|
||||
return s.deleteAwardEmoji(pid, awardMergeRequest, issueIID, awardID, options...)
|
||||
}
|
||||
|
||||
// DeleteMergeRequestAwardEmoji delete award emoji on a merge request.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/award_emoji.html#award-a-new-emoji-on-a-note
|
||||
func (s *AwardEmojiService) DeleteMergeRequestAwardEmoji(pid interface{}, mergeRequestIID, awardID int, options ...OptionFunc) (*Response, error) {
|
||||
return s.deleteAwardEmoji(pid, awardMergeRequest, mergeRequestIID, awardID, options...)
|
||||
}
|
||||
|
||||
// DeleteSnippetAwardEmoji delete award emoji on a snippet.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/award_emoji.html#award-a-new-emoji-on-a-note
|
||||
func (s *AwardEmojiService) DeleteSnippetAwardEmoji(pid interface{}, snippetID, awardID int, options ...OptionFunc) (*Response, error) {
|
||||
return s.deleteAwardEmoji(pid, awardMergeRequest, snippetID, awardID, options...)
|
||||
}
|
||||
|
||||
// DeleteAwardEmoji Delete an award emoji on the specified resource.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/award_emoji.html#delete-an-award-emoji
|
||||
func (s *AwardEmojiService) deleteAwardEmoji(pid interface{}, resource string, resourceID, awardID int, options ...OptionFunc) (*Response, error) {
|
||||
project, err := parseID(pid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
u := fmt.Sprintf("projects/%s/%s/%d/award_emoji/%d", url.QueryEscape(project), resource,
|
||||
resourceID, awardID)
|
||||
|
||||
req, err := s.client.NewRequest("DELETE", 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/ce/api/award_emoji.html#award-emoji-on-notes
|
||||
func (s *AwardEmojiService) ListIssuesAwardEmojiOnNote(pid interface{}, issueID, noteID int, opt *ListAwardEmojiOptions, options ...OptionFunc) ([]*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/ce/api/award_emoji.html#award-emoji-on-notes
|
||||
func (s *AwardEmojiService) ListMergeRequestAwardEmojiOnNote(pid interface{}, mergeRequestIID, noteID int, opt *ListAwardEmojiOptions, options ...OptionFunc) ([]*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/ce/api/award_emoji.html#award-emoji-on-notes
|
||||
func (s *AwardEmojiService) ListSnippetAwardEmojiOnNote(pid interface{}, snippetIID, noteID int, opt *ListAwardEmojiOptions, options ...OptionFunc) ([]*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 ...OptionFunc) ([]*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", url.QueryEscape(project), resources,
|
||||
ressourceID, noteID)
|
||||
|
||||
req, err := s.client.NewRequest("GET", 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, err
|
||||
}
|
||||
|
||||
// GetIssuesAwardEmojiOnNote gets an award emoji on a note from an issue.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/award_emoji.html#award-emoji-on-notes
|
||||
func (s *AwardEmojiService) GetIssuesAwardEmojiOnNote(pid interface{}, issueID, noteID, awardID int, options ...OptionFunc) (*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/ce/api/award_emoji.html#award-emoji-on-notes
|
||||
func (s *AwardEmojiService) GetMergeRequestAwardEmojiOnNote(pid interface{}, mergeRequestIID, noteID, awardID int, options ...OptionFunc) (*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/ce/api/award_emoji.html#award-emoji-on-notes
|
||||
func (s *AwardEmojiService) GetSnippetAwardEmojiOnNote(pid interface{}, snippetIID, noteID, awardID int, options ...OptionFunc) (*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 ...OptionFunc) (*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",
|
||||
url.QueryEscape(project),
|
||||
ressource,
|
||||
resourceID,
|
||||
noteID,
|
||||
awardID,
|
||||
)
|
||||
|
||||
req, err := s.client.NewRequest("GET", 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, err
|
||||
}
|
||||
|
||||
// CreateIssuesAwardEmojiOnNote gets an award emoji on a note from an issue.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/award_emoji.html#award-emoji-on-notes
|
||||
func (s *AwardEmojiService) CreateIssuesAwardEmojiOnNote(pid interface{}, issueID, noteID int, opt *CreateAwardEmojiOptions, options ...OptionFunc) (*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/ce/api/award_emoji.html#award-emoji-on-notes
|
||||
func (s *AwardEmojiService) CreateMergeRequestAwardEmojiOnNote(pid interface{}, mergeRequestIID, noteID int, opt *CreateAwardEmojiOptions, options ...OptionFunc) (*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/ce/api/award_emoji.html#award-emoji-on-notes
|
||||
func (s *AwardEmojiService) CreateSnippetAwardEmojiOnNote(pid interface{}, snippetIID, noteID int, opt *CreateAwardEmojiOptions, options ...OptionFunc) (*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/ce/api/award_emoji.html#award-a-new-emoji-on-a-note
|
||||
func (s *AwardEmojiService) createAwardEmojiOnNote(pid interface{}, resource string, resourceID, noteID int, opt *CreateAwardEmojiOptions, options ...OptionFunc) (*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",
|
||||
url.QueryEscape(project),
|
||||
resource,
|
||||
resourceID,
|
||||
noteID,
|
||||
)
|
||||
|
||||
req, err := s.client.NewRequest("POST", 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, err
|
||||
}
|
||||
|
||||
// DeleteIssuesAwardEmojiOnNote deletes an award emoji on a note from an issue.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/award_emoji.html#award-emoji-on-notes
|
||||
func (s *AwardEmojiService) DeleteIssuesAwardEmojiOnNote(pid interface{}, issueID, noteID, awardID int, options ...OptionFunc) (*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/ce/api/award_emoji.html#award-emoji-on-notes
|
||||
func (s *AwardEmojiService) DeleteMergeRequestAwardEmojiOnNote(pid interface{}, mergeRequestIID, noteID, awardID int, options ...OptionFunc) (*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/ce/api/award_emoji.html#award-emoji-on-notes
|
||||
func (s *AwardEmojiService) DeleteSnippetAwardEmojiOnNote(pid interface{}, snippetIID, noteID, awardID int, options ...OptionFunc) (*Response, error) {
|
||||
return s.deleteAwardEmojiOnNote(pid, awardSnippets, snippetIID, noteID, awardID, options...)
|
||||
}
|
||||
|
||||
func (s *AwardEmojiService) deleteAwardEmojiOnNote(pid interface{}, resource string, resourceID, noteID, awardID int, options ...OptionFunc) (*Response, error) {
|
||||
project, err := parseID(pid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
u := fmt.Sprintf("projects/%s/%s/%d/notes/%d/award_emoji/%d",
|
||||
url.QueryEscape(project),
|
||||
resource,
|
||||
resourceID,
|
||||
noteID,
|
||||
awardID,
|
||||
)
|
||||
|
||||
req, err := s.client.NewRequest("DELETE", u, nil, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s.client.Do(req, nil)
|
||||
}
|
||||
261
vendor/github.com/xanzy/go-gitlab/boards.go
generated
vendored
Normal file
261
vendor/github.com/xanzy/go-gitlab/boards.go
generated
vendored
Normal file
@@ -0,0 +1,261 @@
|
||||
//
|
||||
// Copyright 2015, 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/url"
|
||||
)
|
||||
|
||||
// IssueBoardsService handles communication with the issue board related
|
||||
// methods of the GitLab API.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/api/boards.html
|
||||
type IssueBoardsService struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// IssueBoard represents a GitLab issue board.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/api/boards.html
|
||||
type IssueBoard struct {
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Project *Project `json:"project"`
|
||||
Milestone *Milestone `json:"milestone"`
|
||||
Lists []*BoardList `json:"lists"`
|
||||
}
|
||||
|
||||
func (b IssueBoard) String() string {
|
||||
return Stringify(b)
|
||||
}
|
||||
|
||||
// BoardList represents a GitLab board list.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/api/boards.html
|
||||
type BoardList struct {
|
||||
ID int `json:"id"`
|
||||
Labels []*Label `json:"labels"`
|
||||
Position int `json:"position"`
|
||||
}
|
||||
|
||||
func (b BoardList) String() string {
|
||||
return Stringify(b)
|
||||
}
|
||||
|
||||
// ListIssueBoardsOptions represents the available ListIssueBoards() options.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/api/boards.html#project-board
|
||||
type ListIssueBoardsOptions ListOptions
|
||||
|
||||
// ListIssueBoards gets a list of all issue boards in a project.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/api/boards.html#project-board
|
||||
func (s *IssueBoardsService) ListIssueBoards(pid interface{}, opt *ListIssueBoardsOptions, options ...OptionFunc) ([]*IssueBoard, *Response, error) {
|
||||
project, err := parseID(pid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("projects/%s/boards", url.QueryEscape(project))
|
||||
|
||||
req, err := s.client.NewRequest("GET", 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, err
|
||||
}
|
||||
|
||||
// GetIssueBoard gets a single issue board of a project.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/api/boards.html#single-board
|
||||
func (s *IssueBoardsService) GetIssueBoard(pid interface{}, board int, options ...OptionFunc) (*IssueBoard, *Response, error) {
|
||||
project, err := parseID(pid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("projects/%s/boards/%d", url.QueryEscape(project), board)
|
||||
|
||||
req, err := s.client.NewRequest("GET", 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, err
|
||||
}
|
||||
|
||||
// GetIssueBoardListsOptions represents the available GetIssueBoardLists() options.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/api/boards.html#list-board-lists
|
||||
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/ce/api/boards.html#list-board-lists
|
||||
func (s *IssueBoardsService) GetIssueBoardLists(pid interface{}, board int, opt *GetIssueBoardListsOptions, options ...OptionFunc) ([]*BoardList, *Response, error) {
|
||||
project, err := parseID(pid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("projects/%s/boards/%d/lists", url.QueryEscape(project), board)
|
||||
|
||||
req, err := s.client.NewRequest("GET", 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, err
|
||||
}
|
||||
|
||||
// GetIssueBoardList gets a single issue board list.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/api/boards.html#single-board-list
|
||||
func (s *IssueBoardsService) GetIssueBoardList(pid interface{}, board, list int, options ...OptionFunc) (*BoardList, *Response, error) {
|
||||
project, err := parseID(pid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("projects/%s/boards/%d/lists/%d",
|
||||
url.QueryEscape(project),
|
||||
board,
|
||||
list,
|
||||
)
|
||||
|
||||
req, err := s.client.NewRequest("GET", 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, err
|
||||
}
|
||||
|
||||
// CreateIssueBoardListOptions represents the available CreateIssueBoardList()
|
||||
// options.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/api/boards.html#new-board-list
|
||||
type CreateIssueBoardListOptions struct {
|
||||
LabelID *int `url:"label_id" json:"label_id"`
|
||||
}
|
||||
|
||||
// CreateIssueBoardList creates a new issue board list.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/api/boards.html#new-board-list
|
||||
func (s *IssueBoardsService) CreateIssueBoardList(pid interface{}, board int, opt *CreateIssueBoardListOptions, options ...OptionFunc) (*BoardList, *Response, error) {
|
||||
project, err := parseID(pid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("projects/%s/boards/%d/lists", url.QueryEscape(project), board)
|
||||
|
||||
req, err := s.client.NewRequest("POST", 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, err
|
||||
}
|
||||
|
||||
// UpdateIssueBoardListOptions represents the available UpdateIssueBoardList()
|
||||
// options.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/api/boards.html#edit-board-list
|
||||
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/ce/api/boards.html#edit-board-list
|
||||
func (s *IssueBoardsService) UpdateIssueBoardList(pid interface{}, board, list int, opt *UpdateIssueBoardListOptions, options ...OptionFunc) (*BoardList, *Response, error) {
|
||||
project, err := parseID(pid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("projects/%s/boards/%d/lists/%d",
|
||||
url.QueryEscape(project),
|
||||
board,
|
||||
list,
|
||||
)
|
||||
|
||||
req, err := s.client.NewRequest("PUT", 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, err
|
||||
}
|
||||
|
||||
// DeleteIssueBoardList soft deletes an issue board list. Only for admins and
|
||||
// project owners.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/boards.html#delete-a-board-list
|
||||
func (s *IssueBoardsService) DeleteIssueBoardList(pid interface{}, board, list int, options ...OptionFunc) (*Response, error) {
|
||||
project, err := parseID(pid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
u := fmt.Sprintf("projects/%s/boards/%d/lists/%d",
|
||||
url.QueryEscape(project),
|
||||
board,
|
||||
list,
|
||||
)
|
||||
|
||||
req, err := s.client.NewRequest("DELETE", u, nil, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s.client.Do(req, nil)
|
||||
}
|
||||
238
vendor/github.com/xanzy/go-gitlab/branches.go
generated
vendored
Normal file
238
vendor/github.com/xanzy/go-gitlab/branches.go
generated
vendored
Normal file
@@ -0,0 +1,238 @@
|
||||
//
|
||||
// Copyright 2017, 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/url"
|
||||
)
|
||||
|
||||
// BranchesService handles communication with the branch related methods
|
||||
// of the GitLab API.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/api/branches.html
|
||||
type BranchesService struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// Branch represents a GitLab branch.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/api/branches.html
|
||||
type Branch struct {
|
||||
Commit *Commit `json:"commit"`
|
||||
Name string `json:"name"`
|
||||
Protected bool `json:"protected"`
|
||||
Merged bool `json:"merged"`
|
||||
DevelopersCanPush bool `json:"developers_can_push"`
|
||||
DevelopersCanMerge bool `json:"developers_can_merge"`
|
||||
}
|
||||
|
||||
func (b Branch) String() string {
|
||||
return Stringify(b)
|
||||
}
|
||||
|
||||
// ListBranchesOptions represents the available ListBranches() options.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/branches.html#list-repository-branches
|
||||
type ListBranchesOptions ListOptions
|
||||
|
||||
// ListBranches gets a list of repository branches from a project, sorted by
|
||||
// name alphabetically.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/branches.html#list-repository-branches
|
||||
func (s *BranchesService) ListBranches(pid interface{}, opts *ListBranchesOptions, options ...OptionFunc) ([]*Branch, *Response, error) {
|
||||
project, err := parseID(pid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("projects/%s/repository/branches", url.QueryEscape(project))
|
||||
|
||||
req, err := s.client.NewRequest("GET", 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, err
|
||||
}
|
||||
|
||||
// GetBranch gets a single project repository branch.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/branches.html#get-single-repository-branch
|
||||
func (s *BranchesService) GetBranch(pid interface{}, branch string, options ...OptionFunc) (*Branch, *Response, error) {
|
||||
project, err := parseID(pid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("projects/%s/repository/branches/%s", url.QueryEscape(project), url.QueryEscape(branch))
|
||||
|
||||
req, err := s.client.NewRequest("GET", 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, err
|
||||
}
|
||||
|
||||
// ProtectBranchOptions represents the available ProtectBranch() options.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/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.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/branches.html#protect-repository-branch
|
||||
func (s *BranchesService) ProtectBranch(pid interface{}, branch string, opts *ProtectBranchOptions, options ...OptionFunc) (*Branch, *Response, error) {
|
||||
project, err := parseID(pid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("projects/%s/repository/branches/%s/protect", url.QueryEscape(project), url.QueryEscape(branch))
|
||||
|
||||
req, err := s.client.NewRequest("PUT", 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, err
|
||||
}
|
||||
|
||||
// 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.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/branches.html#unprotect-repository-branch
|
||||
func (s *BranchesService) UnprotectBranch(pid interface{}, branch string, options ...OptionFunc) (*Branch, *Response, error) {
|
||||
project, err := parseID(pid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("projects/%s/repository/branches/%s/unprotect", url.QueryEscape(project), url.QueryEscape(branch))
|
||||
|
||||
req, err := s.client.NewRequest("PUT", 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, err
|
||||
}
|
||||
|
||||
// CreateBranchOptions represents the available CreateBranch() options.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/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/ce/api/branches.html#create-repository-branch
|
||||
func (s *BranchesService) CreateBranch(pid interface{}, opt *CreateBranchOptions, options ...OptionFunc) (*Branch, *Response, error) {
|
||||
project, err := parseID(pid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("projects/%s/repository/branches", url.QueryEscape(project))
|
||||
|
||||
req, err := s.client.NewRequest("POST", 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, err
|
||||
}
|
||||
|
||||
// DeleteBranch deletes an existing branch.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/branches.html#delete-repository-branch
|
||||
func (s *BranchesService) DeleteBranch(pid interface{}, branch string, options ...OptionFunc) (*Response, error) {
|
||||
project, err := parseID(pid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
u := fmt.Sprintf("projects/%s/repository/branches/%s", url.QueryEscape(project), url.QueryEscape(branch))
|
||||
|
||||
req, err := s.client.NewRequest("DELETE", 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/ce/api/branches.html#delete-merged-branches
|
||||
func (s *BranchesService) DeleteMergedBranches(pid interface{}, options ...OptionFunc) (*Response, error) {
|
||||
project, err := parseID(pid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
u := fmt.Sprintf("projects/%s/repository/merged_branches", url.QueryEscape(project))
|
||||
|
||||
req, err := s.client.NewRequest("DELETE", u, nil, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s.client.Do(req, nil)
|
||||
}
|
||||
172
vendor/github.com/xanzy/go-gitlab/broadcast_messages.go
generated
vendored
Normal file
172
vendor/github.com/xanzy/go-gitlab/broadcast_messages.go
generated
vendored
Normal file
@@ -0,0 +1,172 @@
|
||||
//
|
||||
// Copyright 2018, 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"
|
||||
"time"
|
||||
)
|
||||
|
||||
// BroadcastMessagesService handles communication with the broadcast
|
||||
// messages methods of the GitLab API.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/api/broadcast_messages.html
|
||||
type BroadcastMessagesService struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// BroadcastMessage represents a GitLab issue board.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/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"`
|
||||
Color string `json:"color"`
|
||||
Font string `json:"font"`
|
||||
ID int `json:"id"`
|
||||
Active bool `json:"active"`
|
||||
}
|
||||
|
||||
// ListBroadcastMessagesOptions represents the available ListBroadcastMessages()
|
||||
// options.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/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/ce/api/broadcast_messages.html#get-all-broadcast-messages
|
||||
func (s *BroadcastMessagesService) ListBroadcastMessages(opt *ListBroadcastMessagesOptions, options ...OptionFunc) ([]*BroadcastMessage, *Response, error) {
|
||||
req, err := s.client.NewRequest("GET", "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, err
|
||||
}
|
||||
|
||||
// GetBroadcastMessage gets a single broadcast message.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/broadcast_messages.html#get-a-specific-broadcast-message
|
||||
func (s *BroadcastMessagesService) GetBroadcastMessage(broadcast int, options ...OptionFunc) (*BroadcastMessage, *Response, error) {
|
||||
u := fmt.Sprintf("broadcast_messages/%d", broadcast)
|
||||
|
||||
req, err := s.client.NewRequest("GET", 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, err
|
||||
}
|
||||
|
||||
// CreateBroadcastMessageOptions represents the available CreateBroadcastMessage()
|
||||
// options.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/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"`
|
||||
Color *string `url:"color,omitempty" json:"color,omitempty"`
|
||||
Font *string `url:"font,omitempty" json:"font,omitempty"`
|
||||
}
|
||||
|
||||
// CreateBroadcastMessage creates a message to broadcast.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/broadcast_messages.html#create-a-broadcast-message
|
||||
func (s *BroadcastMessagesService) CreateBroadcastMessage(opt *CreateBroadcastMessageOptions, options ...OptionFunc) (*BroadcastMessage, *Response, error) {
|
||||
req, err := s.client.NewRequest("POST", "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, err
|
||||
}
|
||||
|
||||
// UpdateBroadcastMessageOptions represents the available CreateBroadcastMessage()
|
||||
// options.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/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"`
|
||||
Color *string `url:"color,omitempty" json:"color,omitempty"`
|
||||
Font *string `url:"font,omitempty" json:"font,omitempty"`
|
||||
}
|
||||
|
||||
// UpdateBroadcastMessage update a broadcasted message.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/broadcast_messages.html#update-a-broadcast-message
|
||||
func (s *BroadcastMessagesService) UpdateBroadcastMessage(broadcast int, opt *UpdateBroadcastMessageOptions, options ...OptionFunc) (*BroadcastMessage, *Response, error) {
|
||||
u := fmt.Sprintf("broadcast_messages/%d", broadcast)
|
||||
|
||||
req, err := s.client.NewRequest("PUT", 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, err
|
||||
}
|
||||
|
||||
// DeleteBroadcastMessage deletes a broadcasted message.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/broadcast_messages.html#delete-a-broadcast-message
|
||||
func (s *BroadcastMessagesService) DeleteBroadcastMessage(broadcast int, options ...OptionFunc) (*Response, error) {
|
||||
u := fmt.Sprintf("broadcast_messages/%d", broadcast)
|
||||
|
||||
req, err := s.client.NewRequest("DELETE", u, nil, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s.client.Do(req, nil)
|
||||
}
|
||||
173
vendor/github.com/xanzy/go-gitlab/build_variables.go
generated
vendored
Normal file
173
vendor/github.com/xanzy/go-gitlab/build_variables.go
generated
vendored
Normal file
@@ -0,0 +1,173 @@
|
||||
package gitlab
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
// BuildVariablesService handles communication with the project variables related methods
|
||||
// of the Gitlab API
|
||||
//
|
||||
// Gitlab API Docs : https://docs.gitlab.com/ce/api/build_variables.html
|
||||
type BuildVariablesService struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// BuildVariable represents a variable available for each build of the given project
|
||||
//
|
||||
// Gitlab API Docs : https://docs.gitlab.com/ce/api/build_variables.html
|
||||
type BuildVariable struct {
|
||||
Key string `json:"key"`
|
||||
Value string `json:"value"`
|
||||
Protected bool `json:"protected"`
|
||||
}
|
||||
|
||||
func (v BuildVariable) String() string {
|
||||
return Stringify(v)
|
||||
}
|
||||
|
||||
// ListBuildVariablesOptions are the parameters to ListBuildVariables()
|
||||
//
|
||||
// Gitlab API Docs:
|
||||
// https://docs.gitlab.com/ce/api/build_variables.html#list-project-variables
|
||||
type ListBuildVariablesOptions ListOptions
|
||||
|
||||
// ListBuildVariables gets the a list of project variables in a project
|
||||
//
|
||||
// Gitlab API Docs:
|
||||
// https://docs.gitlab.com/ce/api/build_variables.html#list-project-variables
|
||||
func (s *BuildVariablesService) ListBuildVariables(pid interface{}, opts *ListBuildVariablesOptions, options ...OptionFunc) ([]*BuildVariable, *Response, error) {
|
||||
project, err := parseID(pid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("projects/%s/variables", url.QueryEscape(project))
|
||||
|
||||
req, err := s.client.NewRequest("GET", u, opts, options)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var v []*BuildVariable
|
||||
resp, err := s.client.Do(req, &v)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
|
||||
return v, resp, err
|
||||
}
|
||||
|
||||
// GetBuildVariable gets a single project variable of a project
|
||||
//
|
||||
// Gitlab API Docs:
|
||||
// https://docs.gitlab.com/ce/api/build_variables.html#show-variable-details
|
||||
func (s *BuildVariablesService) GetBuildVariable(pid interface{}, key string, options ...OptionFunc) (*BuildVariable, *Response, error) {
|
||||
project, err := parseID(pid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("projects/%s/variables/%s", url.QueryEscape(project), key)
|
||||
|
||||
req, err := s.client.NewRequest("GET", u, nil, options)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
v := new(BuildVariable)
|
||||
resp, err := s.client.Do(req, v)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
|
||||
return v, resp, err
|
||||
}
|
||||
|
||||
// CreateBuildVariableOptions are the parameters to CreateBuildVariable()
|
||||
//
|
||||
// Gitlab API Docs:
|
||||
// https://docs.gitlab.com/ce/api/build_variables.html#create-variable
|
||||
type CreateBuildVariableOptions struct {
|
||||
Key *string `url:"key" json:"key"`
|
||||
Value *string `url:"value" json:"value"`
|
||||
Protected *bool `url:"protected,omitempty" json:"protected,omitempty"`
|
||||
}
|
||||
|
||||
// CreateBuildVariable creates a variable for a given project
|
||||
//
|
||||
// Gitlab API Docs:
|
||||
// https://docs.gitlab.com/ce/api/build_variables.html#create-variable
|
||||
func (s *BuildVariablesService) CreateBuildVariable(pid interface{}, opt *CreateBuildVariableOptions, options ...OptionFunc) (*BuildVariable, *Response, error) {
|
||||
project, err := parseID(pid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("projects/%s/variables", url.QueryEscape(project))
|
||||
|
||||
req, err := s.client.NewRequest("POST", u, opt, options)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
v := new(BuildVariable)
|
||||
resp, err := s.client.Do(req, v)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
|
||||
return v, resp, err
|
||||
}
|
||||
|
||||
// UpdateBuildVariableOptions are the parameters to UpdateBuildVariable()
|
||||
//
|
||||
// Gitlab API Docs:
|
||||
// https://docs.gitlab.com/ce/api/build_variables.html#update-variable
|
||||
type UpdateBuildVariableOptions struct {
|
||||
Key *string `url:"key" json:"key"`
|
||||
Value *string `url:"value" json:"value"`
|
||||
Protected *bool `url:"protected,omitempty" json:"protected,omitempty"`
|
||||
}
|
||||
|
||||
// UpdateBuildVariable updates an existing project variable
|
||||
// The variable key must exist
|
||||
//
|
||||
// Gitlab API Docs:
|
||||
// https://docs.gitlab.com/ce/api/build_variables.html#update-variable
|
||||
func (s *BuildVariablesService) UpdateBuildVariable(pid interface{}, key string, opt *UpdateBuildVariableOptions, options ...OptionFunc) (*BuildVariable, *Response, error) {
|
||||
project, err := parseID(pid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("projects/%s/variables/%s", url.QueryEscape(project), key)
|
||||
|
||||
req, err := s.client.NewRequest("PUT", u, opt, options)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
v := new(BuildVariable)
|
||||
resp, err := s.client.Do(req, v)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
|
||||
return v, resp, err
|
||||
}
|
||||
|
||||
// RemoveBuildVariable removes a project variable of a given project identified by its key
|
||||
//
|
||||
// Gitlab API Docs:
|
||||
// https://docs.gitlab.com/ce/api/build_variables.html#remove-variable
|
||||
func (s *BuildVariablesService) RemoveBuildVariable(pid interface{}, key string, options ...OptionFunc) (*Response, error) {
|
||||
project, err := parseID(pid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
u := fmt.Sprintf("projects/%s/variables/%s", url.QueryEscape(project), key)
|
||||
|
||||
req, err := s.client.NewRequest("DELETE", u, nil, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s.client.Do(req, nil)
|
||||
}
|
||||
448
vendor/github.com/xanzy/go-gitlab/commits.go
generated
vendored
Normal file
448
vendor/github.com/xanzy/go-gitlab/commits.go
generated
vendored
Normal file
@@ -0,0 +1,448 @@
|
||||
//
|
||||
// Copyright 2017, 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/url"
|
||||
"time"
|
||||
)
|
||||
|
||||
// CommitsService handles communication with the commit related methods
|
||||
// of the GitLab API.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html
|
||||
type CommitsService struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// Commit represents a GitLab commit.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/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"`
|
||||
}
|
||||
|
||||
// CommitStats represents the number of added and deleted files in a commit.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/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/ce/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"`
|
||||
All *bool `url:"all,omitempty" json:"all,omitempty"`
|
||||
}
|
||||
|
||||
// ListCommits gets a list of repository commits in a project.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#list-commits
|
||||
func (s *CommitsService) ListCommits(pid interface{}, opt *ListCommitsOptions, options ...OptionFunc) ([]*Commit, *Response, error) {
|
||||
project, err := parseID(pid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("projects/%s/repository/commits", url.QueryEscape(project))
|
||||
|
||||
req, err := s.client.NewRequest("GET", 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, err
|
||||
}
|
||||
|
||||
// FileAction represents the available actions that can be performed on a file.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#create-a-commit-with-multiple-files-and-actions
|
||||
type FileAction string
|
||||
|
||||
// The available file actions.
|
||||
const (
|
||||
FileCreate FileAction = "create"
|
||||
FileDelete FileAction = "delete"
|
||||
FileMove FileAction = "move"
|
||||
FileUpdate FileAction = "update"
|
||||
)
|
||||
|
||||
// CommitAction represents a single file action within a commit.
|
||||
type CommitAction struct {
|
||||
Action FileAction `url:"action" json:"action"`
|
||||
FilePath string `url:"file_path" json:"file_path"`
|
||||
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"`
|
||||
}
|
||||
|
||||
// GetCommit gets a specific commit identified by the commit hash or name of a
|
||||
// branch or tag.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#get-a-single-commit
|
||||
func (s *CommitsService) GetCommit(pid interface{}, sha string, options ...OptionFunc) (*Commit, *Response, error) {
|
||||
project, err := parseID(pid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("projects/%s/repository/commits/%s", url.QueryEscape(project), sha)
|
||||
|
||||
req, err := s.client.NewRequest("GET", u, nil, 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, err
|
||||
}
|
||||
|
||||
// CreateCommitOptions represents the available options for a new commit.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#create-a-commit-with-multiple-files-and-actions
|
||||
type CreateCommitOptions struct {
|
||||
Branch *string `url:"branch" json:"branch"`
|
||||
CommitMessage *string `url:"commit_message" json:"commit_message"`
|
||||
StartBranch *string `url:"start_branch,omitempty" json:"start_branch,omitempty"`
|
||||
Actions []*CommitAction `url:"actions" json:"actions"`
|
||||
AuthorEmail *string `url:"author_email,omitempty" json:"author_email,omitempty"`
|
||||
AuthorName *string `url:"author_name,omitempty" json:"author_name,omitempty"`
|
||||
}
|
||||
|
||||
// CreateCommit creates a commit with multiple files and actions.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#create-a-commit-with-multiple-files-and-actions
|
||||
func (s *CommitsService) CreateCommit(pid interface{}, opt *CreateCommitOptions, options ...OptionFunc) (*Commit, *Response, error) {
|
||||
project, err := parseID(pid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("projects/%s/repository/commits", url.QueryEscape(project))
|
||||
|
||||
req, err := s.client.NewRequest("POST", 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, err
|
||||
}
|
||||
|
||||
// Diff represents a GitLab diff.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/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/ce/api/commits.html#get-the-diff-of-a-commit
|
||||
type GetCommitDiffOptions ListOptions
|
||||
|
||||
// GetCommitDiff gets the diff of a commit in a project..
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/commits.html#get-the-diff-of-a-commit
|
||||
func (s *CommitsService) GetCommitDiff(pid interface{}, sha string, opt *GetCommitDiffOptions, options ...OptionFunc) ([]*Diff, *Response, error) {
|
||||
project, err := parseID(pid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("projects/%s/repository/commits/%s/diff", url.QueryEscape(project), sha)
|
||||
|
||||
req, err := s.client.NewRequest("GET", 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, err
|
||||
}
|
||||
|
||||
// CommitComment represents a GitLab commit comment.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/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/ce/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/ce/api/commits.html#get-the-comments-of-a-commit
|
||||
func (s *CommitsService) GetCommitComments(pid interface{}, sha string, opt *GetCommitCommentsOptions, options ...OptionFunc) ([]*CommitComment, *Response, error) {
|
||||
project, err := parseID(pid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("projects/%s/repository/commits/%s/comments", url.QueryEscape(project), sha)
|
||||
|
||||
req, err := s.client.NewRequest("GET", 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, err
|
||||
}
|
||||
|
||||
// PostCommitCommentOptions represents the available PostCommitComment()
|
||||
// options.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/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/ce/api/commits.html#post-comment-to-commit
|
||||
func (s *CommitsService) PostCommitComment(pid interface{}, sha string, opt *PostCommitCommentOptions, options ...OptionFunc) (*CommitComment, *Response, error) {
|
||||
project, err := parseID(pid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("projects/%s/repository/commits/%s/comments", url.QueryEscape(project), sha)
|
||||
|
||||
req, err := s.client.NewRequest("POST", 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, err
|
||||
}
|
||||
|
||||
// GetCommitStatusesOptions represents the available GetCommitStatuses() options.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#get-the-status-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/ce/api/commits.html#get-the-status-of-a-commit
|
||||
type CommitStatus struct {
|
||||
ID int `json:"id"`
|
||||
SHA string `json:"sha"`
|
||||
Ref string `json:"ref"`
|
||||
Status string `json:"status"`
|
||||
Name string `json:"name"`
|
||||
TargetURL string `json:"target_url"`
|
||||
Description string `json:"description"`
|
||||
CreatedAt *time.Time `json:"created_at"`
|
||||
StartedAt *time.Time `json:"started_at"`
|
||||
FinishedAt *time.Time `json:"finished_at"`
|
||||
Author Author `json:"author"`
|
||||
}
|
||||
|
||||
// GetCommitStatuses gets the statuses of a commit in a project.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#get-the-status-of-a-commit
|
||||
func (s *CommitsService) GetCommitStatuses(pid interface{}, sha string, opt *GetCommitStatusesOptions, options ...OptionFunc) ([]*CommitStatus, *Response, error) {
|
||||
project, err := parseID(pid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("projects/%s/repository/commits/%s/statuses", url.QueryEscape(project), sha)
|
||||
|
||||
req, err := s.client.NewRequest("GET", 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, err
|
||||
}
|
||||
|
||||
// SetCommitStatusOptions represents the available SetCommitStatus() options.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#post-the-status-to-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"`
|
||||
}
|
||||
|
||||
// SetCommitStatus sets the status of a commit in a project.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#post-the-status-to-commit
|
||||
func (s *CommitsService) SetCommitStatus(pid interface{}, sha string, opt *SetCommitStatusOptions, options ...OptionFunc) (*CommitStatus, *Response, error) {
|
||||
project, err := parseID(pid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("projects/%s/statuses/%s", url.QueryEscape(project), sha)
|
||||
|
||||
req, err := s.client.NewRequest("POST", 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, err
|
||||
}
|
||||
|
||||
// CherryPickCommitOptions represents the available options for cherry-picking a commit.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#cherry-pick-a-commit
|
||||
type CherryPickCommitOptions struct {
|
||||
TargetBranch *string `url:"branch" json:"branch,omitempty"`
|
||||
}
|
||||
|
||||
// CherryPickCommit sherry picks a commit to a given branch.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#cherry-pick-a-commit
|
||||
func (s *CommitsService) CherryPickCommit(pid interface{}, sha string, opt *CherryPickCommitOptions, options ...OptionFunc) (*Commit, *Response, error) {
|
||||
project, err := parseID(pid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("projects/%s/repository/commits/%s/cherry_pick",
|
||||
url.QueryEscape(project), url.QueryEscape(sha))
|
||||
|
||||
req, err := s.client.NewRequest("POST", 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, err
|
||||
}
|
||||
201
vendor/github.com/xanzy/go-gitlab/deploy_keys.go
generated
vendored
Normal file
201
vendor/github.com/xanzy/go-gitlab/deploy_keys.go
generated
vendored
Normal file
@@ -0,0 +1,201 @@
|
||||
//
|
||||
// Copyright 2017, 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/url"
|
||||
"time"
|
||||
)
|
||||
|
||||
// DeployKeysService handles communication with the keys related methods
|
||||
// of the GitLab API.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/api/deploy_keys.html
|
||||
type DeployKeysService struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// DeployKey represents a GitLab deploy key.
|
||||
type DeployKey struct {
|
||||
ID int `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Key string `json:"key"`
|
||||
CanPush *bool `json:"can_push"`
|
||||
CreatedAt *time.Time `json:"created_at"`
|
||||
}
|
||||
|
||||
func (k DeployKey) String() string {
|
||||
return Stringify(k)
|
||||
}
|
||||
|
||||
// ListAllDeployKeys gets a list of all deploy keys
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/deploy_keys.html#list-all-deploy-keys
|
||||
func (s *DeployKeysService) ListAllDeployKeys(options ...OptionFunc) ([]*DeployKey, *Response, error) {
|
||||
req, err := s.client.NewRequest("GET", "deploy_keys", nil, options)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var ks []*DeployKey
|
||||
resp, err := s.client.Do(req, &ks)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
|
||||
return ks, resp, err
|
||||
}
|
||||
|
||||
// ListProjectDeployKeysOptions represents the available ListProjectDeployKeys()
|
||||
// options.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/deploy_keys.html#list-project-deploy-keys
|
||||
type ListProjectDeployKeysOptions ListOptions
|
||||
|
||||
// ListProjectDeployKeys gets a list of a project's deploy keys
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/deploy_keys.html#list-project-deploy-keys
|
||||
func (s *DeployKeysService) ListProjectDeployKeys(pid interface{}, opt *ListProjectDeployKeysOptions, options ...OptionFunc) ([]*DeployKey, *Response, error) {
|
||||
project, err := parseID(pid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("projects/%s/deploy_keys", url.QueryEscape(project))
|
||||
|
||||
req, err := s.client.NewRequest("GET", u, opt, options)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var ks []*DeployKey
|
||||
resp, err := s.client.Do(req, &ks)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
|
||||
return ks, resp, err
|
||||
}
|
||||
|
||||
// GetDeployKey gets a single deploy key.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/deploy_keys.html#single-deploy-key
|
||||
func (s *DeployKeysService) GetDeployKey(pid interface{}, deployKey int, options ...OptionFunc) (*DeployKey, *Response, error) {
|
||||
project, err := parseID(pid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("projects/%s/deploy_keys/%d", url.QueryEscape(project), deployKey)
|
||||
|
||||
req, err := s.client.NewRequest("GET", u, nil, options)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
k := new(DeployKey)
|
||||
resp, err := s.client.Do(req, k)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
|
||||
return k, resp, err
|
||||
}
|
||||
|
||||
// AddDeployKeyOptions represents the available ADDDeployKey() options.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/deploy_keys.html#add-deploy-key
|
||||
type AddDeployKeyOptions struct {
|
||||
Title *string `url:"title,omitempty" json:"title,omitempty"`
|
||||
Key *string `url:"key,omitempty" json:"key,omitempty"`
|
||||
CanPush *bool `url:"can_push,omitempty" json:"can_push,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/ce/api/deploy_keys.html#add-deploy-key
|
||||
func (s *DeployKeysService) AddDeployKey(pid interface{}, opt *AddDeployKeyOptions, options ...OptionFunc) (*DeployKey, *Response, error) {
|
||||
project, err := parseID(pid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("projects/%s/deploy_keys", url.QueryEscape(project))
|
||||
|
||||
req, err := s.client.NewRequest("POST", u, opt, options)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
k := new(DeployKey)
|
||||
resp, err := s.client.Do(req, k)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
|
||||
return k, resp, err
|
||||
}
|
||||
|
||||
// DeleteDeployKey deletes a deploy key from a project.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/deploy_keys.html#delete-deploy-key
|
||||
func (s *DeployKeysService) DeleteDeployKey(pid interface{}, deployKey int, options ...OptionFunc) (*Response, error) {
|
||||
project, err := parseID(pid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
u := fmt.Sprintf("projects/%s/deploy_keys/%d", url.QueryEscape(project), deployKey)
|
||||
|
||||
req, err := s.client.NewRequest("DELETE", 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/ce/api/deploy_keys.html#enable-deploy-key
|
||||
func (s *DeployKeysService) EnableDeployKey(pid interface{}, deployKey int, options ...OptionFunc) (*DeployKey, *Response, error) {
|
||||
project, err := parseID(pid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("projects/%s/deploy_keys/%d/enable", url.QueryEscape(project), deployKey)
|
||||
|
||||
req, err := s.client.NewRequest("POST", u, nil, options)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
k := new(DeployKey)
|
||||
resp, err := s.client.Do(req, k)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
|
||||
return k, resp, err
|
||||
}
|
||||
121
vendor/github.com/xanzy/go-gitlab/deployments.go
generated
vendored
Normal file
121
vendor/github.com/xanzy/go-gitlab/deployments.go
generated
vendored
Normal file
@@ -0,0 +1,121 @@
|
||||
//
|
||||
// Copyright 2018, 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/url"
|
||||
"time"
|
||||
)
|
||||
|
||||
// DeploymentsService handles communication with the deployment related methods
|
||||
// of the GitLab API.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/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"`
|
||||
CreatedAt *time.Time `json:"created_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"`
|
||||
} `json:"pipeline"`
|
||||
Runner *Runner `json:"runner"`
|
||||
} `json:"deployable"`
|
||||
}
|
||||
|
||||
// ListProjectDeploymentsOptions represents the available ListProjectDeployments() options.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/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"`
|
||||
}
|
||||
|
||||
// ListProjectDeployments gets a list of deployments in a project.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/api/deployments.html#list-project-deployments
|
||||
func (s *DeploymentsService) ListProjectDeployments(pid interface{}, opts *ListProjectDeploymentsOptions, options ...OptionFunc) ([]*Deployment, *Response, error) {
|
||||
project, err := parseID(pid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("projects/%s/deployments", url.QueryEscape(project))
|
||||
|
||||
req, err := s.client.NewRequest("GET", 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, err
|
||||
}
|
||||
|
||||
// GetProjectDeployment get a deployment for a project.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/api/deployments.html#get-a-specific-deployment
|
||||
func (s *DeploymentsService) GetProjectDeployment(pid interface{}, deployment int, options ...OptionFunc) (*Deployment, *Response, error) {
|
||||
project, err := parseID(pid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("projects/%s/deployments/%d", url.QueryEscape(project), deployment)
|
||||
|
||||
req, err := s.client.NewRequest("GET", 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, err
|
||||
}
|
||||
166
vendor/github.com/xanzy/go-gitlab/environments.go
generated
vendored
Normal file
166
vendor/github.com/xanzy/go-gitlab/environments.go
generated
vendored
Normal file
@@ -0,0 +1,166 @@
|
||||
//
|
||||
// Copyright 2017, 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/url"
|
||||
)
|
||||
|
||||
// EnvironmentsService handles communication with the environment related methods
|
||||
// of the GitLab API.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/api/environments.html
|
||||
type EnvironmentsService struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// Environment represents a GitLab environment.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/api/environments.html
|
||||
type Environment struct {
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Slug string `json:"slug"`
|
||||
ExternalURL string `json:"external_url"`
|
||||
}
|
||||
|
||||
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 ListOptions
|
||||
|
||||
// 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 ...OptionFunc) ([]*Environment, *Response, error) {
|
||||
project, err := parseID(pid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("projects/%s/environments", url.QueryEscape(project))
|
||||
|
||||
req, err := s.client.NewRequest("GET", 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, err
|
||||
}
|
||||
|
||||
// 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"`
|
||||
ExternalURL *string `url:"external_url,omitempty" json:"external_url,omitempty"`
|
||||
}
|
||||
|
||||
// CreateEnvironment adds a 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 ...OptionFunc) (*Environment, *Response, error) {
|
||||
project, err := parseID(pid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("projects/%s/environments", url.QueryEscape(project))
|
||||
|
||||
req, err := s.client.NewRequest("POST", 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, err
|
||||
}
|
||||
|
||||
// EditEnvironmentOptions represents the available EditEnvironment() options.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ee/api/environments.html#edit-an-existing-environment
|
||||
type EditEnvironmentOptions struct {
|
||||
Name *string `url:"name,omitempty" json:"name,omitempty"`
|
||||
ExternalURL *string `url:"external_url,omitempty" json:"external_url,omitempty"`
|
||||
}
|
||||
|
||||
// EditEnvironment updates a project team environment to a specified access level..
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ee/api/environments.html#edit-an-existing-environment
|
||||
func (s *EnvironmentsService) EditEnvironment(pid interface{}, environment int, opt *EditEnvironmentOptions, options ...OptionFunc) (*Environment, *Response, error) {
|
||||
project, err := parseID(pid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("projects/%s/environments/%d", url.QueryEscape(project), environment)
|
||||
|
||||
req, err := s.client.NewRequest("PUT", 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, err
|
||||
}
|
||||
|
||||
// DeleteEnvironment removes a environment from a project team.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/environments.html#remove-a-environment-from-a-group-or-project
|
||||
func (s *EnvironmentsService) DeleteEnvironment(pid interface{}, environment int, options ...OptionFunc) (*Response, error) {
|
||||
project, err := parseID(pid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
u := fmt.Sprintf("projects/%s/environments/%d", url.QueryEscape(project), environment)
|
||||
|
||||
req, err := s.client.NewRequest("DELETE", u, nil, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s.client.Do(req, nil)
|
||||
}
|
||||
649
vendor/github.com/xanzy/go-gitlab/event_types.go
generated
vendored
Normal file
649
vendor/github.com/xanzy/go-gitlab/event_types.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
147
vendor/github.com/xanzy/go-gitlab/events.go
generated
vendored
Normal file
147
vendor/github.com/xanzy/go-gitlab/events.go
generated
vendored
Normal file
@@ -0,0 +1,147 @@
|
||||
//
|
||||
// Copyright 2017, 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/url"
|
||||
"time"
|
||||
)
|
||||
|
||||
// EventsService handles communication with the event related methods of
|
||||
// the GitLab API.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/api/events.html
|
||||
type EventsService struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// ContributionEvent represents a user's contribution
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/events.html#get-user-contribution-events
|
||||
type ContributionEvent struct {
|
||||
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/ce/api/events.html#get-user-contribution-events
|
||||
type ListContributionEventsOptions struct {
|
||||
ListOptions
|
||||
Action *EventTypeValue `json:"action,omitempty"`
|
||||
TargetType *EventTargetTypeValue `json:"target_type,omitempty"`
|
||||
Before *ISOTime `json:"before,omitempty"`
|
||||
After *ISOTime `json:"after,omitempty"`
|
||||
Sort *string `json:"sort,omitempty"`
|
||||
}
|
||||
|
||||
// ListUserContributionEvents retrieves user contribution events
|
||||
// for the specified user, sorted from newest to oldest.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/events.html#get-user-contribution-events
|
||||
func (s *UsersService) ListUserContributionEvents(uid interface{}, opt *ListContributionEventsOptions, options ...OptionFunc) ([]*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("GET", 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, err
|
||||
}
|
||||
|
||||
// ListCurrentUserContributionEvents gets a list currently authenticated user's events
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/api/events.html#list-currently-authenticated-user-39-s-events
|
||||
func (s *EventsService) ListCurrentUserContributionEvents(opt *ListContributionEventsOptions, options ...OptionFunc) ([]*ContributionEvent, *Response, error) {
|
||||
req, err := s.client.NewRequest("GET", "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, err
|
||||
}
|
||||
|
||||
// ListProjectContributionEvents gets a list currently authenticated user's events
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/api/events.html#list-a-project-39-s-visible-events
|
||||
func (s *EventsService) ListProjectContributionEvents(pid interface{}, opt *ListContributionEventsOptions, options ...OptionFunc) ([]*ContributionEvent, *Response, error) {
|
||||
project, err := parseID(pid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("%s/events", url.QueryEscape(project))
|
||||
|
||||
req, err := s.client.NewRequest("GET", 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, err
|
||||
}
|
||||
79
vendor/github.com/xanzy/go-gitlab/feature_flags.go
generated
vendored
Normal file
79
vendor/github.com/xanzy/go-gitlab/feature_flags.go
generated
vendored
Normal file
@@ -0,0 +1,79 @@
|
||||
package gitlab
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
// FeaturesService handles the communication with the application FeaturesService
|
||||
// related methods of the GitLab API.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/api/features.html
|
||||
type FeaturesService struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// Feature represents a GitLab feature flag.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/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/ce/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/ce/api/features.html#list-all-features
|
||||
func (s *FeaturesService) ListFeatures(options ...OptionFunc) ([]*Feature, *Response, error) {
|
||||
req, err := s.client.NewRequest("GET", "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, err
|
||||
}
|
||||
|
||||
// SetFeatureFlag sets or creates a feature flag gate
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/features.html#set-or-create-a-feature
|
||||
func (s *FeaturesService) SetFeatureFlag(name string, value interface{}, options ...OptionFunc) (*Feature, *Response, error) {
|
||||
u := fmt.Sprintf("features/%s", url.QueryEscape(name))
|
||||
|
||||
opt := struct {
|
||||
Value interface{} `url:"value" json:"value"`
|
||||
}{
|
||||
value,
|
||||
}
|
||||
|
||||
req, err := s.client.NewRequest("POST", 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, err
|
||||
}
|
||||
84
vendor/github.com/xanzy/go-gitlab/gitignore_templates.go
generated
vendored
Normal file
84
vendor/github.com/xanzy/go-gitlab/gitignore_templates.go
generated
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
//
|
||||
// Copyright 2018, 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/url"
|
||||
)
|
||||
|
||||
// GitIgnoreTemplatesService handles communication with the gitignore
|
||||
// templates related methods of the GitLab API.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/api/templates/gitignores.html
|
||||
type GitIgnoreTemplatesService struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// GitIgnoreTemplate represents a GitLab gitignore template.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/api/templates/gitignores.html
|
||||
type GitIgnoreTemplate struct {
|
||||
Name string `json:"name"`
|
||||
Content string `json:"content"`
|
||||
}
|
||||
|
||||
// ListTemplatesOptions represents the available ListAllTemplates() options.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/templates/gitignores.html#list-gitignore-templates
|
||||
type ListTemplatesOptions ListOptions
|
||||
|
||||
// ListTemplates get a list of available git ignore templates
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/templates/gitignores.html#list-gitignore-templates
|
||||
func (s *GitIgnoreTemplatesService) ListTemplates(opt *ListTemplatesOptions, options ...OptionFunc) ([]*GitIgnoreTemplate, *Response, error) {
|
||||
req, err := s.client.NewRequest("GET", "templates/gitignores", opt, options)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var gs []*GitIgnoreTemplate
|
||||
resp, err := s.client.Do(req, &gs)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
|
||||
return gs, resp, err
|
||||
}
|
||||
|
||||
// GetTemplate get a git ignore template
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/templates/gitignores.html#single-gitignore-template
|
||||
func (s *GitIgnoreTemplatesService) GetTemplate(key string, options ...OptionFunc) (*GitIgnoreTemplate, *Response, error) {
|
||||
u := fmt.Sprintf("templates/gitignores/%s", url.QueryEscape(key))
|
||||
|
||||
req, err := s.client.NewRequest("GET", 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, err
|
||||
}
|
||||
836
vendor/github.com/xanzy/go-gitlab/gitlab.go
generated
vendored
Normal file
836
vendor/github.com/xanzy/go-gitlab/gitlab.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
195
vendor/github.com/xanzy/go-gitlab/group_members.go
generated
vendored
Normal file
195
vendor/github.com/xanzy/go-gitlab/group_members.go
generated
vendored
Normal file
@@ -0,0 +1,195 @@
|
||||
//
|
||||
// Copyright 2017, 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/url"
|
||||
"time"
|
||||
)
|
||||
|
||||
// GroupMembersService handles communication with the group members
|
||||
// related methods of the GitLab API.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/api/members.html
|
||||
type GroupMembersService struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// GroupMember represents a GitLab group member.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/api/members.html
|
||||
type GroupMember 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"`
|
||||
AccessLevel AccessLevelValue `json:"access_level"`
|
||||
ExpiresAt *ISOTime `json:"expires_at"`
|
||||
}
|
||||
|
||||
// ListGroupMembersOptions represents the available ListGroupMembers()
|
||||
// options.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/members.html#list-all-members-of-a-group-or-project
|
||||
type ListGroupMembersOptions struct {
|
||||
ListOptions
|
||||
Query *string `url:"query,omitempty" json:"query,omitempty"`
|
||||
}
|
||||
|
||||
// ListGroupMembers get a list of group members viewable by the authenticated
|
||||
// user.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/members.html#list-all-members-of-a-group-or-project
|
||||
func (s *GroupsService) ListGroupMembers(gid interface{}, opt *ListGroupMembersOptions, options ...OptionFunc) ([]*GroupMember, *Response, error) {
|
||||
group, err := parseID(gid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("groups/%s/members", url.QueryEscape(group))
|
||||
|
||||
req, err := s.client.NewRequest("GET", 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, err
|
||||
}
|
||||
|
||||
// AddGroupMemberOptions represents the available AddGroupMember() options.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/members.html#add-a-member-to-a-group-or-project
|
||||
type AddGroupMemberOptions struct {
|
||||
UserID *int `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"`
|
||||
}
|
||||
|
||||
// GetGroupMember gets a member of a group.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/members.html#get-a-member-of-a-group-or-project
|
||||
func (s *GroupMembersService) GetGroupMember(gid interface{}, user int, options ...OptionFunc) (*GroupMember, *Response, error) {
|
||||
group, err := parseID(gid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("groups/%s/members/%d", url.QueryEscape(group), user)
|
||||
|
||||
req, err := s.client.NewRequest("GET", 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
|
||||
}
|
||||
|
||||
// AddGroupMember adds a user to the list of group members.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/members.html#add-a-member-to-a-group-or-project
|
||||
func (s *GroupMembersService) AddGroupMember(gid interface{}, opt *AddGroupMemberOptions, options ...OptionFunc) (*GroupMember, *Response, error) {
|
||||
group, err := parseID(gid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("groups/%s/members", url.QueryEscape(group))
|
||||
|
||||
req, err := s.client.NewRequest("POST", 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, err
|
||||
}
|
||||
|
||||
// EditGroupMemberOptions represents the available EditGroupMember()
|
||||
// options.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/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"`
|
||||
}
|
||||
|
||||
// EditGroupMember updates a member of a group.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/members.html#edit-a-member-of-a-group-or-project
|
||||
func (s *GroupMembersService) EditGroupMember(gid interface{}, user int, opt *EditGroupMemberOptions, options ...OptionFunc) (*GroupMember, *Response, error) {
|
||||
group, err := parseID(gid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("groups/%s/members/%d", url.QueryEscape(group), user)
|
||||
|
||||
req, err := s.client.NewRequest("PUT", 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, err
|
||||
}
|
||||
|
||||
// RemoveGroupMember removes user from user team.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/members.html#remove-a-member-from-a-group-or-project
|
||||
func (s *GroupMembersService) RemoveGroupMember(gid interface{}, user int, options ...OptionFunc) (*Response, error) {
|
||||
group, err := parseID(gid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
u := fmt.Sprintf("groups/%s/members/%d", url.QueryEscape(group), user)
|
||||
|
||||
req, err := s.client.NewRequest("DELETE", u, nil, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s.client.Do(req, nil)
|
||||
}
|
||||
250
vendor/github.com/xanzy/go-gitlab/group_milestones.go
generated
vendored
Normal file
250
vendor/github.com/xanzy/go-gitlab/group_milestones.go
generated
vendored
Normal file
@@ -0,0 +1,250 @@
|
||||
//
|
||||
// Copyright 2018, 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/url"
|
||||
"time"
|
||||
)
|
||||
|
||||
// GroupMilestonesService handles communication with the milestone related
|
||||
// methods of the GitLab API.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/api/group_milestones.html
|
||||
type GroupMilestonesService struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// GroupMilestone represents a GitLab milestone.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/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"`
|
||||
}
|
||||
|
||||
func (m GroupMilestone) String() string {
|
||||
return Stringify(m)
|
||||
}
|
||||
|
||||
// ListGroupMilestonesOptions represents the available
|
||||
// ListGroupMilestones() options.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/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"`
|
||||
Search string `url:"search,omitempty" json:"search,omitempty"`
|
||||
}
|
||||
|
||||
// ListGroupMilestones returns a list of group milestones.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/group_milestones.html#list-group-milestones
|
||||
func (s *GroupMilestonesService) ListGroupMilestones(gid interface{}, opt *ListGroupMilestonesOptions, options ...OptionFunc) ([]*GroupMilestone, *Response, error) {
|
||||
group, err := parseID(gid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("groups/%s/milestones", url.QueryEscape(group))
|
||||
|
||||
req, err := s.client.NewRequest("GET", 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, err
|
||||
}
|
||||
|
||||
// GetGroupMilestone gets a single group milestone.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/group_milestones.html#get-single-milestone
|
||||
func (s *GroupMilestonesService) GetGroupMilestone(gid interface{}, milestone int, options ...OptionFunc) (*GroupMilestone, *Response, error) {
|
||||
group, err := parseID(gid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("groups/%s/milestones/%d", url.QueryEscape(group), milestone)
|
||||
|
||||
req, err := s.client.NewRequest("GET", 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, err
|
||||
}
|
||||
|
||||
// CreateGroupMilestoneOptions represents the available CreateGroupMilestone() options.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/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/ce/api/group_milestones.html#create-new-milestone
|
||||
func (s *GroupMilestonesService) CreateGroupMilestone(gid interface{}, opt *CreateGroupMilestoneOptions, options ...OptionFunc) (*GroupMilestone, *Response, error) {
|
||||
group, err := parseID(gid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("groups/%s/milestones", url.QueryEscape(group))
|
||||
|
||||
req, err := s.client.NewRequest("POST", 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, err
|
||||
}
|
||||
|
||||
// UpdateGroupMilestoneOptions represents the available UpdateGroupMilestone() options.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/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/ce/api/group_milestones.html#edit-milestone
|
||||
func (s *GroupMilestonesService) UpdateGroupMilestone(gid interface{}, milestone int, opt *UpdateGroupMilestoneOptions, options ...OptionFunc) (*GroupMilestone, *Response, error) {
|
||||
group, err := parseID(gid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("groups/%s/milestones/%d", url.QueryEscape(group), milestone)
|
||||
|
||||
req, err := s.client.NewRequest("PUT", 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, err
|
||||
}
|
||||
|
||||
// GetGroupMilestoneIssuesOptions represents the available GetGroupMilestoneIssues() options.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/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/ce/api/group_milestones.html#get-all-issues-assigned-to-a-single-milestone
|
||||
func (s *GroupMilestonesService) GetGroupMilestoneIssues(gid interface{}, milestone int, opt *GetGroupMilestoneIssuesOptions, options ...OptionFunc) ([]*Issue, *Response, error) {
|
||||
group, err := parseID(gid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("groups/%s/milestones/%d/issues", url.QueryEscape(group), milestone)
|
||||
|
||||
req, err := s.client.NewRequest("GET", 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, err
|
||||
}
|
||||
|
||||
// GetGroupMilestoneMergeRequestsOptions represents the available
|
||||
// GetGroupMilestoneMergeRequests() options.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/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/ce/api/group_milestones.html#get-all-merge-requests-assigned-to-a-single-milestone
|
||||
func (s *GroupMilestonesService) GetGroupMilestoneMergeRequests(gid interface{}, milestone int, opt *GetGroupMilestoneMergeRequestsOptions, options ...OptionFunc) ([]*MergeRequest, *Response, error) {
|
||||
group, err := parseID(gid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("groups/%s/milestones/%d/merge_requests", url.QueryEscape(group), milestone)
|
||||
|
||||
req, err := s.client.NewRequest("GET", 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, err
|
||||
}
|
||||
307
vendor/github.com/xanzy/go-gitlab/groups.go
generated
vendored
Normal file
307
vendor/github.com/xanzy/go-gitlab/groups.go
generated
vendored
Normal file
@@ -0,0 +1,307 @@
|
||||
//
|
||||
// Copyright 2017, 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/url"
|
||||
)
|
||||
|
||||
// GroupsService handles communication with the group related methods of
|
||||
// the GitLab API.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/api/groups.html
|
||||
type GroupsService struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// Group represents a GitLab group.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/api/groups.html
|
||||
type Group struct {
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Path string `json:"path"`
|
||||
Description string `json:"description"`
|
||||
Visibility *VisibilityValue `json:"visibility"`
|
||||
LFSEnabled bool `json:"lfs_enabled"`
|
||||
AvatarURL string `json:"avatar_url"`
|
||||
WebURL string `json:"web_url"`
|
||||
RequestAccessEnabled bool `json:"request_access_enabled"`
|
||||
FullName string `json:"full_name"`
|
||||
FullPath string `json:"full_path"`
|
||||
ParentID int `json:"parent_id"`
|
||||
Projects []*Project `json:"projects"`
|
||||
Statistics *StorageStatistics `json:"statistics"`
|
||||
}
|
||||
|
||||
// ListGroupsOptions represents the available ListGroups() options.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/api/groups.html#list-project-groups
|
||||
type ListGroupsOptions struct {
|
||||
ListOptions
|
||||
AllAvailable *bool `url:"all_available,omitempty" json:"all_available,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"`
|
||||
Sort *string `url:"sort,omitempty" json:"sort,omitempty"`
|
||||
Statistics *bool `url:"statistics,omitempty" json:"statistics,omitempty"`
|
||||
}
|
||||
|
||||
// ListGroups gets a list of groups (as user: my groups, as admin: all groups).
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/groups.html#list-project-groups
|
||||
func (s *GroupsService) ListGroups(opt *ListGroupsOptions, options ...OptionFunc) ([]*Group, *Response, error) {
|
||||
req, err := s.client.NewRequest("GET", "groups", opt, options)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var g []*Group
|
||||
resp, err := s.client.Do(req, &g)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
|
||||
return g, resp, err
|
||||
}
|
||||
|
||||
// GetGroup gets all details of a group.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/api/groups.html#details-of-a-group
|
||||
func (s *GroupsService) GetGroup(gid interface{}, options ...OptionFunc) (*Group, *Response, error) {
|
||||
group, err := parseID(gid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("groups/%s", url.QueryEscape(group))
|
||||
|
||||
req, err := s.client.NewRequest("GET", 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, err
|
||||
}
|
||||
|
||||
// CreateGroupOptions represents the available CreateGroup() options.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/api/groups.html#new-group
|
||||
type CreateGroupOptions struct {
|
||||
Name *string `url:"name,omitempty" json:"name,omitempty"`
|
||||
Path *string `url:"path,omitempty" json:"path,omitempty"`
|
||||
Description *string `url:"description,omitempty" json:"description,omitempty"`
|
||||
Visibility *VisibilityValue `url:"visibility,omitempty" json:"visibility,omitempty"`
|
||||
LFSEnabled *bool `url:"lfs_enabled,omitempty" json:"lfs_enabled,omitempty"`
|
||||
RequestAccessEnabled *bool `url:"request_access_enabled,omitempty" json:"request_access_enabled,omitempty"`
|
||||
ParentID *int `url:"parent_id,omitempty" json:"parent_id,omitempty"`
|
||||
}
|
||||
|
||||
// CreateGroup creates a new project group. Available only for users who can
|
||||
// create groups.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/api/groups.html#new-group
|
||||
func (s *GroupsService) CreateGroup(opt *CreateGroupOptions, options ...OptionFunc) (*Group, *Response, error) {
|
||||
req, err := s.client.NewRequest("POST", "groups", 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, err
|
||||
}
|
||||
|
||||
// TransferGroup transfers a project to the Group namespace. Available only
|
||||
// for admin.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/groups.html#transfer-project-to-group
|
||||
func (s *GroupsService) TransferGroup(gid interface{}, pid interface{}, options ...OptionFunc) (*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", url.QueryEscape(group),
|
||||
url.QueryEscape(project))
|
||||
|
||||
req, err := s.client.NewRequest("POST", 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, err
|
||||
}
|
||||
|
||||
// UpdateGroupOptions represents the set of available options to update a Group;
|
||||
// as of today these are exactly the same available when creating a new Group.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/api/groups.html#update-group
|
||||
type UpdateGroupOptions CreateGroupOptions
|
||||
|
||||
// UpdateGroup updates an existing group; only available to group owners and
|
||||
// administrators.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/api/groups.html#update-group
|
||||
func (s *GroupsService) UpdateGroup(gid interface{}, opt *UpdateGroupOptions, options ...OptionFunc) (*Group, *Response, error) {
|
||||
group, err := parseID(gid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("groups/%s", url.QueryEscape(group))
|
||||
|
||||
req, err := s.client.NewRequest("PUT", 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, err
|
||||
}
|
||||
|
||||
// DeleteGroup removes group with all projects inside.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/api/groups.html#remove-group
|
||||
func (s *GroupsService) DeleteGroup(gid interface{}, options ...OptionFunc) (*Response, error) {
|
||||
group, err := parseID(gid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
u := fmt.Sprintf("groups/%s", url.QueryEscape(group))
|
||||
|
||||
req, err := s.client.NewRequest("DELETE", u, nil, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s.client.Do(req, nil)
|
||||
}
|
||||
|
||||
// SearchGroup get all groups that match your string in their name or path.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/api/groups.html#search-for-group
|
||||
func (s *GroupsService) SearchGroup(query string, options ...OptionFunc) ([]*Group, *Response, error) {
|
||||
var q struct {
|
||||
Search string `url:"search,omitempty" json:"search,omitempty"`
|
||||
}
|
||||
q.Search = query
|
||||
|
||||
req, err := s.client.NewRequest("GET", "groups", &q, options)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var g []*Group
|
||||
resp, err := s.client.Do(req, &g)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
|
||||
return g, resp, err
|
||||
}
|
||||
|
||||
// ListGroupProjectsOptions represents the available ListGroupProjects()
|
||||
// options.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/groups.html#list-a-group-39-s-projects
|
||||
type ListGroupProjectsOptions ListProjectsOptions
|
||||
|
||||
// ListGroupProjects get a list of group projects
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/groups.html#list-a-group-39-s-projects
|
||||
func (s *GroupsService) ListGroupProjects(gid interface{}, opt *ListGroupProjectsOptions, options ...OptionFunc) ([]*Project, *Response, error) {
|
||||
group, err := parseID(gid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("groups/%s/projects", url.QueryEscape(group))
|
||||
|
||||
req, err := s.client.NewRequest("GET", 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, err
|
||||
}
|
||||
|
||||
// ListSubgroupsOptions represents the available ListSubgroupsOptions()
|
||||
// options.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/groups.html#list-a-groups-s-subgroups
|
||||
type ListSubgroupsOptions ListGroupsOptions
|
||||
|
||||
// ListSubgroups gets a list of subgroups for a given project.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/groups.html#list-a-groups-s-subgroups
|
||||
func (s *GroupsService) ListSubgroups(gid interface{}, opt *ListSubgroupsOptions, options ...OptionFunc) ([]*Group, *Response, error) {
|
||||
group, err := parseID(gid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("groups/%s/subgroups", url.QueryEscape(group))
|
||||
|
||||
req, err := s.client.NewRequest("GET", u, opt, options)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var g []*Group
|
||||
resp, err := s.client.Do(req, &g)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
|
||||
return g, resp, err
|
||||
}
|
||||
128
vendor/github.com/xanzy/go-gitlab/issue_links.go
generated
vendored
Normal file
128
vendor/github.com/xanzy/go-gitlab/issue_links.go
generated
vendored
Normal file
@@ -0,0 +1,128 @@
|
||||
//
|
||||
// Copyright 2017, 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/url"
|
||||
)
|
||||
|
||||
// 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"`
|
||||
}
|
||||
|
||||
// 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{}, issueIID int, options ...OptionFunc) ([]*Issue, *Response, error) {
|
||||
project, err := parseID(pid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("projects/%s/issues/%d/links", url.QueryEscape(project), issueIID)
|
||||
|
||||
req, err := s.client.NewRequest("GET", u, nil, 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, err
|
||||
}
|
||||
|
||||
// CreateIssueLinkOptions represents the available CreateIssueLink() options.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ee/api/issue_links.html
|
||||
type CreateIssueLinkOptions struct {
|
||||
TargetProjectID *string `json:"target_project_id"`
|
||||
TargetIssueIID *string `json:"target_issue_iid"`
|
||||
}
|
||||
|
||||
// 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{}, issueIID int, opt *CreateIssueLinkOptions, options ...OptionFunc) (*IssueLink, *Response, error) {
|
||||
project, err := parseID(pid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("projects/%s/issues/%d/links", url.QueryEscape(project), issueIID)
|
||||
|
||||
req, err := s.client.NewRequest("POST", 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, err
|
||||
}
|
||||
|
||||
// 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{}, issueIID, issueLinkID int, options ...OptionFunc) (*IssueLink, *Response, error) {
|
||||
project, err := parseID(pid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("projects/%s/issues/%d/links/%d",
|
||||
url.QueryEscape(project),
|
||||
issueIID,
|
||||
issueLinkID)
|
||||
|
||||
req, err := s.client.NewRequest("DELETE", u, nil, 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, err
|
||||
}
|
||||
436
vendor/github.com/xanzy/go-gitlab/issues.go
generated
vendored
Normal file
436
vendor/github.com/xanzy/go-gitlab/issues.go
generated
vendored
Normal file
@@ -0,0 +1,436 @@
|
||||
//
|
||||
// Copyright 2017, 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/url"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// IssuesService handles communication with the issue related methods
|
||||
// of the GitLab API.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/api/issues.html
|
||||
type IssuesService struct {
|
||||
client *Client
|
||||
timeStats *timeStatsService
|
||||
}
|
||||
|
||||
// Issue represents a GitLab issue.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/api/issues.html
|
||||
type Issue struct {
|
||||
ID int `json:"id"`
|
||||
IID int `json:"iid"`
|
||||
ProjectID int `json:"project_id"`
|
||||
Milestone *Milestone `json:"milestone"`
|
||||
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"`
|
||||
Description string `json:"description"`
|
||||
State string `json:"state"`
|
||||
Assignees []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:"assignees"`
|
||||
Assignee 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:"assignee"`
|
||||
Upvotes int `json:"upvotes"`
|
||||
Downvotes int `json:"downvotes"`
|
||||
Labels []string `json:"labels"`
|
||||
Title string `json:"title"`
|
||||
UpdatedAt *time.Time `json:"updated_at"`
|
||||
CreatedAt *time.Time `json:"created_at"`
|
||||
ClosedAt *time.Time `json:"closed_at"`
|
||||
Subscribed bool `json:"subscribed"`
|
||||
UserNotesCount int `json:"user_notes_count"`
|
||||
DueDate *ISOTime `json:"due_date"`
|
||||
WebURL string `json:"web_url"`
|
||||
TimeStats *TimeStats `json:"time_stats"`
|
||||
Confidential bool `json:"confidential"`
|
||||
Weight int `json:"weight"`
|
||||
DiscussionLocked bool `json:"discussion_locked"`
|
||||
Links struct {
|
||||
Self string `json:"self"`
|
||||
Notes string `json:"notes"`
|
||||
AwardEmoji string `json:"award_emoji"`
|
||||
Project string `json:"project"`
|
||||
} `json:"_links"`
|
||||
IssueLinkID int `json:"issue_link_id"`
|
||||
}
|
||||
|
||||
func (i Issue) String() string {
|
||||
return Stringify(i)
|
||||
}
|
||||
|
||||
// Labels is a custom type with specific marshaling characteristics.
|
||||
type Labels []string
|
||||
|
||||
// MarshalJSON implements the json.Marshaler interface.
|
||||
func (l *Labels) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(strings.Join(*l, ","))
|
||||
}
|
||||
|
||||
// ListIssuesOptions represents the available ListIssues() options.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/api/issues.html#list-issues
|
||||
type ListIssuesOptions struct {
|
||||
ListOptions
|
||||
State *string `url:"state,omitempty" json:"state,omitempty"`
|
||||
Labels Labels `url:"labels,comma,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"`
|
||||
AssigneeID *int `url:"assignee_id,omitempty" json:"assignee_id,omitempty"`
|
||||
MyReactionEmoji *string `url:"my_reaction_emoji,omitempty" json:"my_reaction_emoji,omitempty"`
|
||||
IIDs []int `url:"iids[],omitempty" json:"iids,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"`
|
||||
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"`
|
||||
}
|
||||
|
||||
// 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/ce/api/issues.html#list-issues
|
||||
func (s *IssuesService) ListIssues(opt *ListIssuesOptions, options ...OptionFunc) ([]*Issue, *Response, error) {
|
||||
req, err := s.client.NewRequest("GET", "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, err
|
||||
}
|
||||
|
||||
// ListGroupIssuesOptions represents the available ListGroupIssues() options.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/api/issues.html#list-group-issues
|
||||
type ListGroupIssuesOptions struct {
|
||||
ListOptions
|
||||
State *string `url:"state,omitempty" json:"state,omitempty"`
|
||||
Labels Labels `url:"labels,comma,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"`
|
||||
AssigneeID *int `url:"assignee_id,omitempty" json:"assignee_id,omitempty"`
|
||||
MyReactionEmoji *string `url:"my_reaction_emoji,omitempty" json:"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"`
|
||||
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"`
|
||||
}
|
||||
|
||||
// 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/ce/api/issues.html#list-group-issues
|
||||
func (s *IssuesService) ListGroupIssues(pid interface{}, opt *ListGroupIssuesOptions, options ...OptionFunc) ([]*Issue, *Response, error) {
|
||||
group, err := parseID(pid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("groups/%s/issues", url.QueryEscape(group))
|
||||
|
||||
req, err := s.client.NewRequest("GET", 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, err
|
||||
}
|
||||
|
||||
// ListProjectIssuesOptions represents the available ListProjectIssues() options.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/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 Labels `url:"labels,comma,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"`
|
||||
AssigneeID *int `url:"assignee_id,omitempty" json:"assignee_id,omitempty"`
|
||||
MyReactionEmoji *string `url:"my_reaction_emoji,omitempty" json:"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"`
|
||||
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"`
|
||||
}
|
||||
|
||||
// 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/ce/api/issues.html#list-project-issues
|
||||
func (s *IssuesService) ListProjectIssues(pid interface{}, opt *ListProjectIssuesOptions, options ...OptionFunc) ([]*Issue, *Response, error) {
|
||||
project, err := parseID(pid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("projects/%s/issues", url.QueryEscape(project))
|
||||
|
||||
req, err := s.client.NewRequest("GET", 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, err
|
||||
}
|
||||
|
||||
// GetIssue gets a single project issue.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/api/issues.html#single-issues
|
||||
func (s *IssuesService) GetIssue(pid interface{}, issue int, options ...OptionFunc) (*Issue, *Response, error) {
|
||||
project, err := parseID(pid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("projects/%s/issues/%d", url.QueryEscape(project), issue)
|
||||
|
||||
req, err := s.client.NewRequest("GET", 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, err
|
||||
}
|
||||
|
||||
// CreateIssueOptions represents the available CreateIssue() options.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/api/issues.html#new-issues
|
||||
type CreateIssueOptions 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 Labels `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"`
|
||||
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"`
|
||||
}
|
||||
|
||||
// CreateIssue creates a new project issue.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/api/issues.html#new-issues
|
||||
func (s *IssuesService) CreateIssue(pid interface{}, opt *CreateIssueOptions, options ...OptionFunc) (*Issue, *Response, error) {
|
||||
project, err := parseID(pid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("projects/%s/issues", url.QueryEscape(project))
|
||||
|
||||
req, err := s.client.NewRequest("POST", 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, err
|
||||
}
|
||||
|
||||
// UpdateIssueOptions represents the available UpdateIssue() options.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/api/issues.html#edit-issues
|
||||
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 Labels `url:"labels,comma,omitempty" json:"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"`
|
||||
DiscussionLocked *bool `url:"discussion_locked,omitempty" json:"discussion_locked,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/ce/api/issues.html#edit-issues
|
||||
func (s *IssuesService) UpdateIssue(pid interface{}, issue int, opt *UpdateIssueOptions, options ...OptionFunc) (*Issue, *Response, error) {
|
||||
project, err := parseID(pid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("projects/%s/issues/%d", url.QueryEscape(project), issue)
|
||||
|
||||
req, err := s.client.NewRequest("PUT", 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, err
|
||||
}
|
||||
|
||||
// DeleteIssue deletes a single project issue.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/api/issues.html#delete-an-issue
|
||||
func (s *IssuesService) DeleteIssue(pid interface{}, issue int, options ...OptionFunc) (*Response, error) {
|
||||
project, err := parseID(pid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
u := fmt.Sprintf("projects/%s/issues/%d", url.QueryEscape(project), issue)
|
||||
|
||||
req, err := s.client.NewRequest("DELETE", u, nil, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s.client.Do(req, nil)
|
||||
}
|
||||
|
||||
// ListMergeRequestsClosingIssueOptions represents the available
|
||||
// ListMergeRequestsClosingIssue() options.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/issues.html#list-merge-requests-that-will-close-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/ce/api/issues.html#list-merge-requests-that-will-close-issue-on-merge
|
||||
func (s *IssuesService) ListMergeRequestsClosingIssue(pid interface{}, issue int, opt *ListMergeRequestsClosingIssueOptions, options ...OptionFunc) ([]*MergeRequest, *Response, error) {
|
||||
project, err := parseID(pid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("/projects/%s/issues/%d/closed_by", url.QueryEscape(project), issue)
|
||||
|
||||
req, err := s.client.NewRequest("GET", 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, err
|
||||
}
|
||||
|
||||
// SetTimeEstimate sets the time estimate for a single project issue.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/issues.html#set-a-time-estimate-for-an-issue
|
||||
func (s *IssuesService) SetTimeEstimate(pid interface{}, issue int, opt *SetTimeEstimateOptions, options ...OptionFunc) (*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/ce/api/issues.html#reset-the-time-estimate-for-an-issue
|
||||
func (s *IssuesService) ResetTimeEstimate(pid interface{}, issue int, options ...OptionFunc) (*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/ce/api/issues.html#add-spent-time-for-an-issue
|
||||
func (s *IssuesService) AddSpentTime(pid interface{}, issue int, opt *AddSpentTimeOptions, options ...OptionFunc) (*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/ce/api/issues.html#reset-spent-time-for-an-issue
|
||||
func (s *IssuesService) ResetSpentTime(pid interface{}, issue int, options ...OptionFunc) (*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/ce/api/issues.html#get-time-tracking-stats
|
||||
func (s *IssuesService) GetTimeSpent(pid interface{}, issue int, options ...OptionFunc) (*TimeStats, *Response, error) {
|
||||
return s.timeStats.getTimeSpent(pid, "issues", issue, options...)
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user