├── .dockerignore ├── .gitignore ├── Dockerfile ├── Gopkg.lock ├── Gopkg.toml ├── LICENSE ├── README.md ├── api ├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── branches.go ├── commits.go ├── commits_test.go ├── deploy_keys.go ├── gitlab.go ├── gitlab_test.go ├── groups.go ├── issues.go ├── labels.go ├── merge_requests.go ├── milestones.go ├── namespaces.go ├── notes.go ├── project_snippets.go ├── projects.go ├── projects_test.go ├── repositories.go ├── repository_files.go ├── services.go ├── services_test.go ├── session.go ├── settings.go ├── strings.go ├── system_hooks.go └── users.go ├── cmd └── main.go └── gitlab.go /.dockerignore: -------------------------------------------------------------------------------- 1 | vendor 2 | .env 3 | Dockerfile 4 | docker-compose* -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | vendor -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.11 AS builder 2 | 3 | RUN curl -fsSL -o /usr/local/bin/dep https://github.com/golang/dep/releases/download/v0.3.2/dep-linux-amd64 && chmod +x /usr/local/bin/dep 4 | ENV PKG github.com/integram-org/gitlab 5 | WORKDIR /go/src/${PKG} 6 | 7 | COPY Gopkg.toml Gopkg.lock ./ 8 | 9 | # install locked dependencies(including Integram framework) versions from Gopkg.lock 10 | RUN dep ensure -vendor-only 11 | 12 | COPY . ./ 13 | 14 | RUN CGO_ENABLED=0 GOOS=linux go build -installsuffix cgo -o /go/app ${PKG}/cmd 15 | 16 | # move the builded binary into the tiny alpine linux image 17 | FROM alpine:latest 18 | RUN apk --no-cache add ca-certificates && rm -rf /var/cache/apk/* 19 | WORKDIR /app 20 | 21 | COPY --from=builder /go/app . 22 | CMD ["./app"] 23 | -------------------------------------------------------------------------------- /Gopkg.lock: -------------------------------------------------------------------------------- 1 | # This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. 2 | 3 | 4 | [[projects]] 5 | branch = "master" 6 | digest = "1:899ab67e7a794d8c454b4e6088c7cb85e6b7b4ec9cf5c94d6c742eb8b2e7da26" 7 | name = "github.com/dchest/uniuri" 8 | packages = ["."] 9 | pruneopts = "" 10 | revision = "8902c56451e9b58ff940bbe5fec35d5f9c04584a" 11 | 12 | [[projects]] 13 | digest = "1:9edeb5a0bfb31fd45134c4d35865a4d08f310c0bc200901ce1370cb90494f3e2" 14 | name = "github.com/garyburd/redigo" 15 | packages = [ 16 | "internal", 17 | "redis", 18 | ] 19 | pruneopts = "" 20 | revision = "d1ed5c67e5794de818ea85e6b522fda02623a484" 21 | version = "v1.4.0" 22 | 23 | [[projects]] 24 | branch = "master" 25 | digest = "1:1120f960f5c334f0f94bad29eefaf73d52d226893369693686148f66c1993f15" 26 | name = "github.com/gin-contrib/sse" 27 | packages = ["."] 28 | pruneopts = "" 29 | revision = "22d885f9ecc78bf4ee5d72b937e4bbcdc58e8cae" 30 | 31 | [[projects]] 32 | branch = "fix-redirect-slash" 33 | digest = "1:c51a9d5aba803d3f443ef7f6ed50cae3d57fe234e508df0fa40062a261ed6143" 34 | name = "github.com/gin-gonic/gin" 35 | packages = [ 36 | ".", 37 | "binding", 38 | "json", 39 | "render", 40 | ] 41 | pruneopts = "" 42 | revision = "588879e55f3c13099159e1f24b7b90946f31266b" 43 | source = "https://github.com/requilence/gin.git" 44 | 45 | [[projects]] 46 | branch = "master" 47 | digest = "1:3b760d3b93f994df8eb1d9ebfad17d3e9e37edcb7f7efaa15b427c0d7a64f4e4" 48 | name = "github.com/golang/protobuf" 49 | packages = ["proto"] 50 | pruneopts = "" 51 | revision = "1e59b77b52bf8e4b449a57e6f79f21226d571845" 52 | 53 | [[projects]] 54 | branch = "master" 55 | digest = "1:9abc49f39e3e23e262594bb4fb70abf74c0c99e94f99153f43b143805e850719" 56 | name = "github.com/google/go-querystring" 57 | packages = ["query"] 58 | pruneopts = "" 59 | revision = "53e6ce116135b80d037921a7fdd5138cf32d7a8a" 60 | 61 | [[projects]] 62 | digest = "1:9eab2325abbed0ebcee9d44bb3660a69d5d10e42d5ac4a0e77f7a6ea22bfce88" 63 | name = "github.com/json-iterator/go" 64 | packages = ["."] 65 | pruneopts = "" 66 | revision = "ca39e5af3ece67bbcda3d0f4f56a8e24d9f2dad4" 67 | version = "1.1.3" 68 | 69 | [[projects]] 70 | digest = "1:b60a24f942c7031ece6c48bcab0b683c7d3d6aa9fd17e21459d9ae604da258fa" 71 | name = "github.com/kelseyhightower/envconfig" 72 | packages = ["."] 73 | pruneopts = "" 74 | revision = "f611eb38b3875cc3bd991ca91c51d06446afa14c" 75 | version = "v1.3.0" 76 | 77 | [[projects]] 78 | digest = "1:d077b8c23b0017d6b34d179a85f03b5eebef699306405f7dac734ceed3b23161" 79 | name = "github.com/kennygrant/sanitize" 80 | packages = ["."] 81 | pruneopts = "" 82 | revision = "2e6820834a1f36c626bf19a253b7d3cc060e9b8b" 83 | version = "v1.2.3" 84 | 85 | [[projects]] 86 | digest = "1:78229b46ddb7434f881390029bd1af7661294af31f6802e0e1bedaad4ab0af3c" 87 | name = "github.com/mattn/go-isatty" 88 | packages = ["."] 89 | pruneopts = "" 90 | revision = "0360b2af4f38e8d38c7fce2a9f4e702702d73a39" 91 | version = "v0.0.3" 92 | 93 | [[projects]] 94 | digest = "1:0c0ff2a89c1bb0d01887e1dac043ad7efbf3ec77482ef058ac423d13497e16fd" 95 | name = "github.com/modern-go/concurrent" 96 | packages = ["."] 97 | pruneopts = "" 98 | revision = "bacd9c7ef1dd9b15be4a9909b8ac7a4e313eec94" 99 | version = "1.0.3" 100 | 101 | [[projects]] 102 | digest = "1:420f9231f816eeca3ff5aab070caac3ed7f27e4d37ded96ce9de3d7a7a2e31ad" 103 | name = "github.com/modern-go/reflect2" 104 | packages = ["."] 105 | pruneopts = "" 106 | revision = "1df9eeb2bb81f327b96228865c5687bc2194af3f" 107 | version = "1.0.0" 108 | 109 | [[projects]] 110 | digest = "1:2d031fefdf62e820a4e954cc20ef1cce8b296f6df7339df778117362832b4c2b" 111 | name = "github.com/mrjones/oauth" 112 | packages = ["."] 113 | pruneopts = "" 114 | revision = "3f67d9c274355678b2f9844b08d643e2f9213340" 115 | 116 | [[projects]] 117 | branch = "master" 118 | digest = "1:0c15d15b9cf4de5bf1d40e7dd6757e36f0a25027cb4b7c5af5c7eb72d145d6d8" 119 | name = "github.com/requilence/integram" 120 | packages = ["."] 121 | pruneopts = "" 122 | revision = "e8bbd7013b481f0d9ef35c22efbc38dd57b4c0d5" 123 | 124 | [[projects]] 125 | branch = "master" 126 | digest = "1:459489412ad1dc454490ea08c5b6d5b54535f1086c061bd6a967017710d07c70" 127 | name = "github.com/requilence/jobs" 128 | packages = ["."] 129 | pruneopts = "" 130 | revision = "e8ced46bbdc79f5c9ac81547ed86484cfb4ba1f9" 131 | 132 | [[projects]] 133 | branch = "master" 134 | digest = "1:a7825782d7041740a1d1f3c24f9c65d3fb65fcee5830a2e3fb147ce84fefad32" 135 | name = "github.com/requilence/telegram-bot-api" 136 | packages = ["."] 137 | pruneopts = "" 138 | revision = "2167dd61acdc53894eef952626db125f470225d3" 139 | 140 | [[projects]] 141 | branch = "master" 142 | digest = "1:b348faf51e9fab0a644967f4b3cccb9c742cecee8b3d0160a538b39d2c8c56cd" 143 | name = "github.com/requilence/url" 144 | packages = ["."] 145 | pruneopts = "" 146 | revision = "6fc4fc0c65da72e95d19a8e5056e222a4f12357f" 147 | 148 | [[projects]] 149 | digest = "1:42a42c4bc67bed17f40fddf0f24d4403e25e7b96488456cf4248e6d16659d370" 150 | name = "github.com/sirupsen/logrus" 151 | packages = ["."] 152 | pruneopts = "" 153 | revision = "d682213848ed68c0a260ca37d6dd5ace8423f5ba" 154 | version = "v1.0.4" 155 | 156 | [[projects]] 157 | digest = "1:3bfdafac43ceb125f775eb79b8ee8f1e976e227464434ffad40f956ac029e760" 158 | name = "github.com/technoweenie/multipartstreamer" 159 | packages = ["."] 160 | pruneopts = "" 161 | revision = "a90a01d73ae432e2611d178c18367fbaa13e0154" 162 | version = "v1.0.1" 163 | 164 | [[projects]] 165 | branch = "master" 166 | digest = "1:9924ebd2e0ff9b4c92cbb82838d776b5913295bcfeaffd5f3c36e55b845f1d7f" 167 | name = "github.com/ugorji/go" 168 | packages = ["codec"] 169 | pruneopts = "" 170 | revision = "9831f2c3ac1068a78f50999a30db84270f647af6" 171 | 172 | [[projects]] 173 | digest = "1:63e1f34a255c19d5741dd8e019e1fb86cb0c4d954a37294010538725aa38ef9e" 174 | name = "github.com/vova616/xxhash" 175 | packages = ["."] 176 | pruneopts = "" 177 | revision = "f0a9a8b74d487f9563a527daf3bd6b4fbd3f5d00" 178 | 179 | [[projects]] 180 | digest = "1:5c2e0bcdb121d29c54cfcb0e5d0988b502fa5b3f4a2e1ee1231eec7aed1b6b13" 181 | name = "github.com/weekface/mgorus" 182 | packages = ["."] 183 | pruneopts = "" 184 | revision = "83720e22971a8301c5b2738c9364accd3d9eb13a" 185 | 186 | [[projects]] 187 | branch = "master" 188 | digest = "1:43adf91783cc814f60c0dd21c9aadf0b5284721e13542e124536638e0b43a6b3" 189 | name = "golang.org/x/crypto" 190 | packages = ["ssh/terminal"] 191 | pruneopts = "" 192 | revision = "13931e22f9e72ea58bb73048bc752b48c6d4d4ac" 193 | 194 | [[projects]] 195 | branch = "master" 196 | digest = "1:f3ee2a699dd02cac37fbca1d028f1121ce0e48143a0f9abd293d3141dcfe6b92" 197 | name = "golang.org/x/net" 198 | packages = [ 199 | "context", 200 | "context/ctxhttp", 201 | "html", 202 | "html/atom", 203 | ] 204 | pruneopts = "" 205 | revision = "5ccada7d0a7ba9aeb5d3aca8d3501b4c2a509fec" 206 | 207 | [[projects]] 208 | branch = "master" 209 | digest = "1:7183f67d61337c51a0e8f6dc1d669db971887373454504062373105761d7b2c9" 210 | name = "golang.org/x/oauth2" 211 | packages = [ 212 | ".", 213 | "internal", 214 | ] 215 | pruneopts = "" 216 | revision = "b28fcf2b08a19742b43084fb40ab78ac6c3d8067" 217 | 218 | [[projects]] 219 | branch = "master" 220 | digest = "1:aba25123b0b02601b134eaf08bd84afe8e12cab70e43770e7b38d8eef8f1d93e" 221 | name = "golang.org/x/sys" 222 | packages = [ 223 | "unix", 224 | "windows", 225 | ] 226 | pruneopts = "" 227 | revision = "2c42eef0765b9837fbdab12011af7830f55f88f0" 228 | 229 | [[projects]] 230 | digest = "1:934fb8966f303ede63aa405e2c8d7f0a427a05ea8df335dfdc1833dd4d40756f" 231 | name = "google.golang.org/appengine" 232 | packages = [ 233 | "internal", 234 | "internal/base", 235 | "internal/datastore", 236 | "internal/log", 237 | "internal/remote_api", 238 | "internal/urlfetch", 239 | "urlfetch", 240 | ] 241 | pruneopts = "" 242 | revision = "150dc57a1b433e64154302bdc40b6bb8aefa313a" 243 | version = "v1.0.0" 244 | 245 | [[projects]] 246 | digest = "1:dd549e360e5a8f982a28c2bcbe667307ceffe538ed9afc7c965524f1ac285b3f" 247 | name = "gopkg.in/go-playground/validator.v8" 248 | packages = ["."] 249 | pruneopts = "" 250 | revision = "5f1438d3fca68893a817e4a66806cea46a9e4ebf" 251 | version = "v8.18.2" 252 | 253 | [[projects]] 254 | branch = "v2" 255 | digest = "1:c80894778314c7fb90d94a5ab925214900e1341afeddc953cda7398b8cdcd006" 256 | name = "gopkg.in/mgo.v2" 257 | packages = [ 258 | ".", 259 | "bson", 260 | "internal/json", 261 | "internal/sasl", 262 | "internal/scram", 263 | ] 264 | pruneopts = "" 265 | revision = "3f83fa5005286a7fe593b055f0d7771a7dce4655" 266 | 267 | [[projects]] 268 | branch = "v2" 269 | digest = "1:4b4e5848dfe7f316f95f754df071bebfb40cf4482da62e17e7e1aebdf11f4918" 270 | name = "gopkg.in/yaml.v2" 271 | packages = ["."] 272 | pruneopts = "" 273 | revision = "d670f9405373e636a5a2765eea47fac0c9bc91a4" 274 | 275 | [solve-meta] 276 | analyzer-name = "dep" 277 | analyzer-version = 1 278 | input-imports = [ 279 | "github.com/google/go-querystring/query", 280 | "github.com/kelseyhightower/envconfig", 281 | "github.com/requilence/integram", 282 | "golang.org/x/oauth2", 283 | ] 284 | solver-name = "gps-cdcl" 285 | solver-version = 1 286 | -------------------------------------------------------------------------------- /Gopkg.toml: -------------------------------------------------------------------------------- 1 | 2 | # Gopkg.toml example 3 | # 4 | # Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md 5 | # for detailed Gopkg.toml documentation. 6 | # 7 | # required = ["github.com/user/thing/cmd/thing"] 8 | # ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] 9 | # 10 | # [[constraint]] 11 | # name = "github.com/user/project" 12 | # version = "1.0.0" 13 | # 14 | # [[constraint]] 15 | # name = "github.com/user/project2" 16 | # branch = "dev" 17 | # source = "github.com/myfork/project2" 18 | # 19 | # [[override]] 20 | # name = "github.com/x/y" 21 | # version = "2.4.0" 22 | 23 | [[constraint]] 24 | branch = "master" 25 | name = "github.com/requilence/integram" 26 | 27 | [[constraint]] 28 | branch = "master" 29 | name = "github.com/google/go-querystring" 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Gitlab integration for Telegram 2 | =========== 3 | 4 | This integration is the part of [Integram](https://github.com/requilence/integram) – framework and platform for integrating services into Telegram. 5 | 6 | [![Docker Image](https://img.shields.io/docker/build/integram/gitlab.svg)](https://hub.docker.com/r/integram/gitlab/) [![GoDoc](https://godoc.org/github.com/integram-org/gitlab?status.svg)](https://godoc.org/github.com/integram-org/gitlab) 7 | 8 | To start using Gitlab bot just write to [@gitlab_bot](https://t.me/gitlab_bot) on Telegram. 9 | 10 | In case you want to deploy this on your own server please refer to the [Integram README](https://github.com/requilence/integram) 11 | 12 | ### License 13 | Code available on GPLV3 [license](https://github.com/requilence/integram/blob/master/LICENSE) 14 | This code includes the modified Gitlab API bindings by [xanzy/go-gitlab](github.com/xanzy/go-gitlab) 15 | 16 | ![Analytics](https://ga-beacon.appspot.com/UA-80266491-1/github_readme) 17 | -------------------------------------------------------------------------------- /api/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | -------------------------------------------------------------------------------- /api/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.4.3 5 | - 1.5.1 6 | -------------------------------------------------------------------------------- /api/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | go-github CHANGELOG 2 | =================== 3 | 4 | 0.1.0 5 | ----- 6 | - Initial release 7 | -------------------------------------------------------------------------------- /api/LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /api/README.md: -------------------------------------------------------------------------------- 1 | # go-gitlab 2 | 3 | A GitLab API client enabling Go programs to interact with GitLab in a simple and uniform way 4 | 5 | **Documentation:** [![GoDoc](https://godoc.org/github.com/xanzy/go-gitlab?status.svg)](https://godoc.org/github.com/xanzy/go-gitlab) 6 | **Build Status:** [![Build Status](https://travis-ci.org/xanzy/go-gitlab.svg?branch=master)](https://travis-ci.org/xanzy/go-gitlab) 7 | 8 | ## Coverage 9 | 10 | This API client package covers **100%** of the existing GitLab API calls! So this 11 | includes all calls to the following services: 12 | 13 | - [x] Users 14 | - [x] Session 15 | - [x] Projects (including setting Webhooks) 16 | - [x] Project Snippets 17 | - [x] Services 18 | - [x] Repositories 19 | - [x] Repository Files 20 | - [x] Commits 21 | - [x] Branches 22 | - [x] Merge Requests 23 | - [x] Issues 24 | - [x] Labels 25 | - [x] Milestones 26 | - [x] Notes (comments) 27 | - [x] Deploy Keys 28 | - [x] System Hooks 29 | - [x] Groups 30 | - [x] Namespaces 31 | - [x] Settings 32 | 33 | ## Usage 34 | 35 | ```go 36 | import "github.com/xanzy/go-gitlab" 37 | ``` 38 | 39 | Construct a new GitLab client, then use the various services on the client to 40 | access different parts of the GitLab API. For example, to list all 41 | users: 42 | 43 | ```go 44 | git := gitlab.NewClient(nil, "yourtokengoeshere") 45 | users, _, err := git.Users.ListUsers() 46 | ``` 47 | 48 | Some API methods have optional parameters that can be passed. For example, 49 | to list all projects for user "svanharmelen": 50 | 51 | ```go 52 | client := github.NewClient(nil) 53 | opt := &ListProjectsOptions{Search: "svanharmelen"}) 54 | projects, _, err := client.Projects.ListProjects(opt) 55 | ``` 56 | 57 | ### Examples 58 | 59 | The [examples](https://github.com/xanzy/go-gitlab/tree/master/examples) directory 60 | contains a couple for clear examples, of which one is partially listed here as well: 61 | 62 | ```go 63 | package main 64 | 65 | import ( 66 | "log" 67 | 68 | "github.com/xanzy/go-gitlab" 69 | ) 70 | 71 | func main() { 72 | git := gitlab.NewClient(nil, "yourtokengoeshere") 73 | 74 | // Create new project 75 | p := &gitlab.CreateProjectOptions{ 76 | Name: "My Project", 77 | Description: "Just a test project to play with", 78 | MergeRequestsEnabled: true, 79 | SnippetsEnabled: true, 80 | VisibilityLevel: gitlab.PublicVisibility, 81 | } 82 | project, _, err := git.Projects.CreateProject(p) 83 | if err != nil { 84 | log.Fatal(err) 85 | } 86 | 87 | // Add a new snippet 88 | s := &gitlab.CreateSnippetOptions{ 89 | Title: "Dummy Snippet", 90 | FileName: "snippet.go", 91 | Code: "package main....", 92 | VisibilityLevel: gitlab.PublicVisibility, 93 | } 94 | _, _, err = git.ProjectSnippets.CreateSnippet(project.ID, s) 95 | if err != nil { 96 | log.Fatal(err) 97 | } 98 | } 99 | 100 | ``` 101 | 102 | For complete usage of go-gitlab, see the full [package docs](https://godoc.org/github.com/xanzy/go-gitlab). 103 | 104 | ## ToDo 105 | 106 | - The biggest thing this package still needs is tests :disappointed: 107 | 108 | ## Issues 109 | 110 | - If you have an issue: report it on the [issue tracker](https://github.com/xanzy/go-gitlab/issues) 111 | 112 | ## Author 113 | 114 | Sander van Harmelen () 115 | 116 | ## License 117 | 118 | 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 119 | -------------------------------------------------------------------------------- /api/branches.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2015, Sander van Harmelen 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | package gitlab 18 | 19 | import ( 20 | "fmt" 21 | "net/url" 22 | ) 23 | 24 | // BranchesService handles communication with the branch related methods 25 | // of the GitLab API. 26 | // 27 | // GitLab API docs: http://doc.gitlab.com/ce/api/branches.html 28 | type BranchesService struct { 29 | client *Client 30 | } 31 | 32 | // Branch represents a GitLab branch. 33 | // 34 | // GitLab API docs: http://doc.gitlab.com/ce/api/branches.html 35 | type Branch struct { 36 | Commit *Commit `json:"commit"` 37 | Name string `json:"name"` 38 | Protected bool `json:"protected"` 39 | } 40 | 41 | func (b Branch) String() string { 42 | return Stringify(b) 43 | } 44 | 45 | // ListBranches gets a list of repository branches from a project, sorted by 46 | // name alphabetically. 47 | // 48 | // GitLab API docs: 49 | // http://doc.gitlab.com/ce/api/branches.html#list-repository-branches 50 | func (s *BranchesService) ListBranches(pid interface{}) ([]*Branch, *Response, error) { 51 | project, err := parseID(pid) 52 | if err != nil { 53 | return nil, nil, err 54 | } 55 | u := fmt.Sprintf("projects/%s/repository/branches", url.QueryEscape(project)) 56 | 57 | req, err := s.client.NewRequest("GET", u, nil) 58 | if err != nil { 59 | return nil, nil, err 60 | } 61 | 62 | var b []*Branch 63 | resp, err := s.client.Do(req, &b) 64 | if err != nil { 65 | return nil, resp, err 66 | } 67 | 68 | return b, resp, err 69 | } 70 | 71 | // GetBranch gets a single project repository branch. 72 | // 73 | // GitLab API docs: 74 | // http://doc.gitlab.com/ce/api/branches.html#get-single-repository-branch 75 | func (s *BranchesService) GetBranch(pid interface{}, branch string) (*Branch, *Response, error) { 76 | project, err := parseID(pid) 77 | if err != nil { 78 | return nil, nil, err 79 | } 80 | u := fmt.Sprintf("projects/%s/repository/branches/%s", url.QueryEscape(project), branch) 81 | 82 | req, err := s.client.NewRequest("GET", u, nil) 83 | if err != nil { 84 | return nil, nil, err 85 | } 86 | 87 | b := new(Branch) 88 | resp, err := s.client.Do(req, b) 89 | if err != nil { 90 | return nil, resp, err 91 | } 92 | 93 | return b, resp, err 94 | } 95 | 96 | // ProtectBranch protects a single project repository branch. This is an 97 | // idempotent function, protecting an already protected repository branch 98 | // still returns a 200 OK status code. 99 | // 100 | // GitLab API docs: 101 | // http://doc.gitlab.com/ce/api/branches.html#protect-repository-branch 102 | func (s *BranchesService) ProtectBranch(pid interface{}, branch string) (*Branch, *Response, error) { 103 | project, err := parseID(pid) 104 | if err != nil { 105 | return nil, nil, err 106 | } 107 | u := fmt.Sprintf("projects/%s/repository/branches/%s/protect", url.QueryEscape(project), branch) 108 | 109 | req, err := s.client.NewRequest("PUT", u, nil) 110 | if err != nil { 111 | return nil, nil, err 112 | } 113 | 114 | b := new(Branch) 115 | resp, err := s.client.Do(req, b) 116 | if err != nil { 117 | return nil, resp, err 118 | } 119 | 120 | return b, resp, err 121 | } 122 | 123 | // UnprotectBranch unprotects a single project repository branch. This is an 124 | // idempotent function, unprotecting an already unprotected repository branch 125 | // still returns a 200 OK status code. 126 | // 127 | // GitLab API docs: 128 | // http://doc.gitlab.com/ce/api/branches.html#unprotect-repository-branch 129 | func (s *BranchesService) UnprotectBranch( 130 | pid interface{}, 131 | branch string) (*Branch, *Response, error) { 132 | project, err := parseID(pid) 133 | if err != nil { 134 | return nil, nil, err 135 | } 136 | u := fmt.Sprintf("projects/%s/repository/branches/%s/unprotect", url.QueryEscape(project), branch) 137 | 138 | req, err := s.client.NewRequest("PUT", u, nil) 139 | if err != nil { 140 | return nil, nil, err 141 | } 142 | 143 | b := new(Branch) 144 | resp, err := s.client.Do(req, b) 145 | if err != nil { 146 | return nil, resp, err 147 | } 148 | 149 | return b, resp, err 150 | } 151 | 152 | // CreateBranchOptions represents the available CreateBranch() options. 153 | // 154 | // GitLab API docs: 155 | // http://doc.gitlab.com/ce/api/branches.html#create-repository-branch 156 | type CreateBranchOptions struct { 157 | BranchName string `url:"branch_name,omitempty" json:"branch_name,omitempty"` 158 | Ref string `url:"ref,omitempty" json:"ref,omitempty"` 159 | } 160 | 161 | // CreateBranch creates branch from commit SHA or existing branch. 162 | // 163 | // GitLab API docs: 164 | // http://doc.gitlab.com/ce/api/branches.html#create-repository-branch 165 | func (s *BranchesService) CreateBranch( 166 | pid interface{}, 167 | opt *CreateBranchOptions) (*Branch, *Response, error) { 168 | project, err := parseID(pid) 169 | if err != nil { 170 | return nil, nil, err 171 | } 172 | u := fmt.Sprintf("projects/%s/repository/branches", url.QueryEscape(project)) 173 | 174 | req, err := s.client.NewRequest("POST", u, opt) 175 | if err != nil { 176 | return nil, nil, err 177 | } 178 | 179 | b := new(Branch) 180 | resp, err := s.client.Do(req, b) 181 | if err != nil { 182 | return nil, resp, err 183 | } 184 | 185 | return b, resp, err 186 | } 187 | 188 | // DeleteBranch deletes an existing branch. 189 | // 190 | // GitLab API docs: 191 | // http://doc.gitlab.com/ce/api/branches.html#delete-repository-branch 192 | func (s *BranchesService) DeleteBranch(pid interface{}, branch string) (*Response, error) { 193 | project, err := parseID(pid) 194 | if err != nil { 195 | return nil, err 196 | } 197 | u := fmt.Sprintf("projects/%s/repository/branches/%s", url.QueryEscape(project), branch) 198 | 199 | req, err := s.client.NewRequest("DELETE", u, nil) 200 | if err != nil { 201 | return nil, err 202 | } 203 | 204 | resp, err := s.client.Do(req, nil) 205 | if err != nil { 206 | return resp, err 207 | } 208 | 209 | return resp, err 210 | } 211 | -------------------------------------------------------------------------------- /api/commits.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2015, Sander van Harmelen 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | package gitlab 18 | 19 | import ( 20 | "fmt" 21 | "net/url" 22 | "time" 23 | ) 24 | 25 | // CommitsService handles communication with the commit related methods 26 | // of the GitLab API. 27 | // 28 | // GitLab API docs: http://doc.gitlab.com/ce/api/commits.html 29 | type CommitsService struct { 30 | client *Client 31 | } 32 | 33 | // Commit represents a GitLab commit. 34 | // 35 | // GitLab API docs: http://doc.gitlab.com/ce/api/commits.html 36 | type Commit struct { 37 | ID string `json:"id"` 38 | ShortID string `json:"short_id"` 39 | Title string `json:"title"` 40 | AuthorName string `json:"author_name"` 41 | AuthorEmail string `json:"author_email"` 42 | AuthoredDate time.Time `json:"authored_date"` 43 | CommittedDate time.Time `json:"committed_date"` 44 | CreatedAt time.Time `json:"created_at"` 45 | Message string `json:"message"` 46 | ParentsIds []string `json:"parents_ids"` 47 | } 48 | 49 | func (c Commit) String() string { 50 | return Stringify(c) 51 | } 52 | 53 | // ListCommitsOptions represents the available ListCommits() options. 54 | // 55 | // GitLab API docs: http://doc.gitlab.com/ce/api/commits.html#list-commits 56 | type ListCommitsOptions struct { 57 | ListOptions 58 | RefName string `url:"ref_name,omitempty" json:"ref_name,omitempty"` 59 | } 60 | 61 | // ListCommits gets a list of repository commits in a project. 62 | // 63 | // GitLab API docs: http://doc.gitlab.com/ce/api/commits.html#list-commits 64 | func (s *CommitsService) ListCommits( 65 | pid interface{}, 66 | opt *ListCommitsOptions) ([]*Commit, *Response, error) { 67 | project, err := parseID(pid) 68 | if err != nil { 69 | return nil, nil, err 70 | } 71 | u := fmt.Sprintf("projects/%s/repository/commits", url.QueryEscape(project)) 72 | 73 | req, err := s.client.NewRequest("GET", u, opt) 74 | if err != nil { 75 | return nil, nil, err 76 | } 77 | 78 | var c []*Commit 79 | resp, err := s.client.Do(req, &c) 80 | if err != nil { 81 | return nil, resp, err 82 | } 83 | 84 | return c, resp, err 85 | } 86 | 87 | // GetCommit gets a specific commit identified by the commit hash or name of a 88 | // branch or tag. 89 | // 90 | // GitLab API docs: http://doc.gitlab.com/ce/api/commits.html#get-a-single-commit 91 | func (s *CommitsService) GetCommit( 92 | pid interface{}, 93 | sha string) (*Commit, *Response, error) { 94 | project, err := parseID(pid) 95 | if err != nil { 96 | return nil, nil, err 97 | } 98 | u := fmt.Sprintf("projects/%s/repository/commits/%s", url.QueryEscape(project), sha) 99 | 100 | req, err := s.client.NewRequest("GET", u, nil) 101 | if err != nil { 102 | return nil, nil, err 103 | } 104 | 105 | c := new(Commit) 106 | resp, err := s.client.Do(req, c) 107 | if err != nil { 108 | return nil, resp, err 109 | } 110 | 111 | return c, resp, err 112 | } 113 | 114 | // Diff represents a GitLab diff. 115 | // 116 | // GitLab API docs: http://doc.gitlab.com/ce/api/commits.html 117 | type Diff struct { 118 | Diff string `json:"diff"` 119 | NewPath string `json:"new_path"` 120 | OldPath string `json:"old_path"` 121 | AMode string `json:"a_mode"` 122 | BMode string `json:"b_mode"` 123 | NewFile bool `json:"new_file"` 124 | RenamedFile bool `json:"renamed_file"` 125 | DeletedFile bool `json:"deleted_file"` 126 | } 127 | 128 | func (d Diff) String() string { 129 | return Stringify(d) 130 | } 131 | 132 | // GetCommitDiff gets the diff of a commit in a project.. 133 | // 134 | // GitLab API docs: 135 | // http://doc.gitlab.com/ce/api/commits.html#get-the-diff-of-a-commit 136 | func (s *CommitsService) GetCommitDiff( 137 | pid interface{}, 138 | sha string) ([]*Diff, *Response, error) { 139 | project, err := parseID(pid) 140 | if err != nil { 141 | return nil, nil, err 142 | } 143 | u := fmt.Sprintf("projects/%s/repository/commits/%s/diff", url.QueryEscape(project), sha) 144 | 145 | req, err := s.client.NewRequest("GET", u, nil) 146 | if err != nil { 147 | return nil, nil, err 148 | } 149 | 150 | var d []*Diff 151 | resp, err := s.client.Do(req, &d) 152 | if err != nil { 153 | return nil, resp, err 154 | } 155 | 156 | return d, resp, err 157 | } 158 | 159 | // CommitComment represents a GitLab commit comment. 160 | // 161 | // GitLab API docs: http://doc.gitlab.com/ce/api/commits.html 162 | type CommitComment struct { 163 | Note string `json:"note"` 164 | Path string `json:"path"` 165 | Line int `json:"line"` 166 | LineType string `json:"line_type"` 167 | Author Author `json:"author"` 168 | } 169 | 170 | type Author struct { 171 | ID int `json:"id"` 172 | Username string `json:"username"` 173 | Email string `json:"email"` 174 | Name string `json:"name"` 175 | State string `json:"state"` 176 | Blocked bool `json:"blocked"` 177 | CreatedAt time.Time `json:"created_at"` 178 | } 179 | 180 | func (c CommitComment) String() string { 181 | return Stringify(c) 182 | } 183 | 184 | // GetCommitComments gets the comments of a commit in a project. 185 | // 186 | // GitLab API docs: 187 | // http://doc.gitlab.com/ce/api/commits.html#get-the-comments-of-a-commit 188 | func (s *CommitsService) GetCommitComments( 189 | pid interface{}, 190 | sha string) ([]*CommitComment, *Response, error) { 191 | project, err := parseID(pid) 192 | if err != nil { 193 | return nil, nil, err 194 | } 195 | u := fmt.Sprintf("projects/%s/repository/commits/%s/comments", url.QueryEscape(project), sha) 196 | 197 | req, err := s.client.NewRequest("GET", u, nil) 198 | if err != nil { 199 | return nil, nil, err 200 | } 201 | 202 | var c []*CommitComment 203 | resp, err := s.client.Do(req, &c) 204 | if err != nil { 205 | return nil, resp, err 206 | } 207 | 208 | return c, resp, err 209 | } 210 | 211 | // PostCommitCommentOptions represents the available PostCommitComment() 212 | // options. 213 | // 214 | // GitLab API docs: 215 | // http://doc.gitlab.com/ce/api/commits.html#post-comment-to-commit 216 | type PostCommitCommentOptions struct { 217 | Note string `url:"note,omitempty" json:"note,omitempty"` 218 | Path string `url:"path" json:"path"` 219 | Line int `url:"line" json:"line"` 220 | LineType string `url:"line_type" json:"line_type"` 221 | } 222 | 223 | // PostCommitComment adds a comment to a commit. Optionally you can post 224 | // comments on a specific line of a commit. Therefor both path, line_new and 225 | // line_old are required. 226 | // 227 | // GitLab API docs: 228 | // http://doc.gitlab.com/ce/api/commits.html#post-comment-to-commit 229 | func (s *CommitsService) PostCommitComment( 230 | pid interface{}, 231 | sha string, 232 | opt *PostCommitCommentOptions) (*CommitComment, *Response, error) { 233 | project, err := parseID(pid) 234 | if err != nil { 235 | return nil, nil, err 236 | } 237 | u := fmt.Sprintf("projects/%s/repository/commits/%s/comments", url.QueryEscape(project), sha) 238 | 239 | req, err := s.client.NewRequest("POST", u, opt) 240 | if err != nil { 241 | return nil, nil, err 242 | } 243 | 244 | c := new(CommitComment) 245 | resp, err := s.client.Do(req, c) 246 | if err != nil { 247 | return nil, resp, err 248 | } 249 | 250 | return c, resp, err 251 | } 252 | 253 | // GetCommitStatusesOptions represents the available GetCommitStatuses() options. 254 | // 255 | // GitLab API docs: http://doc.gitlab.com/ce/api/commits.html#get-the-status-of-a-commit 256 | type GetCommitStatusesOptions struct { 257 | Ref string `url:"ref,omitempty" json:"ref,omitempty"` 258 | Stage string `url:"stage,omitempty" json:"stage,omitempty"` 259 | Name string `url:"name,omitempty" json:"name,omitempty"` 260 | All bool `url:"all,omitempty" json:"all,omitempty"` 261 | } 262 | 263 | // CommitStatus represents a GitLab commit status. 264 | // 265 | // GitLab API docs: http://doc.gitlab.com/ce/api/commits.html#get-the-status-of-a-commit 266 | type CommitStatus struct { 267 | ID int `json:"id"` 268 | SHA string `json:"sha"` 269 | Ref string `json:"ref"` 270 | Status string `json:"status"` 271 | Name string `json:"name"` 272 | TargetUrl string `json:"target_url"` 273 | Description string `json:"description"` 274 | CreatedAt time.Time `json:"created_at"` 275 | StartedAt time.Time `json:"started_at"` 276 | FinishedAt time.Time `json:"finished_at"` 277 | Author Author `json:"author"` 278 | } 279 | 280 | // GetCommitStatuses gets the statuses of a commit in a project. 281 | // 282 | // GitLab API docs: http://doc.gitlab.com/ce/api/commits.html#get-the-status-of-a-commit 283 | func (s *CommitsService) GetCommitStatuses( 284 | pid interface{}, 285 | sha string, 286 | opt *GetCommitStatusesOptions) ([]*CommitStatus, *Response, error) { 287 | project, err := parseID(pid) 288 | if err != nil { 289 | return nil, nil, err 290 | } 291 | u := fmt.Sprintf("projects/%s/repository/commits/%s/statuses", url.QueryEscape(project), sha) 292 | 293 | req, err := s.client.NewRequest("GET", u, opt) 294 | if err != nil { 295 | return nil, nil, err 296 | } 297 | 298 | var cs []*CommitStatus 299 | resp, err := s.client.Do(req, &cs) 300 | if err != nil { 301 | return nil, resp, err 302 | } 303 | 304 | return cs, resp, err 305 | } 306 | 307 | // SetCommitStatusOptions represents the available SetCommitStatus() options. 308 | // 309 | // GitLab API docs: http://doc.gitlab.com/ce/api/commits.html#post-the-status-to-commit 310 | type SetCommitStatusOptions struct { 311 | State BuildState `url:"state" json:"state"` 312 | Ref string `url:"ref,omitempty" json:"ref,omitempty"` 313 | Name string `url:"name,omitempty" json:"name,omitempty"` 314 | Context string `url:"context,omitempty" json:"context,omitempty"` 315 | TargetUrl string `url:"target_url,omitempty" json:"target_url,omitempty"` 316 | Description string `url:"description,omitempty" json:"description,omitempty"` 317 | } 318 | 319 | type BuildState string 320 | 321 | const ( 322 | Pending BuildState = "pending" 323 | Running BuildState = "running" 324 | Success BuildState = "success" 325 | Failed BuildState = "failed" 326 | Canceled BuildState = "canceled" 327 | ) 328 | 329 | // SetCommitStatus sets the status of a commit in a project. 330 | // 331 | // GitLab API docs: http://doc.gitlab.com/ce/api/commits.html#post-the-status-to-commit 332 | func (s *CommitsService) SetCommitStatus( 333 | pid interface{}, 334 | sha string, 335 | opt *SetCommitStatusOptions) (*CommitStatus, *Response, error) { 336 | project, err := parseID(pid) 337 | if err != nil { 338 | return nil, nil, err 339 | } 340 | u := fmt.Sprintf("projects/%s/statuses/%s", url.QueryEscape(project), sha) 341 | 342 | req, err := s.client.NewRequest("POST", u, opt) 343 | if err != nil { 344 | return nil, nil, err 345 | } 346 | 347 | var cs *CommitStatus 348 | resp, err := s.client.Do(req, &cs) 349 | if err != nil { 350 | return nil, resp, err 351 | } 352 | 353 | return cs, resp, err 354 | } 355 | -------------------------------------------------------------------------------- /api/commits_test.go: -------------------------------------------------------------------------------- 1 | package gitlab 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "reflect" 7 | "testing" 8 | ) 9 | 10 | func TestGetCommitStatuses(t *testing.T) { 11 | mux, server, client := setup() 12 | defer teardown(server) 13 | 14 | mux.HandleFunc("/projects/1/repository/commits/b0b3a907f41409829b307a28b82fdbd552ee5a27/statuses", func(w http.ResponseWriter, r *http.Request) { 15 | testMethod(t, r, "GET") 16 | testFormValues(t, r, values{ 17 | "ref": "master", 18 | "stage": "test", 19 | "name": "ci/jenkins", 20 | "all": "true", 21 | }) 22 | fmt.Fprint(w, `[{"id":1}]`) 23 | }) 24 | 25 | opt := &GetCommitStatusesOptions{"master", "test", "ci/jenkins", true} 26 | statuses, _, err := client.Commits.GetCommitStatuses("1", "b0b3a907f41409829b307a28b82fdbd552ee5a27", opt) 27 | 28 | if err != nil { 29 | t.Errorf("Commits.GetCommitStatuses returned error: %v", err) 30 | } 31 | 32 | want := []*CommitStatus{{ID: 1}} 33 | if !reflect.DeepEqual(want, statuses) { 34 | t.Errorf("Commits.GetCommitStatuses returned %+v, want %+v", statuses, want) 35 | } 36 | } 37 | 38 | func TestSetCommitStatus(t *testing.T) { 39 | mux, server, client := setup() 40 | defer teardown(server) 41 | 42 | mux.HandleFunc("/projects/1/statuses/b0b3a907f41409829b307a28b82fdbd552ee5a27", func(w http.ResponseWriter, r *http.Request) { 43 | testMethod(t, r, "POST") 44 | testJsonBody(t, r, values{ 45 | "state": "running", 46 | "ref": "master", 47 | "name": "ci/jenkins", 48 | "target_url": "http://abc", 49 | "description": "build", 50 | }) 51 | fmt.Fprint(w, `{"id":1}`) 52 | }) 53 | 54 | opt := &SetCommitStatusOptions{Running, "master", "ci/jenkins", "", "http://abc", "build"} 55 | status, _, err := client.Commits.SetCommitStatus("1", "b0b3a907f41409829b307a28b82fdbd552ee5a27", opt) 56 | 57 | if err != nil { 58 | t.Errorf("Commits.SetCommitStatus returned error: %v", err) 59 | } 60 | 61 | want := &CommitStatus{ID: 1} 62 | if !reflect.DeepEqual(want, status) { 63 | t.Errorf("Commits.SetCommitStatus returned %+v, want %+v", status, want) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /api/deploy_keys.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2015, Sander van Harmelen 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | package gitlab 18 | 19 | import ( 20 | "fmt" 21 | "net/url" 22 | "time" 23 | ) 24 | 25 | // DeployKeysService handles communication with the keys related methods 26 | // of the GitLab API. 27 | // 28 | // GitLab API docs: http://doc.gitlab.com/ce/api/deploy_keys.html 29 | type DeployKeysService struct { 30 | client *Client 31 | } 32 | 33 | // DeployKey represents a GitLab deploy key. 34 | type DeployKey struct { 35 | ID int `json:"id"` 36 | Title string `json:"title"` 37 | Key string `json:"key"` 38 | CreatedAt time.Time `json:"created_at"` 39 | } 40 | 41 | func (k DeployKey) String() string { 42 | return Stringify(k) 43 | } 44 | 45 | // ListDeployKeys gets a list of a project's deploy keys 46 | // 47 | // GitLab API docs: 48 | // http://doc.gitlab.com/ce/api/deploy_keys.html#list-deploy-keys 49 | func (s *DeployKeysService) ListDeployKeys(pid interface{}) ([]*DeployKey, *Response, error) { 50 | project, err := parseID(pid) 51 | if err != nil { 52 | return nil, nil, err 53 | } 54 | u := fmt.Sprintf("projects/%s/keys", url.QueryEscape(project)) 55 | 56 | req, err := s.client.NewRequest("GET", u, nil) 57 | if err != nil { 58 | return nil, nil, err 59 | } 60 | 61 | var k []*DeployKey 62 | resp, err := s.client.Do(req, &k) 63 | if err != nil { 64 | return nil, resp, err 65 | } 66 | 67 | return k, resp, err 68 | } 69 | 70 | // GetDeployKey gets a single deploy key. 71 | // 72 | // GitLab API docs: 73 | // http://doc.gitlab.com/ce/api/deploy_keys.html#single-deploy-key 74 | func (s *DeployKeysService) GetDeployKey( 75 | pid interface{}, 76 | deployKey int) (*DeployKey, *Response, error) { 77 | project, err := parseID(pid) 78 | if err != nil { 79 | return nil, nil, err 80 | } 81 | u := fmt.Sprintf("projects/%s/keys/%d", url.QueryEscape(project), deployKey) 82 | 83 | req, err := s.client.NewRequest("GET", u, nil) 84 | if err != nil { 85 | return nil, nil, err 86 | } 87 | 88 | k := new(DeployKey) 89 | resp, err := s.client.Do(req, k) 90 | if err != nil { 91 | return nil, resp, err 92 | } 93 | 94 | return k, resp, err 95 | } 96 | 97 | // AddDeployKeyOptions represents the available ADDDeployKey() options. 98 | // 99 | // GitLab API docs: 100 | // http://doc.gitlab.com/ce/api/deploy_keys.html#add-deploy-key 101 | type AddDeployKeyOptions struct { 102 | Title string `url:"title,omitempty" json:"title,omitempty"` 103 | Key string `url:"key,omitempty" json:"key,omitempty"` 104 | } 105 | 106 | // AddDeployKey creates a new deploy key for a project. If deploy key already 107 | // exists in another project - it will be joined to project but only if 108 | // original one was is accessible by same user. 109 | // 110 | // GitLab API docs: 111 | // http://doc.gitlab.com/ce/api/deploy_keys.html#add-deploy-key 112 | func (s *DeployKeysService) AddDeployKey( 113 | pid interface{}, 114 | opt *AddDeployKeyOptions) (*DeployKey, *Response, error) { 115 | project, err := parseID(pid) 116 | if err != nil { 117 | return nil, nil, err 118 | } 119 | u := fmt.Sprintf("projects/%s/keys", url.QueryEscape(project)) 120 | 121 | req, err := s.client.NewRequest("POST", u, opt) 122 | if err != nil { 123 | return nil, nil, err 124 | } 125 | 126 | k := new(DeployKey) 127 | resp, err := s.client.Do(req, k) 128 | if err != nil { 129 | return nil, resp, err 130 | } 131 | 132 | return k, resp, err 133 | } 134 | 135 | // DeleteDeployKey deletes a deploy key from a project. 136 | // 137 | // GitLab API docs: 138 | // http://doc.gitlab.com/ce/api/deploy_keys.html#delete-deploy-key 139 | func (s *DeployKeysService) DeleteDeployKey(pid interface{}, deployKey int) (*Response, error) { 140 | project, err := parseID(pid) 141 | if err != nil { 142 | return nil, err 143 | } 144 | u := fmt.Sprintf("projects/%s/keys/%d", url.QueryEscape(project), deployKey) 145 | 146 | req, err := s.client.NewRequest("DELETE", u, nil) 147 | if err != nil { 148 | return nil, err 149 | } 150 | 151 | resp, err := s.client.Do(req, nil) 152 | if err != nil { 153 | return resp, err 154 | } 155 | 156 | return resp, err 157 | } 158 | -------------------------------------------------------------------------------- /api/gitlab.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2015, Sander van Harmelen 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | package gitlab 18 | 19 | import ( 20 | "bytes" 21 | "encoding/json" 22 | "errors" 23 | "fmt" 24 | "io" 25 | "io/ioutil" 26 | "net/http" 27 | "net/url" 28 | "strconv" 29 | "strings" 30 | 31 | "github.com/google/go-querystring/query" 32 | ) 33 | 34 | const ( 35 | libraryVersion = "0.1" 36 | defaultBaseURL = "https://gitlab.com/api/v3/" 37 | userAgent = "go-gitlab/" + libraryVersion 38 | ) 39 | 40 | // AccessLevel represents a permission level within GitLab. 41 | // 42 | // GitLab API docs: http://doc.gitlab.com/ce/permissions/permissions.html 43 | type AccessLevel int 44 | 45 | // List of available access levels 46 | // 47 | // GitLab API docs: http://doc.gitlab.com/ce/permissions/permissions.html 48 | const ( 49 | GuestPermissions AccessLevel = 10 50 | ReporterPermissions AccessLevel = 20 51 | DeveloperPermissions AccessLevel = 30 52 | MasterPermissions AccessLevel = 40 53 | OwnerPermission AccessLevel = 50 54 | ) 55 | 56 | // NotificationLevel represents a notification level within Gitlab. 57 | // 58 | // GitLab API docs: http://doc.gitlab.com/ce/...? 59 | type NotificationLevel int 60 | 61 | // List of available notification levels 62 | // 63 | // GitLab API docs: http://doc.gitlab.com/ce/...? 64 | const ( 65 | DisabledNotifications NotificationLevel = iota 66 | ParticipatingNotifications 67 | WatchNotifications 68 | GlobalNotifications 69 | MentionNotifications 70 | ) 71 | 72 | // VisibilityLevel represents a visibility level within GitLab. 73 | // 74 | // GitLab API docs: http://doc.gitlab.com/ce/...? 75 | type VisibilityLevel int 76 | 77 | // List of available visibility levels 78 | // 79 | // GitLab API docs: http://doc.gitlab.com/ce/...? 80 | const ( 81 | PrivateVisibility VisibilityLevel = 0 82 | InternalVisibility VisibilityLevel = 10 83 | PublicVisibility VisibilityLevel = 20 84 | ) 85 | 86 | // A Client manages communication with the GitLab API. 87 | type Client struct { 88 | // HTTP client used to communicate with the API. 89 | client *http.Client 90 | 91 | // Base URL for API requests. Defaults to the public GitLab API, but can be 92 | // set to a domain endpoint to use with aself hosted GitLab server. baseURL 93 | // should always be specified with a trailing slash. 94 | baseURL *url.URL 95 | 96 | // Private token used to make authenticated API calls. 97 | token string 98 | 99 | // User agent used when communicating with the GitLab API. 100 | UserAgent string 101 | 102 | // Services used for talking to different parts of the GitLab API. 103 | Branches *BranchesService 104 | Commits *CommitsService 105 | DeployKeys *DeployKeysService 106 | Groups *GroupsService 107 | Issues *IssuesService 108 | Labels *LabelsService 109 | MergeRequests *MergeRequestsService 110 | Milestones *MilestonesService 111 | Namespaces *NamespacesService 112 | Notes *NotesService 113 | Projects *ProjectsService 114 | ProjectSnippets *ProjectSnippetsService 115 | Repositories *RepositoriesService 116 | RepositoryFiles *RepositoryFilesService 117 | Services *ServicesService 118 | Session *SessionService 119 | Settings *SettingsService 120 | SystemHooks *SystemHooksService 121 | Users *UsersService 122 | } 123 | 124 | // ListOptions specifies the optional parameters to various List methods that 125 | // support pagination. 126 | type ListOptions struct { 127 | // For paginated result sets, page of results to retrieve. 128 | Page int `url:"page,omitempty" json:"page,omitempty"` 129 | 130 | // For paginated result sets, the number of results to include per page. 131 | PerPage int `url:"per_page,omitempty" json:"per_page,omitempty"` 132 | } 133 | 134 | // NewClient returns a new GitLab API client. If a nil httpClient is 135 | // provided, http.DefaultClient will be used. To use API methods which require 136 | // authentication, provide a valid private token. 137 | func NewClient(httpClient *http.Client, token string) *Client { 138 | if httpClient == nil { 139 | httpClient = http.DefaultClient 140 | } 141 | 142 | c := &Client{client: httpClient, token: token, UserAgent: userAgent} 143 | if err := c.SetBaseURL(defaultBaseURL); err != nil { 144 | // should never happen since defaultBaseURL is our constant 145 | panic(err) 146 | } 147 | 148 | c.Branches = &BranchesService{client: c} 149 | c.Commits = &CommitsService{client: c} 150 | c.DeployKeys = &DeployKeysService{client: c} 151 | c.Groups = &GroupsService{client: c} 152 | c.Issues = &IssuesService{client: c} 153 | c.Labels = &LabelsService{client: c} 154 | c.MergeRequests = &MergeRequestsService{client: c} 155 | c.Milestones = &MilestonesService{client: c} 156 | c.Notes = &NotesService{client: c} 157 | c.Namespaces = &NamespacesService{client: c} 158 | c.Projects = &ProjectsService{client: c} 159 | c.ProjectSnippets = &ProjectSnippetsService{client: c} 160 | c.Repositories = &RepositoriesService{client: c} 161 | c.RepositoryFiles = &RepositoryFilesService{client: c} 162 | c.Services = &ServicesService{client: c} 163 | c.Session = &SessionService{client: c} 164 | c.Settings = &SettingsService{client: c} 165 | c.SystemHooks = &SystemHooksService{client: c} 166 | c.Users = &UsersService{client: c} 167 | 168 | return c 169 | } 170 | 171 | // BaseURL return a copy of the baseURL. 172 | func (c *Client) BaseURL() *url.URL { 173 | u := *c.baseURL 174 | return &u 175 | } 176 | 177 | // SetBaseURL sets the base URL for API requests to a custom endpoint. urlStr 178 | // should always be specified with a trailing slash. 179 | func (c *Client) SetBaseURL(urlStr string) error { 180 | // Make sure the given URL end with a slash 181 | if !strings.HasSuffix(urlStr, "/") { 182 | urlStr += "/" 183 | } 184 | 185 | var err error 186 | c.baseURL, err = url.Parse(urlStr) 187 | if err != nil { 188 | return err 189 | } 190 | 191 | return nil 192 | } 193 | 194 | // NewRequest creates an API request. A relative URL path can be provided in 195 | // urlStr, in which case it is resolved relative to the base URL of the Client. 196 | // Relative URL paths should always be specified without a preceding slash. If 197 | // specified, the value pointed to by body is JSON encoded and included as the 198 | // request body. 199 | func (c *Client) NewRequest(method, path string, opt interface{}) (*http.Request, error) { 200 | u := *c.baseURL 201 | // Set the encoded opaque data 202 | u.Opaque = c.baseURL.Path + path 203 | 204 | q, err := query.Values(opt) 205 | if err != nil { 206 | return nil, err 207 | } 208 | u.RawQuery = q.Encode() 209 | 210 | req := &http.Request{ 211 | Method: method, 212 | URL: &u, 213 | Proto: "HTTP/1.1", 214 | ProtoMajor: 1, 215 | ProtoMinor: 1, 216 | Header: make(http.Header), 217 | Host: u.Host, 218 | } 219 | 220 | if method == "POST" || method == "PUT" { 221 | bodyBytes, err := json.Marshal(opt) 222 | if err != nil { 223 | return nil, err 224 | } 225 | bodyReader := bytes.NewReader(bodyBytes) 226 | 227 | u.RawQuery = "" 228 | req.Body = ioutil.NopCloser(bodyReader) 229 | req.ContentLength = int64(bodyReader.Len()) 230 | req.Header.Set("Content-Type", "application/json") 231 | } 232 | 233 | req.Header.Set("Accept", "application/json") 234 | req.Header.Set("PRIVATE-TOKEN", c.token) 235 | if c.UserAgent != "" { 236 | req.Header.Set("User-Agent", c.UserAgent) 237 | } 238 | 239 | return req, nil 240 | } 241 | 242 | // Response is a GitLab API response. This wraps the standard http.Response 243 | // returned from GitLab and provides convenient access to things like 244 | // pagination links. 245 | type Response struct { 246 | *http.Response 247 | 248 | // These fields provide the page values for paginating through a set of 249 | // results. Any or all of these may be set to the zero value for 250 | // responses that are not part of a paginated set, or for which there 251 | // are no additional pages. 252 | 253 | NextPage int 254 | PrevPage int 255 | FirstPage int 256 | LastPage int 257 | } 258 | 259 | // newResponse creats a new Response for the provided http.Response. 260 | func newResponse(r *http.Response) *Response { 261 | response := &Response{Response: r} 262 | response.populatePageValues() 263 | return response 264 | } 265 | 266 | // populatePageValues parses the HTTP Link response headers and populates the 267 | // various pagination link values in the Reponse. 268 | func (r *Response) populatePageValues() { 269 | if links, ok := r.Response.Header["Link"]; ok && len(links) > 0 { 270 | for _, link := range strings.Split(links[0], ",") { 271 | segments := strings.Split(strings.TrimSpace(link), ";") 272 | 273 | // link must at least have href and rel 274 | if len(segments) < 2 { 275 | continue 276 | } 277 | 278 | // ensure href is properly formatted 279 | if !strings.HasPrefix(segments[0], "<") || !strings.HasSuffix(segments[0], ">") { 280 | continue 281 | } 282 | 283 | // try to pull out page parameter 284 | url, err := url.Parse(segments[0][1 : len(segments[0])-1]) 285 | if err != nil { 286 | continue 287 | } 288 | page := url.Query().Get("page") 289 | if page == "" { 290 | continue 291 | } 292 | 293 | for _, segment := range segments[1:] { 294 | switch strings.TrimSpace(segment) { 295 | case `rel="next"`: 296 | r.NextPage, _ = strconv.Atoi(page) 297 | case `rel="prev"`: 298 | r.PrevPage, _ = strconv.Atoi(page) 299 | case `rel="first"`: 300 | r.FirstPage, _ = strconv.Atoi(page) 301 | case `rel="last"`: 302 | r.LastPage, _ = strconv.Atoi(page) 303 | } 304 | 305 | } 306 | } 307 | } 308 | } 309 | 310 | // Do sends an API request and returns the API response. The API response is 311 | // JSON decoded and stored in the value pointed to by v, or returned as an 312 | // error if an API error has occurred. If v implements the io.Writer 313 | // interface, the raw response body will be written to v, without attempting to 314 | // first decode it. 315 | func (c *Client) Do(req *http.Request, v interface{}) (*Response, error) { 316 | resp, err := c.client.Do(req) 317 | if err != nil { 318 | return nil, err 319 | } 320 | 321 | defer resp.Body.Close() 322 | 323 | response := newResponse(resp) 324 | 325 | err = CheckResponse(resp) 326 | if err != nil { 327 | // even though there was an error, we still return the response 328 | // in case the caller wants to inspect it further 329 | return response, err 330 | } 331 | 332 | if v != nil { 333 | if w, ok := v.(io.Writer); ok { 334 | _, err = io.Copy(w, resp.Body) 335 | } else { 336 | err = json.NewDecoder(resp.Body).Decode(v) 337 | } 338 | } 339 | return response, err 340 | } 341 | 342 | // Helper function to accept and format both the project ID or name as project 343 | // identifier for all API calls. 344 | func parseID(id interface{}) (string, error) { 345 | switch v := id.(type) { 346 | case int: 347 | return strconv.Itoa(v), nil 348 | case string: 349 | return v, nil 350 | default: 351 | return "", errors.New("the ID must be an int or a string") 352 | } 353 | } 354 | 355 | // An ErrorResponse reports one or more errors caused by an API request. 356 | // 357 | // GitLab API docs: 358 | // http://doc.gitlab.com/ce/api/README.html#data-validation-and-error-reporting 359 | type ErrorResponse struct { 360 | Response *http.Response // HTTP response that caused this error 361 | Message string `json:"message"` // error message 362 | Errors []Error `json:"errors"` // more detail on individual errors 363 | } 364 | 365 | func (r *ErrorResponse) Error() string { 366 | path, _ := url.QueryUnescape(r.Response.Request.URL.Opaque) 367 | ru := fmt.Sprintf("%s://%s%s", r.Response.Request.URL.Scheme, r.Response.Request.URL.Host, path) 368 | 369 | return fmt.Sprintf("%v %s: %d %v %+v", 370 | r.Response.Request.Method, ru, r.Response.StatusCode, r.Message, r.Errors) 371 | } 372 | 373 | // An Error reports more details on an individual error in an ErrorResponse. 374 | // These are the possible validation error codes: 375 | // 376 | // missing: 377 | // resource does not exist 378 | // missing_field: 379 | // a required field on a resource has not been set 380 | // invalid: 381 | // the formatting of a field is invalid 382 | // already_exists: 383 | // another resource has the same valid as this field 384 | // 385 | // GitLab API docs: 386 | // http://doc.gitlab.com/ce/api/README.html#data-validation-and-error-reporting 387 | type Error struct { 388 | Resource string `json:"resource"` // resource on which the error occurred 389 | Field string `json:"field"` // field on which the error occurred 390 | Code string `json:"code"` // validation error code 391 | } 392 | 393 | func (e *Error) Error() string { 394 | return fmt.Sprintf("%v error caused by %v field on %v resource", 395 | e.Code, e.Field, e.Resource) 396 | } 397 | 398 | // CheckResponse checks the API response for errors, and returns them if 399 | // present. A response is considered an error if it has a status code outside 400 | // the 200 range. API error responses are expected to have either no response 401 | // body, or a JSON response body that maps to ErrorResponse. Any other 402 | // response body will be silently ignored. 403 | func CheckResponse(r *http.Response) error { 404 | if c := r.StatusCode; 200 <= c && c <= 299 { 405 | return nil 406 | } 407 | errorResponse := &ErrorResponse{Response: r} 408 | data, err := ioutil.ReadAll(r.Body) 409 | if err == nil && data != nil { 410 | json.Unmarshal(data, errorResponse) 411 | } 412 | return errorResponse 413 | } 414 | 415 | // Bool is a helper routine that allocates a new bool value 416 | // to store v and returns a pointer to it. 417 | func Bool(v bool) *bool { 418 | p := new(bool) 419 | *p = v 420 | return p 421 | } 422 | 423 | // Int is a helper routine that allocates a new int32 value 424 | // to store v and returns a pointer to it, but unlike Int32 425 | // its argument value is an int. 426 | func Int(v int) *int { 427 | p := new(int) 428 | *p = v 429 | return p 430 | } 431 | 432 | // String is a helper routine that allocates a new string value 433 | // to store v and returns a pointer to it. 434 | func String(v string) *string { 435 | p := new(string) 436 | *p = v 437 | return p 438 | } 439 | -------------------------------------------------------------------------------- /api/gitlab_test.go: -------------------------------------------------------------------------------- 1 | package gitlab 2 | 3 | import ( 4 | "encoding/json" 5 | "io/ioutil" 6 | "net/http" 7 | "net/http/httptest" 8 | "net/url" 9 | "reflect" 10 | "strings" 11 | "testing" 12 | ) 13 | 14 | // setup sets up a test HTTP server along with a gitlab.Client that is 15 | // configured to talk to that test server. Tests should register handlers on 16 | // mux which provide mock responses for the API method being tested. 17 | func setup() (*http.ServeMux, *httptest.Server, *Client) { 18 | // mux is the HTTP request multiplexer used with the test server. 19 | mux := http.NewServeMux() 20 | 21 | // server is a test HTTP server used to provide mock API responses. 22 | server := httptest.NewServer(mux) 23 | 24 | // client is the Gitlab client being tested. 25 | client := NewClient(nil, "") 26 | client.SetBaseURL(server.URL) 27 | 28 | return mux, server, client 29 | } 30 | 31 | // teardown closes the test HTTP server. 32 | func teardown(server *httptest.Server) { 33 | server.Close() 34 | } 35 | 36 | func testUrl(t *testing.T, r *http.Request, want string) { 37 | if got := r.RequestURI; got != want { 38 | t.Errorf("Request url: %+v, want %s", got, want) 39 | } 40 | } 41 | 42 | func testMethod(t *testing.T, r *http.Request, want string) { 43 | if got := r.Method; got != want { 44 | t.Errorf("Request method: %s, want %s", got, want) 45 | } 46 | } 47 | 48 | type values map[string]string 49 | 50 | func testFormValues(t *testing.T, r *http.Request, values values) { 51 | want := url.Values{} 52 | for k, v := range values { 53 | want.Add(k, v) 54 | } 55 | 56 | r.ParseForm() 57 | if got := r.Form; !reflect.DeepEqual(got, want) { 58 | t.Errorf("Request parameters: %v, want %v", got, want) 59 | } 60 | } 61 | 62 | func testHeader(t *testing.T, r *http.Request, header string, want string) { 63 | if got := r.Header.Get(header); got != want { 64 | t.Errorf("Header.Get(%q) returned %s, want %s", header, got, want) 65 | } 66 | } 67 | 68 | func testBody(t *testing.T, r *http.Request, want string) { 69 | b, err := ioutil.ReadAll(r.Body) 70 | if err != nil { 71 | t.Errorf("Error reading request body: %v", err) 72 | } 73 | if got := string(b); got != want { 74 | t.Errorf("request Body is %s, want %s", got, want) 75 | } 76 | } 77 | 78 | func testJsonBody(t *testing.T, r *http.Request, want values) { 79 | b, err := ioutil.ReadAll(r.Body) 80 | if err != nil { 81 | t.Errorf("Error reading request body: %v", err) 82 | } 83 | 84 | var got values 85 | json.Unmarshal(b, &got) 86 | 87 | if !reflect.DeepEqual(got, want) { 88 | t.Errorf("Request parameters: %v, want %v", got, want) 89 | } 90 | } 91 | 92 | func responseBody(w http.ResponseWriter, filename string) { 93 | body, _ := ioutil.ReadFile(filename) 94 | w.Write([]byte(body)) 95 | } 96 | 97 | func TestNewClient(t *testing.T) { 98 | c := NewClient(nil, "") 99 | 100 | if c.BaseURL().String() != defaultBaseURL { 101 | t.Errorf("NewClient BaseURL is %s, want %s", c.BaseURL().String(), defaultBaseURL) 102 | } 103 | if c.UserAgent != userAgent { 104 | t.Errorf("NewClient UserAgent is %s, want %s", c.UserAgent, userAgent) 105 | } 106 | } 107 | 108 | func TestCheckResponse(t *testing.T) { 109 | res := &http.Response{ 110 | Request: &http.Request{}, 111 | StatusCode: http.StatusBadRequest, 112 | Body: ioutil.NopCloser(strings.NewReader(`{"message":"m", 113 | "errors": [{"resource": "r", "field": "f", "code": "c"}]}`)), 114 | } 115 | err := CheckResponse(res).(*ErrorResponse) 116 | 117 | if err == nil { 118 | t.Errorf("Expected error response.") 119 | } 120 | 121 | want := &ErrorResponse{ 122 | Response: res, 123 | Message: "m", 124 | Errors: []Error{{Resource: "r", Field: "f", Code: "c"}}, 125 | } 126 | if !reflect.DeepEqual(err, want) { 127 | t.Errorf("Error = %#v, want %#v", err, want) 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /api/groups.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2015, Sander van Harmelen 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | package gitlab 18 | 19 | import ( 20 | "fmt" 21 | "time" 22 | ) 23 | 24 | // GroupsService handles communication with the group related methods of 25 | // the GitLab API. 26 | // 27 | // GitLab API docs: http://doc.gitlab.com/ce/api/groups.html 28 | type GroupsService struct { 29 | client *Client 30 | } 31 | 32 | // Group represents a GitLab group. 33 | // 34 | // GitLab API docs: http://doc.gitlab.com/ce/api/groups.html 35 | type Group struct { 36 | ID int `json:"id"` 37 | Name string `json:"name"` 38 | Path string `json:"path"` 39 | Description string `json:"description"` 40 | Projects *[]Project `json:"projects,omitempty"` 41 | } 42 | 43 | // ListGroupsOptions represents the available ListGroups() options. 44 | // 45 | // GitLab API docs: http://doc.gitlab.com/ce/api/groups.html#list-project-groups 46 | type ListGroupsOptions struct { 47 | ListOptions 48 | Search string `url:"search,omitempty" json:"search,omitempty"` 49 | } 50 | 51 | // ListGroups gets a list of groups. (As user: my groups, as admin: all groups) 52 | // 53 | // GitLab API docs: 54 | // http://doc.gitlab.com/ce/api/groups.html#list-project-groups 55 | func (s *GroupsService) ListGroups(opt *ListGroupsOptions) ([]*Group, *Response, error) { 56 | req, err := s.client.NewRequest("GET", "groups", opt) 57 | if err != nil { 58 | return nil, nil, err 59 | } 60 | 61 | var g []*Group 62 | resp, err := s.client.Do(req, &g) 63 | if err != nil { 64 | return nil, resp, err 65 | } 66 | 67 | return g, resp, err 68 | } 69 | 70 | // GetGroup gets all details of a group. 71 | // 72 | // GitLab API docs: http://doc.gitlab.com/ce/api/groups.html#details-of-a-group 73 | func (s *GroupsService) GetGroup(gid interface{}) (*Group, *Response, error) { 74 | group, err := parseID(gid) 75 | if err != nil { 76 | return nil, nil, err 77 | } 78 | u := fmt.Sprintf("groups/%s", group) 79 | 80 | req, err := s.client.NewRequest("GET", u, nil) 81 | if err != nil { 82 | return nil, nil, err 83 | } 84 | 85 | g := new(Group) 86 | resp, err := s.client.Do(req, g) 87 | if err != nil { 88 | return nil, resp, err 89 | } 90 | 91 | return g, resp, err 92 | } 93 | 94 | // CreateGroupOptions represents the available CreateGroup() options. 95 | // 96 | // GitLab API docs: http://doc.gitlab.com/ce/api/groups.html#new-group 97 | type CreateGroupOptions struct { 98 | Name string `url:"name,omitempty" json:"name,omitempty"` 99 | Path string `url:"path,omitempty" json:"path,omitempty"` 100 | Description string `url:"description,omitempty" json:"description,omitempty"` 101 | } 102 | 103 | // CreateGroup creates a new project group. Available only for users who can 104 | // create groups. 105 | // 106 | // GitLab API docs: http://doc.gitlab.com/ce/api/groups.html#new-group 107 | func (s *GroupsService) CreateGroup(opt *CreateGroupOptions) (*Group, *Response, error) { 108 | req, err := s.client.NewRequest("POST", "groups", opt) 109 | if err != nil { 110 | return nil, nil, err 111 | } 112 | 113 | g := new(Group) 114 | resp, err := s.client.Do(req, g) 115 | if err != nil { 116 | return nil, resp, err 117 | } 118 | 119 | return g, resp, err 120 | } 121 | 122 | // TransferGroup transfers a project to the Group namespace. Available only 123 | // for admin. 124 | // 125 | // GitLab API docs: 126 | // http://doc.gitlab.com/ce/api/groups.html#transfer-project-to-group 127 | func (s *GroupsService) TransferGroup(gid interface{}, project int) (*Group, *Response, error) { 128 | group, err := parseID(gid) 129 | if err != nil { 130 | return nil, nil, err 131 | } 132 | u := fmt.Sprintf("groups/%s/projects/%d", group, project) 133 | 134 | req, err := s.client.NewRequest("POST", u, nil) 135 | if err != nil { 136 | return nil, nil, err 137 | } 138 | 139 | g := new(Group) 140 | resp, err := s.client.Do(req, g) 141 | if err != nil { 142 | return nil, resp, err 143 | } 144 | 145 | return g, resp, err 146 | } 147 | 148 | // DeleteGroup removes group with all projects inside. 149 | // 150 | // GitLab API docs: http://doc.gitlab.com/ce/api/groups.html#remove-group 151 | func (s *GroupsService) DeleteGroup(gid interface{}) (*Response, error) { 152 | group, err := parseID(gid) 153 | if err != nil { 154 | return nil, err 155 | } 156 | u := fmt.Sprintf("groups/%s", group) 157 | 158 | req, err := s.client.NewRequest("DELETE", u, nil) 159 | if err != nil { 160 | return nil, err 161 | } 162 | 163 | resp, err := s.client.Do(req, nil) 164 | if err != nil { 165 | return resp, err 166 | } 167 | 168 | return resp, err 169 | } 170 | 171 | // SearchGroup get all groups that match your string in their name or path. 172 | // 173 | // GitLab API docs: http://doc.gitlab.com/ce/api/groups.html#search-for-group 174 | func (s *GroupsService) SearchGroup(query string) ([]*Group, *Response, error) { 175 | var q struct { 176 | Search string `url:"search,omitempty" json:"search,omitempty"` 177 | } 178 | q.Search = query 179 | 180 | req, err := s.client.NewRequest("GET", "groups", &q) 181 | if err != nil { 182 | return nil, nil, err 183 | } 184 | 185 | var g []*Group 186 | resp, err := s.client.Do(req, &g) 187 | if err != nil { 188 | return nil, resp, err 189 | } 190 | 191 | return g, resp, err 192 | } 193 | 194 | // GroupMember represents a GitLab group member. 195 | // 196 | // GitLab API docs: http://doc.gitlab.com/ce/api/groups.html 197 | type GroupMember struct { 198 | ID int `json:"id"` 199 | Username string `json:"username"` 200 | Email string `json:"email"` 201 | Name string `json:"name"` 202 | State string `json:"state"` 203 | CreatedAt time.Time `json:"created_at"` 204 | AccessLevel int `json:"access_level"` 205 | } 206 | 207 | // ListGroupMembers get a list of group members viewable by the authenticated 208 | // user. 209 | // 210 | // GitLab API docs: 211 | // http://doc.gitlab.com/ce/api/groups.html#list-group-members 212 | func (s *GroupsService) ListGroupMembers(gid interface{}) ([]*GroupMember, *Response, error) { 213 | group, err := parseID(gid) 214 | if err != nil { 215 | return nil, nil, err 216 | } 217 | u := fmt.Sprintf("groups/%s/members", group) 218 | 219 | req, err := s.client.NewRequest("GET", u, nil) 220 | if err != nil { 221 | return nil, nil, err 222 | } 223 | 224 | var g []*GroupMember 225 | resp, err := s.client.Do(req, &g) 226 | if err != nil { 227 | return nil, resp, err 228 | } 229 | 230 | return g, resp, err 231 | } 232 | 233 | // AddGroupMemberOptions represents the available AddGroupMember() options. 234 | // 235 | // GitLab API docs: http://doc.gitlab.com/ce/api/groups.html#add-group-member 236 | type AddGroupMemberOptions struct { 237 | UserID int `url:"user_id,omitempty" json:"user_id,omitempty"` 238 | AccessLevel AccessLevel `url:"access_level,omitempty" json:"access_level,omitempty"` 239 | } 240 | 241 | // AddGroupMember adds a user to the list of group members. 242 | // 243 | // GitLab API docs: 244 | // http://doc.gitlab.com/ce/api/groups.html#list-group-members 245 | func (s *GroupsService) AddGroupMember( 246 | gid interface{}, 247 | opt *AddGroupMemberOptions) (*GroupMember, *Response, error) { 248 | group, err := parseID(gid) 249 | if err != nil { 250 | return nil, nil, err 251 | } 252 | u := fmt.Sprintf("groups/%s/members", group) 253 | 254 | req, err := s.client.NewRequest("POST", u, opt) 255 | if err != nil { 256 | return nil, nil, err 257 | } 258 | 259 | g := new(GroupMember) 260 | resp, err := s.client.Do(req, g) 261 | if err != nil { 262 | return nil, resp, err 263 | } 264 | 265 | return g, resp, err 266 | } 267 | 268 | // UpdateGroupMemberOptions represents the available UpdateGroupMember() 269 | // options. 270 | // 271 | // GitLab API docs: 272 | // http://doc.gitlab.com/ce/api/groups.html#edit-group-team-member 273 | type UpdateGroupMemberOptions struct { 274 | AccessLevel AccessLevel `url:"access_level,omitempty" json:"access_level,omitempty"` 275 | } 276 | 277 | // UpdateGroupMember updates a group team member to a specified access level. 278 | // 279 | // GitLab API docs: 280 | // http://doc.gitlab.com/ce/api/groups.html#list-group-members 281 | func (s *GroupsService) UpdateGroupMember( 282 | gid interface{}, 283 | user int, 284 | opt *UpdateGroupMemberOptions) (*GroupMember, *Response, error) { 285 | group, err := parseID(gid) 286 | if err != nil { 287 | return nil, nil, err 288 | } 289 | u := fmt.Sprintf("groups/%s/members/%d", group, user) 290 | 291 | req, err := s.client.NewRequest("PUT", u, opt) 292 | if err != nil { 293 | return nil, nil, err 294 | } 295 | 296 | g := new(GroupMember) 297 | resp, err := s.client.Do(req, g) 298 | if err != nil { 299 | return nil, resp, err 300 | } 301 | 302 | return g, resp, err 303 | } 304 | 305 | // RemoveGroupMember removes user from user team. 306 | // 307 | // GitLab API docs: 308 | // http://doc.gitlab.com/ce/api/groups.html#remove-user-from-user-team 309 | func (s *GroupsService) RemoveGroupMember(gid interface{}, user int) (*Response, error) { 310 | group, err := parseID(gid) 311 | if err != nil { 312 | return nil, err 313 | } 314 | u := fmt.Sprintf("groups/%s/members/%d", group, user) 315 | 316 | req, err := s.client.NewRequest("DELETE", u, nil) 317 | if err != nil { 318 | return nil, err 319 | } 320 | 321 | resp, err := s.client.Do(req, nil) 322 | if err != nil { 323 | return resp, err 324 | } 325 | 326 | return resp, err 327 | } 328 | -------------------------------------------------------------------------------- /api/issues.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2015, Sander van Harmelen 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | package gitlab 18 | 19 | import ( 20 | "fmt" 21 | "log" 22 | "net/url" 23 | "strings" 24 | "time" 25 | ) 26 | 27 | // IssuesService handles communication with the issue related methods 28 | // of the GitLab API. 29 | // 30 | // GitLab API docs: http://doc.gitlab.com/ce/api/issues.html 31 | type IssuesService struct { 32 | client *Client 33 | } 34 | 35 | // Issue represents a GitLab issue. 36 | // 37 | // GitLab API docs: http://doc.gitlab.com/ce/api/issues.html 38 | type Issue struct { 39 | ID int `json:"id"` 40 | IID int `json:"iid"` 41 | ProjectID int `json:"project_id"` 42 | Title string `json:"title"` 43 | Description string `json:"description"` 44 | Labels []string `json:"labels"` 45 | Milestone struct { 46 | ID int `json:"id"` 47 | Title string `json:"title"` 48 | Description string `json:"description"` 49 | DueDate string `json:"due_date"` 50 | State string `json:"state"` 51 | UpdatedAt time.Time `json:"updated_at"` 52 | CreatedAt time.Time `json:"created_at"` 53 | } `json:"milestone"` 54 | Assignee struct { 55 | ID int `json:"id"` 56 | Username string `json:"username"` 57 | Email string `json:"email"` 58 | Name string `json:"name"` 59 | State string `json:"state"` 60 | CreatedAt time.Time `json:"created_at"` 61 | } `json:"assignee"` 62 | Author struct { 63 | ID int `json:"id"` 64 | Username string `json:"username"` 65 | Email string `json:"email"` 66 | Name string `json:"name"` 67 | State string `json:"state"` 68 | CreatedAt time.Time `json:"created_at"` 69 | } `json:"author"` 70 | State string `json:"state"` 71 | UpdatedAt time.Time `json:"updated_at"` 72 | CreatedAt time.Time `json:"created_at"` 73 | } 74 | 75 | func (i Issue) String() string { 76 | return Stringify(i) 77 | } 78 | 79 | // ListIssuesOptions represents the available ListIssues() options. 80 | // 81 | // GitLab API docs: http://doc.gitlab.com/ce/api/issues.html#list-issues 82 | type ListIssuesOptions struct { 83 | ListOptions 84 | State string `url:"state,omitempty" json:"state,omitempty"` 85 | Labels []string `url:"labels,omitempty" json:"labels,omitempty"` 86 | OrderBy string `url:"order_by,omitempty" json:"order_by,omitempty"` 87 | Sort string `url:"sort,omitempty" json:"sort,omitempty"` 88 | } 89 | 90 | // ListIssues gets all issues created by authenticated user. This function 91 | // takes pagination parameters page and per_page to restrict the list of issues. 92 | // 93 | // GitLab API docs: http://doc.gitlab.com/ce/api/issues.html#list-issues 94 | func (s *IssuesService) ListIssues(opt *ListIssuesOptions) ([]*Issue, *Response, error) { 95 | req, err := s.client.NewRequest("GET", "issues", opt) 96 | if err != nil { 97 | return nil, nil, err 98 | } 99 | 100 | var i []*Issue 101 | resp, err := s.client.Do(req, &i) 102 | if err != nil { 103 | return nil, resp, err 104 | } 105 | 106 | return i, resp, err 107 | } 108 | 109 | // ListProjectIssuesOptions represents the available ListProjectIssues() options. 110 | // 111 | // GitLab API docs: http://doc.gitlab.com/ce/api/issues.html#list-issues 112 | type ListProjectIssuesOptions struct { 113 | ListOptions 114 | IID int `url:"iid,omitempty" json:"iid,omitempty"` 115 | State string `url:"state,omitempty" json:"state,omitempty"` 116 | Labels []string `url:"labels,omitempty" json:"labels,omitempty"` 117 | Milestone string `url:"milestone,omitempty" json:"milestone,omitempty"` 118 | OrderBy string `url:"order_by,omitempty" json:"order_by,omitempty"` 119 | Sort string `url:"sort,omitempty" json:"sort,omitempty"` 120 | } 121 | 122 | // ListProjectIssues gets a list of project issues. This function accepts 123 | // pagination parameters page and per_page to return the list of project issues. 124 | // 125 | // GitLab API docs: http://doc.gitlab.com/ce/api/issues.html#list-project-issues 126 | func (s *IssuesService) ListProjectIssues( 127 | pid interface{}, 128 | opt *ListProjectIssuesOptions) ([]*Issue, *Response, error) { 129 | project, err := parseID(pid) 130 | if err != nil { 131 | return nil, nil, err 132 | } 133 | u := fmt.Sprintf("projects/%s/issues", url.QueryEscape(project)) 134 | 135 | req, err := s.client.NewRequest("GET", u, opt) 136 | if err != nil { 137 | return nil, nil, err 138 | } 139 | 140 | var i []*Issue 141 | resp, err := s.client.Do(req, &i) 142 | if err != nil { 143 | return nil, resp, err 144 | } 145 | 146 | return i, resp, err 147 | } 148 | 149 | // GetIssue gets a single project issue. 150 | // 151 | // GitLab API docs: http://doc.gitlab.com/ce/api/issues.html#single-issues 152 | func (s *IssuesService) GetIssue(pid interface{}, issue int) (*Issue, *Response, error) { 153 | project, err := parseID(pid) 154 | if err != nil { 155 | return nil, nil, err 156 | } 157 | u := fmt.Sprintf("projects/%s/issues/%d", url.QueryEscape(project), issue) 158 | 159 | req, err := s.client.NewRequest("GET", u, nil) 160 | if err != nil { 161 | return nil, nil, err 162 | } 163 | 164 | i := new(Issue) 165 | resp, err := s.client.Do(req, i) 166 | if err != nil { 167 | return nil, resp, err 168 | } 169 | 170 | return i, resp, err 171 | } 172 | 173 | // CreateIssueOptions represents the available CreateIssue() options. 174 | // 175 | // GitLab API docs: http://doc.gitlab.com/ce/api/issues.html#new-issues 176 | type CreateIssueOptions struct { 177 | Title string `url:"title,omitempty" json:"title,omitempty"` 178 | Description string `url:"description,omitempty" json:"description,omitempty"` 179 | AssigneeID int `url:"assignee_id,omitempty" json:"assignee_id,omitempty"` 180 | MilestoneID int `url:"milestone_id,omitempty" json:"milestone_id,omitempty"` 181 | Labels []string `url:"labels,omitempty" json:"labels,omitempty"` 182 | } 183 | 184 | // CreateIssue creates a new project issue. 185 | // 186 | // GitLab API docs: http://doc.gitlab.com/ce/api/issues.html#new-issues 187 | func (s *IssuesService) CreateIssue( 188 | pid interface{}, 189 | opt *CreateIssueOptions) (*Issue, *Response, error) { 190 | project, err := parseID(pid) 191 | if err != nil { 192 | return nil, nil, err 193 | } 194 | u := fmt.Sprintf("projects/%s/issues", url.QueryEscape(project)) 195 | 196 | // This is needed to get a single, comma separated string 197 | opt.Labels = []string{strings.Join(opt.Labels, ",")} 198 | 199 | req, err := s.client.NewRequest("POST", u, opt) 200 | if err != nil { 201 | return nil, nil, err 202 | } 203 | 204 | log.Printf("req: %#+v\n", req.URL) 205 | 206 | i := new(Issue) 207 | resp, err := s.client.Do(req, i) 208 | if err != nil { 209 | return nil, resp, err 210 | } 211 | 212 | return i, resp, err 213 | } 214 | 215 | // UpdateIssueOptions represents the available UpdateIssue() options. 216 | // 217 | // GitLab API docs: http://doc.gitlab.com/ce/api/issues.html#edit-issues 218 | type UpdateIssueOptions struct { 219 | Title string `url:"title,omitempty" json:"title,omitempty"` 220 | Description string `url:"description,omitempty" json:"description,omitempty"` 221 | AssigneeID int `url:"assignee_id,omitempty" json:"assignee_id,omitempty"` 222 | MilestoneID int `url:"milestone_id,omitempty" json:"milestone_id,omitempty"` 223 | Labels []string `url:"labels,omitempty" json:"labels,omitempty"` 224 | StateEvent string `url:"state_event,omitempty" json:"state_event,omitempty"` 225 | } 226 | 227 | // UpdateIssue updates an existing project issue. This function is also used 228 | // to mark an issue as closed. 229 | // 230 | // GitLab API docs: http://doc.gitlab.com/ce/api/issues.html#edit-issues 231 | func (s *IssuesService) UpdateIssue( 232 | pid interface{}, 233 | issue int, 234 | opt *UpdateIssueOptions) (*Issue, *Response, error) { 235 | project, err := parseID(pid) 236 | if err != nil { 237 | return nil, nil, err 238 | } 239 | u := fmt.Sprintf("projects/%s/issues/%d", url.QueryEscape(project), issue) 240 | 241 | // This is needed to get a single, comma separated string 242 | opt.Labels = []string{strings.Join(opt.Labels, ",")} 243 | 244 | req, err := s.client.NewRequest("PUT", u, opt) 245 | if err != nil { 246 | return nil, nil, err 247 | } 248 | 249 | i := new(Issue) 250 | resp, err := s.client.Do(req, i) 251 | if err != nil { 252 | return nil, resp, err 253 | } 254 | 255 | return i, resp, err 256 | } 257 | -------------------------------------------------------------------------------- /api/labels.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2015, Sander van Harmelen 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | package gitlab 18 | 19 | import ( 20 | "fmt" 21 | "net/url" 22 | ) 23 | 24 | // LabelsService handles communication with the label related methods 25 | // of the GitLab API. 26 | // 27 | // GitLab API docs: http://doc.gitlab.com/ce/api/labels.html 28 | type LabelsService struct { 29 | client *Client 30 | } 31 | 32 | // Label represents a GitLab label. 33 | // 34 | // GitLab API docs: http://doc.gitlab.com/ce/api/labels.html 35 | type Label struct { 36 | Name string `json:"name"` 37 | Color string `json:"color"` 38 | } 39 | 40 | func (l Label) String() string { 41 | return Stringify(l) 42 | } 43 | 44 | // ListLabels gets all labels for given project. 45 | // 46 | // GitLab API docs: http://doc.gitlab.com/ce/api/labels.html#list-labels 47 | func (s *LabelsService) ListLabels(pid interface{}) ([]*Label, *Response, error) { 48 | project, err := parseID(pid) 49 | if err != nil { 50 | return nil, nil, err 51 | } 52 | u := fmt.Sprintf("projects/%s/labels", url.QueryEscape(project)) 53 | 54 | req, err := s.client.NewRequest("GET", u, nil) 55 | if err != nil { 56 | return nil, nil, err 57 | } 58 | 59 | var l []*Label 60 | resp, err := s.client.Do(req, &l) 61 | if err != nil { 62 | return nil, resp, err 63 | } 64 | 65 | return l, resp, err 66 | } 67 | 68 | // CreateLabelOptions represents the available CreateLabel() options. 69 | // 70 | // GitLab API docs: http://doc.gitlab.com/ce/api/labels.html#create-a-new-label 71 | type CreateLabelOptions struct { 72 | Name string `url:"name,omitempty" json:"name,omitempty"` 73 | Color string `url:"color,omitempty" json:"color,omitempty"` 74 | } 75 | 76 | // CreateLabel creates a new label for given repository with given name and 77 | // color. 78 | // 79 | // GitLab API docs: http://doc.gitlab.com/ce/api/labels.html#create-a-new-label 80 | func (s *LabelsService) CreateLabel( 81 | pid interface{}, 82 | opt *CreateLabelOptions) (*Label, *Response, error) { 83 | project, err := parseID(pid) 84 | if err != nil { 85 | return nil, nil, err 86 | } 87 | u := fmt.Sprintf("projects/%s/labels", url.QueryEscape(project)) 88 | 89 | req, err := s.client.NewRequest("POST", u, opt) 90 | if err != nil { 91 | return nil, nil, err 92 | } 93 | 94 | l := new(Label) 95 | resp, err := s.client.Do(req, l) 96 | if err != nil { 97 | return nil, resp, err 98 | } 99 | 100 | return l, resp, err 101 | } 102 | 103 | // DeleteLabelOptions represents the available DeleteLabel() options. 104 | // 105 | // GitLab API docs: http://doc.gitlab.com/ce/api/labels.html#delete-a-label 106 | type DeleteLabelOptions struct { 107 | Name string `url:"name,omitempty" json:"name,omitempty"` 108 | } 109 | 110 | // DeleteLabel deletes a label given by its name. 111 | // 112 | // GitLab API docs: http://doc.gitlab.com/ce/api/labels.html#delete-a-label 113 | func (s *LabelsService) DeleteLabel(pid interface{}, opt *DeleteLabelOptions) (*Response, error) { 114 | project, err := parseID(pid) 115 | if err != nil { 116 | return nil, err 117 | } 118 | u := fmt.Sprintf("projects/%s/labels", url.QueryEscape(project)) 119 | 120 | req, err := s.client.NewRequest("DELETE", u, opt) 121 | if err != nil { 122 | return nil, err 123 | } 124 | 125 | resp, err := s.client.Do(req, nil) 126 | if err != nil { 127 | return resp, err 128 | } 129 | 130 | return resp, err 131 | } 132 | 133 | // UpdateLabelOptions represents the available UpdateLabel() options. 134 | // 135 | // GitLab API docs: http://doc.gitlab.com/ce/api/labels.html#delete-a-label 136 | type UpdateLabelOptions struct { 137 | Name string `url:"name,omitempty" json:"name,omitempty"` 138 | NewName string `url:"new_name,omitempty" json:"new_name,omitempty"` 139 | Color string `url:"color,omitempty" json:"color,omitempty"` 140 | } 141 | 142 | // UpdateLabel updates an existing label with new name or now color. At least 143 | // one parameter is required, to update the label. 144 | // 145 | // GitLab API docs: http://doc.gitlab.com/ce/api/labels.html#edit-an-existing-label 146 | func (s *LabelsService) UpdateLabel( 147 | pid interface{}, 148 | opt *UpdateLabelOptions) (*Label, *Response, error) { 149 | project, err := parseID(pid) 150 | if err != nil { 151 | return nil, nil, err 152 | } 153 | u := fmt.Sprintf("projects/%s/labels", url.QueryEscape(project)) 154 | 155 | req, err := s.client.NewRequest("PUT", u, opt) 156 | if err != nil { 157 | return nil, nil, err 158 | } 159 | 160 | l := new(Label) 161 | resp, err := s.client.Do(req, l) 162 | if err != nil { 163 | return nil, resp, err 164 | } 165 | 166 | return l, resp, err 167 | } 168 | -------------------------------------------------------------------------------- /api/merge_requests.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2015, Sander van Harmelen 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | package gitlab 18 | 19 | import ( 20 | "fmt" 21 | "net/url" 22 | "time" 23 | ) 24 | 25 | // MergeRequestsService handles communication with the merge requests related 26 | // methods of the GitLab API. 27 | // 28 | // GitLab API docs: http://doc.gitlab.com/ce/api/merge_requests.html 29 | type MergeRequestsService struct { 30 | client *Client 31 | } 32 | 33 | // MergeRequest represents a GitLab merge request. 34 | // 35 | // GitLab API docs: http://doc.gitlab.com/ce/api/merge_requests.html 36 | type MergeRequest struct { 37 | ID int `json:"id"` 38 | IID int `json:"iid"` 39 | ProjectID int `json:"project_id"` 40 | Title string `json:"title"` 41 | Description string `json:"description"` 42 | WorkInProgress bool `json:"work_in_progress"` 43 | State string `json:"state"` 44 | CreatedAt time.Time `json:"created_at"` 45 | UpdatedAt time.Time `json:"updated_at"` 46 | TargetBranch string `json:"target_branch"` 47 | SourceBranch string `json:"source_branch"` 48 | Upvotes int `json:"upvotes"` 49 | Downvotes int `json:"downvotes"` 50 | Author struct { 51 | Name string `json:"name"` 52 | Username string `json:"username"` 53 | ID int `json:"id"` 54 | State string `json:"state"` 55 | AvatarURL string `json:"avatar_url"` 56 | } `json:"author"` 57 | Assignee struct { 58 | Name string `json:"name"` 59 | Username string `json:"username"` 60 | ID int `json:"id"` 61 | State string `json:"state"` 62 | AvatarURL string `json:"avatar_url"` 63 | } `json:"assignee"` 64 | SourceProjectID int `json:"source_project_id"` 65 | TargetProjectID int `json:"target_project_id"` 66 | Labels []string `json:"labels"` 67 | Milestone struct { 68 | ID int `json:"id"` 69 | Iid int `json:"iid"` 70 | ProjectID int `json:"project_id"` 71 | Title string `json:"title"` 72 | Description string `json:"description"` 73 | State string `json:"state"` 74 | CreatedAt time.Time `json:"created_at"` 75 | UpdatedAt time.Time `json:"updated_at"` 76 | DueDate string `json:"due_date"` 77 | } `json:"milestone"` 78 | Files []struct { 79 | OldPath string `json:"old_path"` 80 | NewPath string `json:"new_path"` 81 | AMode string `json:"a_mode"` 82 | BMode string `json:"b_mode"` 83 | Diff string `json:"diff"` 84 | NewFile bool `json:"new_file"` 85 | RenamedFile bool `json:"renamed_file"` 86 | DeletedFile bool `json:"deleted_file"` 87 | } `json:"files"` 88 | } 89 | 90 | func (m MergeRequest) String() string { 91 | return Stringify(m) 92 | } 93 | 94 | // ListMergeRequestsOptions represents the available ListMergeRequests() 95 | // options. 96 | // 97 | // GitLab API docs: 98 | // http://doc.gitlab.com/ce/api/merge_requests.html#list-merge-requests 99 | type ListMergeRequestsOptions struct { 100 | ListOptions 101 | IID int `url:"iid,omitempty" json:"iid,omitempty"` 102 | State string `url:"state,omitempty" json:"state,omitempty"` 103 | OrderBy string `url:"order_by,omitempty" json:"order_by,omitempty"` 104 | Sort string `url:"sort,omitempty" json:"sort,omitempty"` 105 | } 106 | 107 | // ListMergeRequests gets all merge requests for this project. The state 108 | // parameter can be used to get only merge requests with a given state (opened, 109 | // closed, or merged) or all of them (all). The pagination parameters page and 110 | // per_page can be used to restrict the list of merge requests. 111 | // 112 | // GitLab API docs: 113 | // http://doc.gitlab.com/ce/api/merge_requests.html#list-merge-requests 114 | func (s *MergeRequestsService) ListMergeRequests( 115 | pid interface{}, 116 | opt *ListMergeRequestsOptions) ([]*MergeRequest, *Response, error) { 117 | project, err := parseID(pid) 118 | if err != nil { 119 | return nil, nil, err 120 | } 121 | u := fmt.Sprintf("projects/%s/merge_requests", url.QueryEscape(project)) 122 | 123 | req, err := s.client.NewRequest("GET", u, opt) 124 | if err != nil { 125 | return nil, nil, err 126 | } 127 | 128 | var m []*MergeRequest 129 | resp, err := s.client.Do(req, &m) 130 | if err != nil { 131 | return nil, resp, err 132 | } 133 | 134 | return m, resp, err 135 | } 136 | 137 | // GetMergeRequest shows information about a single merge request. 138 | // 139 | // GitLab API docs: 140 | // http://doc.gitlab.com/ce/api/merge_requests.html#get-single-mr 141 | func (s *MergeRequestsService) GetMergeRequest( 142 | pid interface{}, 143 | mergeRequest int) (*MergeRequest, *Response, error) { 144 | project, err := parseID(pid) 145 | if err != nil { 146 | return nil, nil, err 147 | } 148 | u := fmt.Sprintf("projects/%s/merge_request/%d", url.QueryEscape(project), mergeRequest) 149 | 150 | req, err := s.client.NewRequest("GET", u, nil) 151 | if err != nil { 152 | return nil, nil, err 153 | } 154 | 155 | m := new(MergeRequest) 156 | resp, err := s.client.Do(req, m) 157 | if err != nil { 158 | return nil, resp, err 159 | } 160 | 161 | return m, resp, err 162 | } 163 | 164 | // GetMergeRequestChanges shows information about the merge request including 165 | // its files and changes. 166 | // 167 | // GitLab API docs: 168 | // http://doc.gitlab.com/ce/api/merge_requests.html#get-single-mr-changes 169 | func (s *MergeRequestsService) GetMergeRequestChanges( 170 | pid interface{}, 171 | mergeRequest int) (*MergeRequest, *Response, error) { 172 | project, err := parseID(pid) 173 | if err != nil { 174 | return nil, nil, err 175 | } 176 | u := fmt.Sprintf("projects/%s/merge_request/%d/changes", url.QueryEscape(project), mergeRequest) 177 | 178 | req, err := s.client.NewRequest("GET", u, nil) 179 | if err != nil { 180 | return nil, nil, err 181 | } 182 | 183 | m := new(MergeRequest) 184 | resp, err := s.client.Do(req, m) 185 | if err != nil { 186 | return nil, resp, err 187 | } 188 | 189 | return m, resp, err 190 | } 191 | 192 | // CreateMergeRequestOptions represents the available CreateMergeRequest() 193 | // options. 194 | // 195 | // GitLab API docs: 196 | // http://doc.gitlab.com/ce/api/merge_requests.html#create-mr 197 | type CreateMergeRequestOptions struct { 198 | Title string `url:"title,omitempty" json:"title,omitempty"` 199 | Description string `url:"description,omitempty" json:"description,omitempty"` 200 | SourceBranch string `url:"source_branch,omitemtpy" json:"source_branch,omitemtpy"` 201 | TargetBranch string `url:"target_branch,omitemtpy" json:"target_branch,omitemtpy"` 202 | AssigneeID int `url:"assignee_id,omitempty" json:"assignee_id,omitempty"` 203 | TargetProjectID int `url:"target_project_id,omitempty" json:"target_project_id,omitempty"` 204 | } 205 | 206 | // CreateMergeRequest creates a new merge request. 207 | // 208 | // GitLab API docs: 209 | // http://doc.gitlab.com/ce/api/merge_requests.html#create-mr 210 | func (s *MergeRequestsService) CreateMergeRequest( 211 | pid interface{}, 212 | opt *CreateMergeRequestOptions) (*MergeRequest, *Response, error) { 213 | project, err := parseID(pid) 214 | if err != nil { 215 | return nil, nil, err 216 | } 217 | u := fmt.Sprintf("projects/%s/merge_requests", url.QueryEscape(project)) 218 | 219 | req, err := s.client.NewRequest("POST", u, opt) 220 | if err != nil { 221 | return nil, nil, err 222 | } 223 | 224 | m := new(MergeRequest) 225 | resp, err := s.client.Do(req, m) 226 | if err != nil { 227 | return nil, resp, err 228 | } 229 | 230 | return m, resp, err 231 | } 232 | 233 | // UpdateMergeRequestOptions represents the available UpdateMergeRequest() 234 | // options. 235 | // 236 | // GitLab API docs: 237 | // http://doc.gitlab.com/ce/api/merge_requests.html#update-mr 238 | type UpdateMergeRequestOptions struct { 239 | Title string `url:"title,omitempty" json:"title,omitempty"` 240 | Description string `url:"description,omitempty" json:"description,omitempty"` 241 | TargetBranch string `url:"target_branch,omitemtpy" json:"target_branch,omitemtpy"` 242 | AssigneeID int `url:"assignee_id,omitempty" json:"assignee_id,omitempty"` 243 | StateEvent string `url:"state_event,omitempty" json:"state_event,omitempty"` 244 | } 245 | 246 | // UpdateMergeRequest updates an existing project milestone. 247 | // 248 | // GitLab API docs: 249 | // http://doc.gitlab.com/ce/api/merge_requests.html#update-mr 250 | func (s *MergeRequestsService) UpdateMergeRequest( 251 | pid interface{}, 252 | mergeRequest int, 253 | opt *UpdateMergeRequestOptions) (*MergeRequest, *Response, error) { 254 | project, err := parseID(pid) 255 | if err != nil { 256 | return nil, nil, err 257 | } 258 | u := fmt.Sprintf("projects/%s/merge_request/%d", url.QueryEscape(project), mergeRequest) 259 | 260 | req, err := s.client.NewRequest("PUT", u, opt) 261 | if err != nil { 262 | return nil, nil, err 263 | } 264 | 265 | m := new(MergeRequest) 266 | resp, err := s.client.Do(req, m) 267 | if err != nil { 268 | return nil, resp, err 269 | } 270 | 271 | return m, resp, err 272 | } 273 | 274 | // AcceptMergeRequest merges changes submitted with MR using this API. If merge 275 | // success you get 200 OK. If it has some conflicts and can not be merged - you 276 | // get 405 and error message 'Branch cannot be merged'. If merge request is 277 | // already merged or closed - you get 405 and error message 'Method Not Allowed' 278 | // 279 | // GitLab API docs: 280 | // http://doc.gitlab.com/ce/api/merge_requests.html#accept-mr 281 | func (s *MergeRequestsService) AcceptMergeRequest( 282 | pid interface{}, 283 | mergeRequest int) (*MergeRequest, *Response, error) { 284 | project, err := parseID(pid) 285 | if err != nil { 286 | return nil, nil, err 287 | } 288 | u := fmt.Sprintf("projects/%s/merge_request/%d/merge", url.QueryEscape(project), mergeRequest) 289 | 290 | req, err := s.client.NewRequest("PUT", u, nil) 291 | if err != nil { 292 | return nil, nil, err 293 | } 294 | 295 | m := new(MergeRequest) 296 | resp, err := s.client.Do(req, m) 297 | if err != nil { 298 | return nil, resp, err 299 | } 300 | 301 | return m, resp, err 302 | } 303 | 304 | // MergeRequestComment represents a GitLab merge request comment. 305 | // 306 | // GitLab API docs: http://doc.gitlab.com/ce/api/merge_requests.html 307 | type MergeRequestComment struct { 308 | Note string `json:"note"` 309 | Author struct { 310 | ID int `json:"id"` 311 | Username string `json:"username"` 312 | Email string `json:"email"` 313 | Name string `json:"name"` 314 | State string `json:"state"` 315 | CreatedAt time.Time `json:"created_at"` 316 | } `json:"author"` 317 | } 318 | 319 | func (m MergeRequestComment) String() string { 320 | return Stringify(m) 321 | } 322 | 323 | // GetMergeRequestCommentsOptions represents the available GetMergeRequestComments() 324 | // options. 325 | // 326 | // GitLab API docs: 327 | // http://doc.gitlab.com/ce/api/merge_requests.html#get-the-comments-on-a-mr 328 | type GetMergeRequestCommentsOptions struct { 329 | ListOptions 330 | } 331 | 332 | // GetMergeRequestComments gets all the comments associated with a merge 333 | // request. 334 | // 335 | // GitLab API docs: 336 | // http://doc.gitlab.com/ce/api/merge_requests.html#get-the-comments-on-a-mr 337 | func (s *MergeRequestsService) GetMergeRequestComments( 338 | pid interface{}, 339 | mergeRequest int, 340 | opt *GetMergeRequestCommentsOptions) ([]*MergeRequestComment, *Response, error) { 341 | project, err := parseID(pid) 342 | if err != nil { 343 | return nil, nil, err 344 | } 345 | u := fmt.Sprintf("projects/%s/merge_request/%d/comments", url.QueryEscape(project), mergeRequest) 346 | 347 | req, err := s.client.NewRequest("GET", u, opt) 348 | if err != nil { 349 | return nil, nil, err 350 | } 351 | 352 | var c []*MergeRequestComment 353 | resp, err := s.client.Do(req, &c) 354 | if err != nil { 355 | return nil, resp, err 356 | } 357 | 358 | return c, resp, err 359 | } 360 | 361 | // PostMergeRequestCommentOptions represents the available 362 | // PostMergeRequestComment() options. 363 | // 364 | // GitLab API docs: 365 | // http://doc.gitlab.com/ce/api/commits.html#post-comment-to-mr 366 | type PostMergeRequestCommentOptions struct { 367 | Note string `url:"note,omitempty" json:"note,omitempty"` 368 | } 369 | 370 | // PostMergeRequestComment dds a comment to a merge request. 371 | // 372 | // GitLab API docs: 373 | // http://doc.gitlab.com/ce/api/commits.html#post-comment-to-mr 374 | func (s *MergeRequestsService) PostMergeRequestComment( 375 | pid interface{}, 376 | mergeRequest int, 377 | opt *PostMergeRequestCommentOptions) (*MergeRequestComment, *Response, error) { 378 | project, err := parseID(pid) 379 | if err != nil { 380 | return nil, nil, err 381 | } 382 | u := fmt.Sprintf("projects/%s/merge_request/%d/comments", url.QueryEscape(project), mergeRequest) 383 | 384 | req, err := s.client.NewRequest("POST", u, opt) 385 | if err != nil { 386 | return nil, nil, err 387 | } 388 | 389 | c := new(MergeRequestComment) 390 | resp, err := s.client.Do(req, c) 391 | if err != nil { 392 | return nil, resp, err 393 | } 394 | 395 | return c, resp, err 396 | } 397 | -------------------------------------------------------------------------------- /api/milestones.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2015, Sander van Harmelen 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | package gitlab 18 | 19 | import ( 20 | "fmt" 21 | "net/url" 22 | "time" 23 | ) 24 | 25 | // MilestonesService handles communication with the milestone related methods 26 | // of the GitLab API. 27 | // 28 | // GitLab API docs: http://doc.gitlab.com/ce/api/milestones.html 29 | type MilestonesService struct { 30 | client *Client 31 | } 32 | 33 | // Milestone represents a GitLab milestone. 34 | // 35 | // GitLab API docs: http://doc.gitlab.com/ce/api/branches.html 36 | type Milestone struct { 37 | ID int `json:"id"` 38 | Iid int `json:"iid"` 39 | ProjectID int `json:"project_id"` 40 | Title string `json:"title"` 41 | Description string `json:"description"` 42 | DueDate string `json:"due_date"` 43 | State string `json:"state"` 44 | UpdatedAt time.Time `json:"updated_at"` 45 | CreatedAt time.Time `json:"created_at"` 46 | } 47 | 48 | func (m Milestone) String() string { 49 | return Stringify(m) 50 | } 51 | 52 | // ListMilestonesOptions represents the available ListMilestones() options. 53 | // 54 | // GitLab API docs: 55 | // http://doc.gitlab.com/ce/api/milestones.html#list-project-milestones 56 | type ListMilestonesOptions struct { 57 | ListOptions 58 | IID int `url:"iid,omitempty" json:"iid,omitempty"` 59 | } 60 | 61 | // ListMilestones returns a list of project milestones. 62 | // 63 | // GitLab API docs: 64 | // http://doc.gitlab.com/ce/api/milestones.html#list-project-milestones 65 | func (s *MilestonesService) ListMilestones( 66 | pid interface{}, 67 | opt *ListMilestonesOptions) ([]*Milestone, *Response, error) { 68 | project, err := parseID(pid) 69 | if err != nil { 70 | return nil, nil, err 71 | } 72 | u := fmt.Sprintf("projects/%s/milestones", url.QueryEscape(project)) 73 | 74 | req, err := s.client.NewRequest("GET", u, opt) 75 | if err != nil { 76 | return nil, nil, err 77 | } 78 | 79 | var m []*Milestone 80 | resp, err := s.client.Do(req, &m) 81 | if err != nil { 82 | return nil, resp, err 83 | } 84 | 85 | return m, resp, err 86 | } 87 | 88 | // GetMilestone gets a single project milestone. 89 | // 90 | // GitLab API docs: 91 | // http://doc.gitlab.com/ce/api/milestones.html#get-single-milestone 92 | func (s *MilestonesService) GetMilestone( 93 | pid interface{}, 94 | milestone int) (*Milestone, *Response, error) { 95 | project, err := parseID(pid) 96 | if err != nil { 97 | return nil, nil, err 98 | } 99 | u := fmt.Sprintf("projects/%s/milestones/%d", url.QueryEscape(project), milestone) 100 | 101 | req, err := s.client.NewRequest("GET", u, nil) 102 | if err != nil { 103 | return nil, nil, err 104 | } 105 | 106 | m := new(Milestone) 107 | resp, err := s.client.Do(req, m) 108 | if err != nil { 109 | return nil, resp, err 110 | } 111 | 112 | return m, resp, err 113 | } 114 | 115 | // CreateMilestoneOptions represents the available CreateMilestone() options. 116 | // 117 | // GitLab API docs: 118 | // http://doc.gitlab.com/ce/api/milestones.html#create-new-milestone 119 | type CreateMilestoneOptions struct { 120 | Title string `url:"title,omitempty" json:"title,omitempty"` 121 | Description string `url:"description,omitempty" json:"description,omitempty"` 122 | DueDate string `url:"due_date,omitempty" json:"due_date,omitempty"` 123 | } 124 | 125 | // CreateMilestone creates a new project milestone. 126 | // 127 | // GitLab API docs: 128 | // http://doc.gitlab.com/ce/api/milestones.html#create-new-milestone 129 | func (s *MilestonesService) CreateMilestone( 130 | pid interface{}, 131 | opt *CreateMilestoneOptions) (*Milestone, *Response, error) { 132 | project, err := parseID(pid) 133 | if err != nil { 134 | return nil, nil, err 135 | } 136 | u := fmt.Sprintf("projects/%s/milestones", url.QueryEscape(project)) 137 | 138 | req, err := s.client.NewRequest("POST", u, opt) 139 | if err != nil { 140 | return nil, nil, err 141 | } 142 | 143 | m := new(Milestone) 144 | resp, err := s.client.Do(req, m) 145 | if err != nil { 146 | return nil, resp, err 147 | } 148 | 149 | return m, resp, err 150 | } 151 | 152 | // UpdateMilestoneOptions represents the available UpdateMilestone() options. 153 | // 154 | // GitLab API docs: 155 | // http://doc.gitlab.com/ce/api/milestones.html#edit-milestone 156 | type UpdateMilestoneOptions struct { 157 | Title string `url:"title,omitempty" json:"title,omitempty"` 158 | Description string `url:"description,omitempty" json:"description,omitempty"` 159 | DueDate string `url:"due_date,omitempty" json:"due_date,omitempty"` 160 | StateEvent string `url:"state_event,omitempty" json:"state_event,omitempty"` 161 | } 162 | 163 | // UpdateMilestone updates an existing project milestone. 164 | // 165 | // GitLab API docs: 166 | // http://doc.gitlab.com/ce/api/milestones.html#edit-milestone 167 | func (s *MilestonesService) UpdateMilestone( 168 | pid interface{}, 169 | milestone int, 170 | opt *UpdateMilestoneOptions) (*Milestone, *Response, error) { 171 | project, err := parseID(pid) 172 | if err != nil { 173 | return nil, nil, err 174 | } 175 | u := fmt.Sprintf("projects/%s/milestones/%d", url.QueryEscape(project), milestone) 176 | 177 | req, err := s.client.NewRequest("PUT", u, opt) 178 | if err != nil { 179 | return nil, nil, err 180 | } 181 | 182 | m := new(Milestone) 183 | resp, err := s.client.Do(req, m) 184 | if err != nil { 185 | return nil, resp, err 186 | } 187 | 188 | return m, resp, err 189 | } 190 | 191 | // GetMilestoneIssuesOptions represents the available GetMilestoneIssues() options. 192 | // 193 | // GitLab API docs: 194 | // http://doc.gitlab.com/ce/api/milestones.html#get-all-issues-assigned-to-a-single-milestone 195 | type GetMilestoneIssuesOptions struct { 196 | ListOptions 197 | } 198 | 199 | // GetMilestoneIssues gets all issues assigned to a single project milestone. 200 | // 201 | // GitLab API docs: 202 | // http://doc.gitlab.com/ce/api/milestones.html#get-all-issues-assigned-to-a-single-milestone 203 | func (s *MilestonesService) GetMilestoneIssues( 204 | pid interface{}, 205 | milestone int, 206 | opt *GetMilestoneIssuesOptions) ([]*Issue, *Response, error) { 207 | project, err := parseID(pid) 208 | if err != nil { 209 | return nil, nil, err 210 | } 211 | u := fmt.Sprintf("projects/%s/milestones/%d/issues", url.QueryEscape(project), milestone) 212 | 213 | req, err := s.client.NewRequest("GET", u, opt) 214 | if err != nil { 215 | return nil, nil, err 216 | } 217 | 218 | var i []*Issue 219 | resp, err := s.client.Do(req, &i) 220 | if err != nil { 221 | return nil, resp, err 222 | } 223 | 224 | return i, resp, err 225 | } 226 | -------------------------------------------------------------------------------- /api/namespaces.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2015, Sander van Harmelen 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | package gitlab 18 | 19 | // NamespacesService handles communication with the namespace related methods 20 | // of the GitLab API. 21 | // 22 | // GitLab API docs: http://doc.gitlab.com/ce/api/namespaces.html 23 | type NamespacesService struct { 24 | client *Client 25 | } 26 | 27 | // Namespace represents a GitLab namespace. 28 | // 29 | // GitLab API docs: http://doc.gitlab.com/ce/api/namespaces.html 30 | type Namespace struct { 31 | ID int `json:"id"` 32 | Path string `json:"path"` 33 | Kind string `json:"kind"` 34 | } 35 | 36 | func (n Namespace) String() string { 37 | return Stringify(n) 38 | } 39 | 40 | // ListNamespacesOptions represents the available ListNamespaces() options. 41 | // 42 | // GitLab API docs: http://doc.gitlab.com/ce/api/namespaces.html#list-namespaces 43 | type ListNamespacesOptions struct { 44 | ListOptions 45 | Search string `url:"search,omitempty" json:"search,omitempty"` 46 | } 47 | 48 | // ListNamespaces gets a list of projects accessible by the authenticated user. 49 | // 50 | // GitLab API docs: http://doc.gitlab.com/ce/api/namespaces.html#list-namespaces 51 | func (s *NamespacesService) ListNamespaces(opt *ListNamespacesOptions) ([]*Namespace, *Response, error) { 52 | req, err := s.client.NewRequest("GET", "namespaces", opt) 53 | if err != nil { 54 | return nil, nil, err 55 | } 56 | 57 | var n []*Namespace 58 | resp, err := s.client.Do(req, &n) 59 | if err != nil { 60 | return nil, resp, err 61 | } 62 | 63 | return n, resp, err 64 | } 65 | 66 | // SearchNamespace gets all namespaces that match your string in their name 67 | // or path. 68 | // 69 | // GitLab API docs: 70 | // http://doc.gitlab.com/ce/api/namespaces.html#search-for-namespace 71 | func (s *NamespacesService) SearchNamespace(query string) ([]*Namespace, *Response, error) { 72 | var q struct { 73 | Search string `url:"search,omitempty" json:"search,omitempty"` 74 | } 75 | q.Search = query 76 | 77 | req, err := s.client.NewRequest("GET", "namespaces", &q) 78 | if err != nil { 79 | return nil, nil, err 80 | } 81 | 82 | var n []*Namespace 83 | resp, err := s.client.Do(req, &n) 84 | if err != nil { 85 | return nil, resp, err 86 | } 87 | 88 | return n, resp, err 89 | } 90 | -------------------------------------------------------------------------------- /api/notes.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2015, Sander van Harmelen 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | package gitlab 18 | 19 | import ( 20 | "fmt" 21 | "net/url" 22 | "time" 23 | ) 24 | 25 | // NotesService handles communication with the notes related methods 26 | // of the GitLab API. 27 | // 28 | // GitLab API docs: http://doc.gitlab.com/ce/api/notes.html 29 | type NotesService struct { 30 | client *Client 31 | } 32 | 33 | // Note represents a GitLab note. 34 | // 35 | // GitLab API docs: http://doc.gitlab.com/ce/api/notes.html 36 | type Note struct { 37 | ID int `json:"id"` 38 | Body string `json:"body"` 39 | Attachment string `json:"attachment"` 40 | Title string `json:"title"` 41 | FileName string `json:"file_name"` 42 | Author struct { 43 | ID int `json:"id"` 44 | Username string `json:"username"` 45 | Email string `json:"email"` 46 | Name string `json:"name"` 47 | State string `json:"state"` 48 | CreatedAt time.Time `json:"created_at"` 49 | } `json:"author"` 50 | ExpiresAt *time.Time `json:"expires_at"` 51 | UpdatedAt string `json:"updated_at"` 52 | CreatedAt string `json:"created_at"` 53 | } 54 | 55 | func (n Note) String() string { 56 | return Stringify(n) 57 | } 58 | 59 | // ListIssueNotesOptions represents the available ListIssueNotes() options. 60 | // 61 | // GitLab API docs: 62 | // http://doc.gitlab.com/ce/api/notes.html#list-project-issue-notes 63 | type ListIssueNotesOptions struct { 64 | ListOptions 65 | } 66 | 67 | // ListIssueNotes gets a list of all notes for a single issue. 68 | // 69 | // GitLab API docs: 70 | // http://doc.gitlab.com/ce/api/notes.html#list-project-issue-notes 71 | func (s *NotesService) ListIssueNotes( 72 | pid interface{}, 73 | issue int, 74 | opt *ListIssueNotesOptions) ([]*Note, *Response, error) { 75 | project, err := parseID(pid) 76 | if err != nil { 77 | return nil, nil, err 78 | } 79 | u := fmt.Sprintf("projects/%s/issues/%d/notes", url.QueryEscape(project), issue) 80 | 81 | req, err := s.client.NewRequest("GET", u, opt) 82 | if err != nil { 83 | return nil, nil, err 84 | } 85 | 86 | var n []*Note 87 | resp, err := s.client.Do(req, &n) 88 | if err != nil { 89 | return nil, resp, err 90 | } 91 | 92 | return n, resp, err 93 | } 94 | 95 | // GetIssueNote returns a single note for a specific project issue. 96 | // 97 | // GitLab API docs: 98 | // http://doc.gitlab.com/ce/api/notes.html#get-single-issue-note 99 | func (s *NotesService) GetIssueNote( 100 | pid interface{}, 101 | issue int, 102 | note int) (*Note, *Response, error) { 103 | project, err := parseID(pid) 104 | if err != nil { 105 | return nil, nil, err 106 | } 107 | u := fmt.Sprintf("projects/%s/issues/%d/notes/%d", url.QueryEscape(project), issue, note) 108 | 109 | req, err := s.client.NewRequest("GET", u, nil) 110 | if err != nil { 111 | return nil, nil, err 112 | } 113 | 114 | n := new(Note) 115 | resp, err := s.client.Do(req, n) 116 | if err != nil { 117 | return nil, resp, err 118 | } 119 | 120 | return n, resp, err 121 | } 122 | 123 | // CreateIssueNoteOptions represents the available CreateIssueNote() 124 | // options. 125 | // 126 | // GitLab API docs: 127 | // http://doc.gitlab.com/ce/api/notes.html#create-new-issue-note 128 | type CreateIssueNoteOptions struct { 129 | Body string `url:"body,omitempty" json:"body,omitempty"` 130 | } 131 | 132 | // CreateIssueNoteOptions represents the available CreateIssueNote() 133 | // options. 134 | // 135 | // GitLab API docs: 136 | // http://doc.gitlab.com/ce/api/notes.html#create-new-issue-note 137 | type CreateCommitNoteOptions struct { 138 | Note string `url:"note,omitempty" json:"note,omitempty"` 139 | } 140 | 141 | // CreateIssueNote creates a new note to a single project issue. 142 | // 143 | // GitLab API docs: 144 | // http://doc.gitlab.com/ce/api/notes.html#create-new-issue-note 145 | func (s *NotesService) CreateIssueNote( 146 | pid interface{}, 147 | issue int, 148 | opt *CreateIssueNoteOptions) (*Note, *Response, error) { 149 | project, err := parseID(pid) 150 | if err != nil { 151 | return nil, nil, err 152 | } 153 | u := fmt.Sprintf("projects/%s/issues/%d/notes", url.QueryEscape(project), issue) 154 | 155 | req, err := s.client.NewRequest("POST", u, opt) 156 | if err != nil { 157 | return nil, nil, err 158 | } 159 | 160 | n := new(Note) 161 | resp, err := s.client.Do(req, n) 162 | if err != nil { 163 | return nil, resp, err 164 | } 165 | 166 | return n, resp, err 167 | } 168 | 169 | // CreateIssueNote creates a new note to a single project issue. 170 | // 171 | // GitLab API docs: 172 | // http://doc.gitlab.com/ce/api/notes.html#create-new-issue-note 173 | func (s *NotesService) CreateCommitNote( 174 | pid interface{}, 175 | commitID string, 176 | opt *CreateCommitNoteOptions) (*Note, *Response, error) { 177 | project, err := parseID(pid) 178 | if err != nil { 179 | return nil, nil, err 180 | } 181 | u := fmt.Sprintf("projects/%s/repository/commits/%s/comments", url.QueryEscape(project), commitID) 182 | 183 | req, err := s.client.NewRequest("POST", u, opt) 184 | if err != nil { 185 | return nil, nil, err 186 | } 187 | 188 | n := new(Note) 189 | resp, err := s.client.Do(req, n) 190 | if err != nil { 191 | return nil, resp, err 192 | } 193 | 194 | return n, resp, err 195 | } 196 | 197 | // UpdateIssueNoteOptions represents the available UpdateIssueNote() 198 | // options. 199 | // 200 | // GitLab API docs: 201 | // http://doc.gitlab.com/ce/api/notes.html#modify-existing-issue-note 202 | type UpdateIssueNoteOptions struct { 203 | Body string `url:"body,omitempty" json:"body,omitempty"` 204 | } 205 | 206 | // UpdateIssueNote modifies existing note of an issue. 207 | // 208 | // http://doc.gitlab.com/ce/api/notes.html#modify-existing-issue-note 209 | func (s *NotesService) UpdateIssueNote( 210 | pid interface{}, 211 | issue int, 212 | note int, 213 | opt *UpdateIssueNoteOptions) (*Note, *Response, error) { 214 | project, err := parseID(pid) 215 | if err != nil { 216 | return nil, nil, err 217 | } 218 | u := fmt.Sprintf("projects/%s/issues/%d/notes/%d", url.QueryEscape(project), issue, note) 219 | 220 | req, err := s.client.NewRequest("PUT", u, opt) 221 | if err != nil { 222 | return nil, nil, err 223 | } 224 | 225 | n := new(Note) 226 | resp, err := s.client.Do(req, n) 227 | if err != nil { 228 | return nil, resp, err 229 | } 230 | 231 | return n, resp, err 232 | } 233 | 234 | // ListSnippetNotes gets a list of all notes for a single snippet. Snippet 235 | // notes are comments users can post to a snippet. 236 | // 237 | // GitLab API docs: 238 | // http://doc.gitlab.com/ce/api/notes.html#list-all-snippet-notes 239 | func (s *NotesService) ListSnippetNotes(pid interface{}, snippet int) ([]*Note, *Response, error) { 240 | project, err := parseID(pid) 241 | if err != nil { 242 | return nil, nil, err 243 | } 244 | u := fmt.Sprintf("projects/%s/snippets/%d/notes", url.QueryEscape(project), snippet) 245 | 246 | req, err := s.client.NewRequest("GET", u, nil) 247 | if err != nil { 248 | return nil, nil, err 249 | } 250 | 251 | var n []*Note 252 | resp, err := s.client.Do(req, &n) 253 | if err != nil { 254 | return nil, resp, err 255 | } 256 | 257 | return n, resp, err 258 | } 259 | 260 | // GetSnippetNote returns a single note for a given snippet. 261 | // 262 | // GitLab API docs: 263 | // http://doc.gitlab.com/ce/api/notes.html#get-single-snippet-note 264 | func (s *NotesService) GetSnippetNote( 265 | pid interface{}, 266 | snippet int, 267 | note int) (*Note, *Response, error) { 268 | project, err := parseID(pid) 269 | if err != nil { 270 | return nil, nil, err 271 | } 272 | u := fmt.Sprintf("projects/%s/snippets/%d/notes/%d", url.QueryEscape(project), snippet, note) 273 | 274 | req, err := s.client.NewRequest("GET", u, nil) 275 | if err != nil { 276 | return nil, nil, err 277 | } 278 | 279 | n := new(Note) 280 | resp, err := s.client.Do(req, n) 281 | if err != nil { 282 | return nil, resp, err 283 | } 284 | 285 | return n, resp, err 286 | } 287 | 288 | // CreateSnippetNoteOptions represents the available CreateSnippetNote() 289 | // options. 290 | // 291 | // GitLab API docs: 292 | // http://doc.gitlab.com/ce/api/notes.html#create-new-snippet-note 293 | type CreateSnippetNoteOptions struct { 294 | Body string `url:"body,omitempty" json:"body,omitempty"` 295 | } 296 | 297 | // CreateSnippetNote creates a new note for a single snippet. Snippet notes are 298 | // comments users can post to a snippet. 299 | // 300 | // GitLab API docs: 301 | // http://doc.gitlab.com/ce/api/notes.html#create-new-snippet-note 302 | func (s *NotesService) CreateSnippetNote( 303 | pid interface{}, 304 | snippet int, 305 | opt *CreateSnippetNoteOptions) (*Note, *Response, error) { 306 | project, err := parseID(pid) 307 | if err != nil { 308 | return nil, nil, err 309 | } 310 | u := fmt.Sprintf("projects/%s/snippets/%d/notes", url.QueryEscape(project), snippet) 311 | 312 | req, err := s.client.NewRequest("POST", u, opt) 313 | if err != nil { 314 | return nil, nil, err 315 | } 316 | 317 | n := new(Note) 318 | resp, err := s.client.Do(req, n) 319 | if err != nil { 320 | return nil, resp, err 321 | } 322 | 323 | return n, resp, err 324 | } 325 | 326 | // UpdateSnippetNoteOptions represents the available UpdateSnippetNote() 327 | // options. 328 | // 329 | // GitLab API docs: 330 | // http://doc.gitlab.com/ce/api/notes.html#modify-existing-snippet-note 331 | type UpdateSnippetNoteOptions struct { 332 | Body string `url:"body,omitempty" json:"body,omitempty"` 333 | } 334 | 335 | // UpdateSnippetNote modifies existing note of a snippet. 336 | // 337 | // http://doc.gitlab.com/ce/api/notes.html#modify-existing-snippet-note 338 | func (s *NotesService) UpdateSnippetNote( 339 | pid interface{}, 340 | snippet int, 341 | note int, 342 | opt *UpdateSnippetNoteOptions) (*Note, *Response, error) { 343 | project, err := parseID(pid) 344 | if err != nil { 345 | return nil, nil, err 346 | } 347 | u := fmt.Sprintf("projects/%s/snippets/%d/notes/%d", url.QueryEscape(project), snippet, note) 348 | 349 | req, err := s.client.NewRequest("PUT", u, opt) 350 | if err != nil { 351 | return nil, nil, err 352 | } 353 | 354 | n := new(Note) 355 | resp, err := s.client.Do(req, n) 356 | if err != nil { 357 | return nil, resp, err 358 | } 359 | 360 | return n, resp, err 361 | } 362 | 363 | // ListMergeRequestNotes gets a list of all notes for a single merge request. 364 | // 365 | // GitLab API docs: 366 | // http://doc.gitlab.com/ce/api/notes.html#list-all-merge-request-notes 367 | func (s *NotesService) ListMergeRequestNotes( 368 | pid interface{}, 369 | mergeRequest int) ([]*Note, *Response, error) { 370 | project, err := parseID(pid) 371 | if err != nil { 372 | return nil, nil, err 373 | } 374 | u := fmt.Sprintf("projects/%s/merge_requests/%d/notes", url.QueryEscape(project), mergeRequest) 375 | 376 | req, err := s.client.NewRequest("GET", u, nil) 377 | if err != nil { 378 | return nil, nil, err 379 | } 380 | 381 | var n []*Note 382 | resp, err := s.client.Do(req, &n) 383 | if err != nil { 384 | return nil, resp, err 385 | } 386 | 387 | return n, resp, err 388 | } 389 | 390 | // GetMergeRequestNote returns a single note for a given merge request. 391 | // 392 | // GitLab API docs: 393 | // http://doc.gitlab.com/ce/api/notes.html#get-single-merge-request-note 394 | func (s *NotesService) GetMergeRequestNote( 395 | pid interface{}, 396 | mergeRequest int, 397 | note int) (*Note, *Response, error) { 398 | project, err := parseID(pid) 399 | if err != nil { 400 | return nil, nil, err 401 | } 402 | u := fmt.Sprintf("projects/%s/merge_requests/%d/notes/%d", url.QueryEscape(project), mergeRequest, note) 403 | 404 | req, err := s.client.NewRequest("GET", u, nil) 405 | if err != nil { 406 | return nil, nil, err 407 | } 408 | 409 | n := new(Note) 410 | resp, err := s.client.Do(req, n) 411 | if err != nil { 412 | return nil, resp, err 413 | } 414 | 415 | return n, resp, err 416 | } 417 | 418 | // CreateMergeRequestNoteOptions represents the available 419 | // CreateMergeRequestNote() options. 420 | // 421 | // GitLab API docs: 422 | // http://doc.gitlab.com/ce/api/notes.html#create-new-merge-request-note 423 | type CreateMergeRequestNoteOptions struct { 424 | Body string `url:"body,omitempty" json:"body,omitempty"` 425 | } 426 | 427 | // CreateMergeRequestNote creates a new note for a single merge request. 428 | // 429 | // GitLab API docs: 430 | // http://doc.gitlab.com/ce/api/notes.html#create-new-merge-request-note 431 | func (s *NotesService) CreateMergeRequestNote( 432 | pid interface{}, 433 | mergeRequest int, 434 | opt *CreateMergeRequestNoteOptions) (*Note, *Response, error) { 435 | project, err := parseID(pid) 436 | if err != nil { 437 | return nil, nil, err 438 | } 439 | u := fmt.Sprintf("projects/%s/merge_requests/%d/notes", url.QueryEscape(project), mergeRequest) 440 | 441 | req, err := s.client.NewRequest("POST", u, opt) 442 | if err != nil { 443 | return nil, nil, err 444 | } 445 | 446 | n := new(Note) 447 | resp, err := s.client.Do(req, n) 448 | if err != nil { 449 | return nil, resp, err 450 | } 451 | 452 | return n, resp, err 453 | } 454 | 455 | // UpdateMergeRequestNoteOptions represents the available 456 | // UpdateMergeRequestNote() options. 457 | // 458 | // GitLab API docs: 459 | // http://doc.gitlab.com/ce/api/notes.html#modify-existing-merge-request-note 460 | type UpdateMergeRequestNoteOptions struct { 461 | Body string `url:"body,omitempty" json:"body,omitempty"` 462 | } 463 | 464 | // UpdateMergeRequestNote modifies existing note of a merge request. 465 | // 466 | // http://doc.gitlab.com/ce/api/notes.html#modify-existing-merge-request-note 467 | func (s *NotesService) UpdateMergeRequestNote( 468 | pid interface{}, 469 | mergeRequest int, 470 | note int, 471 | opt *UpdateMergeRequestNoteOptions) (*Note, *Response, error) { 472 | project, err := parseID(pid) 473 | if err != nil { 474 | return nil, nil, err 475 | } 476 | u := fmt.Sprintf( 477 | "projects/%s/merge_requests/%d/notes/%d", url.QueryEscape(project), mergeRequest, note) 478 | 479 | req, err := s.client.NewRequest("PUT", u, opt) 480 | if err != nil { 481 | return nil, nil, err 482 | } 483 | 484 | n := new(Note) 485 | resp, err := s.client.Do(req, n) 486 | if err != nil { 487 | return nil, resp, err 488 | } 489 | 490 | return n, resp, err 491 | } 492 | -------------------------------------------------------------------------------- /api/project_snippets.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2015, Sander van Harmelen 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | package gitlab 18 | 19 | import ( 20 | "bytes" 21 | "fmt" 22 | "net/url" 23 | "time" 24 | ) 25 | 26 | // ProjectSnippetsService handles communication with the project snippets 27 | // related methods of the GitLab API. 28 | // 29 | // GitLab API docs: http://doc.gitlab.com/ce/api/project_snippets.html 30 | type ProjectSnippetsService struct { 31 | client *Client 32 | } 33 | 34 | // Snippet represents a GitLab project snippet. 35 | // 36 | // GitLab API docs: http://doc.gitlab.com/ce/api/project_snippets.html 37 | type Snippet struct { 38 | ID int `json:"id"` 39 | Title string `json:"title"` 40 | FileName string `json:"file_name"` 41 | Author struct { 42 | ID int `json:"id"` 43 | Username string `json:"username"` 44 | Email string `json:"email"` 45 | Name string `json:"name"` 46 | State string `json:"state"` 47 | CreatedAt time.Time `json:"created_at"` 48 | } `json:"author"` 49 | ExpiresAt *time.Time `json:"expires_at"` 50 | UpdatedAt time.Time `json:"updated_at"` 51 | CreatedAt time.Time `json:"created_at"` 52 | } 53 | 54 | func (s Snippet) String() string { 55 | return Stringify(s) 56 | } 57 | 58 | // ListSnippetsOptions represents the available ListSnippets() options. 59 | // 60 | // GitLab API docs: http://doc.gitlab.com/ce/api/project_snippets.html#list-snippets 61 | type ListSnippetsOptions struct { 62 | ListOptions 63 | } 64 | 65 | // ListSnippets gets a list of project snippets. 66 | // 67 | // GitLab API docs: http://doc.gitlab.com/ce/api/project_snippets.html#list-snippets 68 | func (s *ProjectSnippetsService) ListSnippets( 69 | pid interface{}, 70 | opt *ListSnippetsOptions) ([]*Snippet, *Response, error) { 71 | project, err := parseID(pid) 72 | if err != nil { 73 | return nil, nil, err 74 | } 75 | u := fmt.Sprintf("projects/%s/snippets", url.QueryEscape(project)) 76 | 77 | req, err := s.client.NewRequest("GET", u, opt) 78 | if err != nil { 79 | return nil, nil, err 80 | } 81 | 82 | var ps []*Snippet 83 | resp, err := s.client.Do(req, &ps) 84 | if err != nil { 85 | return nil, resp, err 86 | } 87 | 88 | return ps, resp, err 89 | } 90 | 91 | // GetSnippet gets a single project snippet 92 | // 93 | // GitLab API docs: 94 | // http://doc.gitlab.com/ce/api/project_snippets.html#single-snippet 95 | func (s *ProjectSnippetsService) GetSnippet( 96 | pid interface{}, 97 | snippet int) (*Snippet, *Response, error) { 98 | project, err := parseID(pid) 99 | if err != nil { 100 | return nil, nil, err 101 | } 102 | u := fmt.Sprintf("projects/%s/snippets/%d", url.QueryEscape(project), snippet) 103 | 104 | req, err := s.client.NewRequest("GET", u, nil) 105 | if err != nil { 106 | return nil, nil, err 107 | } 108 | 109 | ps := new(Snippet) 110 | resp, err := s.client.Do(req, ps) 111 | if err != nil { 112 | return nil, resp, err 113 | } 114 | 115 | return ps, resp, err 116 | } 117 | 118 | // CreateSnippetOptions represents the available CreateSnippet() options. 119 | // 120 | // GitLab API docs: 121 | // http://doc.gitlab.com/ce/api/project_snippets.html#create-new-snippet 122 | type CreateSnippetOptions struct { 123 | Title string `url:"title,omitempty" json:"title,omitempty"` 124 | FileName string `url:"file_name,omitempty" json:"file_name,omitempty"` 125 | Code string `url:"code,omitempty" json:"code,omitempty"` 126 | VisibilityLevel VisibilityLevel `url:"visibility_level,omitempty" json:"visibility_level,omitempty"` 127 | } 128 | 129 | // CreateSnippet creates a new project snippet. The user must have permission 130 | // to create new snippets. 131 | // 132 | // GitLab API docs: 133 | // http://doc.gitlab.com/ce/api/project_snippets.html#create-new-snippet 134 | func (s *ProjectSnippetsService) CreateSnippet( 135 | pid interface{}, 136 | opt *CreateSnippetOptions) (*Snippet, *Response, error) { 137 | project, err := parseID(pid) 138 | if err != nil { 139 | return nil, nil, err 140 | } 141 | u := fmt.Sprintf("projects/%s/snippets", url.QueryEscape(project)) 142 | 143 | req, err := s.client.NewRequest("POST", u, opt) 144 | if err != nil { 145 | return nil, nil, err 146 | } 147 | 148 | ps := new(Snippet) 149 | resp, err := s.client.Do(req, ps) 150 | if err != nil { 151 | return nil, resp, err 152 | } 153 | 154 | return ps, resp, err 155 | } 156 | 157 | // UpdateSnippetOptions represents the available UpdateSnippet() options. 158 | // 159 | // GitLab API docs: 160 | // http://doc.gitlab.com/ce/api/project_snippets.html#update-snippet 161 | type UpdateSnippetOptions struct { 162 | Title string `url:"title,omitempty" json:"title,omitempty"` 163 | FileName string `url:"file_name,omitempty" json:"file_name,omitempty"` 164 | Code string `url:"code,omitempty" json:"code,omitempty"` 165 | VisibilityLevel VisibilityLevel `url:"visibility_level,omitempty" json:"visibility_level,omitempty"` 166 | } 167 | 168 | // UpdateSnippet updates an existing project snippet. The user must have 169 | // permission to change an existing snippet. 170 | // 171 | // GitLab API docs: 172 | // http://doc.gitlab.com/ce/api/project_snippets.html#update-snippet 173 | func (s *ProjectSnippetsService) UpdateSnippet( 174 | pid interface{}, 175 | snippet int, 176 | opt *UpdateSnippetOptions) (*Snippet, *Response, error) { 177 | project, err := parseID(pid) 178 | if err != nil { 179 | return nil, nil, err 180 | } 181 | u := fmt.Sprintf("projects/%s/snippets/%d", url.QueryEscape(project), snippet) 182 | 183 | req, err := s.client.NewRequest("PUT", u, opt) 184 | if err != nil { 185 | return nil, nil, err 186 | } 187 | 188 | ps := new(Snippet) 189 | resp, err := s.client.Do(req, ps) 190 | if err != nil { 191 | return nil, resp, err 192 | } 193 | 194 | return ps, resp, err 195 | } 196 | 197 | // DeleteSnippet deletes an existing project snippet. This is an idempotent 198 | // function and deleting a non-existent snippet still returns a 200 OK status 199 | // code. 200 | // 201 | // GitLab API docs: 202 | // http://doc.gitlab.com/ce/api/project_snippets.html#delete-snippet 203 | func (s *ProjectSnippetsService) DeleteSnippet(pid interface{}, snippet int) (*Response, error) { 204 | project, err := parseID(pid) 205 | if err != nil { 206 | return nil, err 207 | } 208 | u := fmt.Sprintf("projects/%s/snippets/%d", url.QueryEscape(project), snippet) 209 | 210 | req, err := s.client.NewRequest("DELETE", u, nil) 211 | if err != nil { 212 | return nil, err 213 | } 214 | 215 | resp, err := s.client.Do(req, nil) 216 | if err != nil { 217 | return resp, err 218 | } 219 | 220 | return resp, err 221 | } 222 | 223 | // SnippetContent returns the raw project snippet as plain text. 224 | // 225 | // GitLab API docs: 226 | // http://doc.gitlab.com/ce/api/project_snippets.html#snippet-content 227 | func (s *ProjectSnippetsService) SnippetContent( 228 | pid interface{}, 229 | snippet int) ([]byte, *Response, error) { 230 | project, err := parseID(pid) 231 | if err != nil { 232 | return nil, nil, err 233 | } 234 | u := fmt.Sprintf("projects/%s/snippets/%d/raw", url.QueryEscape(project), snippet) 235 | 236 | req, err := s.client.NewRequest("GET", u, nil) 237 | if err != nil { 238 | return nil, nil, err 239 | } 240 | 241 | var b bytes.Buffer 242 | resp, err := s.client.Do(req, &b) 243 | if err != nil { 244 | return nil, resp, err 245 | } 246 | 247 | return b.Bytes(), resp, err 248 | } 249 | -------------------------------------------------------------------------------- /api/projects_test.go: -------------------------------------------------------------------------------- 1 | package gitlab 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "reflect" 7 | "testing" 8 | ) 9 | 10 | func TestListProjects(t *testing.T) { 11 | mux, server, client := setup() 12 | defer teardown(server) 13 | 14 | mux.HandleFunc("/projects", func(w http.ResponseWriter, r *http.Request) { 15 | testMethod(t, r, "GET") 16 | testFormValues(t, r, values{ 17 | "page": "2", 18 | "per_page": "3", 19 | "archived": "true", 20 | "order_by": "name", 21 | "sort": "asc", 22 | "search": "query", 23 | "ci_enabled_first": "true", 24 | }) 25 | fmt.Fprint(w, `[{"id":1},{"id":2}]`) 26 | }) 27 | 28 | opt := &ListProjectsOptions{ListOptions{2, 3}, true, "name", "asc", "query", true} 29 | projects, _, err := client.Projects.ListProjects(opt) 30 | 31 | if err != nil { 32 | t.Errorf("Projects.ListProjects returned error: %v", err) 33 | } 34 | 35 | want := []*Project{{ID: Int(1)}, {ID: Int(2)}} 36 | if !reflect.DeepEqual(want, projects) { 37 | t.Errorf("Projects.ListProjects returned %+v, want %+v", projects, want) 38 | } 39 | } 40 | 41 | func TestListOwnedProjects(t *testing.T) { 42 | mux, server, client := setup() 43 | defer teardown(server) 44 | 45 | mux.HandleFunc("/projects/owned", func(w http.ResponseWriter, r *http.Request) { 46 | testMethod(t, r, "GET") 47 | testFormValues(t, r, values{ 48 | "page": "2", 49 | "per_page": "3", 50 | "archived": "true", 51 | "order_by": "name", 52 | "sort": "asc", 53 | "search": "query", 54 | "ci_enabled_first": "true", 55 | }) 56 | fmt.Fprint(w, `[{"id":1},{"id":2}]`) 57 | }) 58 | 59 | opt := &ListProjectsOptions{ListOptions{2, 3}, true, "name", "asc", "query", true} 60 | projects, _, err := client.Projects.ListOwnedProjects(opt) 61 | 62 | if err != nil { 63 | t.Errorf("Projects.ListOwnedProjects returned error: %v", err) 64 | } 65 | 66 | want := []*Project{{ID: Int(1)}, {ID: Int(2)}} 67 | if !reflect.DeepEqual(want, projects) { 68 | t.Errorf("Projects.ListOwnedProjects returned %+v, want %+v", projects, want) 69 | } 70 | } 71 | 72 | func TestListAllProjects(t *testing.T) { 73 | mux, server, client := setup() 74 | defer teardown(server) 75 | 76 | mux.HandleFunc("/projects/all", func(w http.ResponseWriter, r *http.Request) { 77 | testMethod(t, r, "GET") 78 | testFormValues(t, r, values{ 79 | "page": "2", 80 | "per_page": "3", 81 | "archived": "true", 82 | "order_by": "name", 83 | "sort": "asc", 84 | "search": "query", 85 | "ci_enabled_first": "true", 86 | }) 87 | fmt.Fprint(w, `[{"id":1},{"id":2}]`) 88 | }) 89 | 90 | opt := &ListProjectsOptions{ListOptions{2, 3}, true, "name", "asc", "query", true} 91 | projects, _, err := client.Projects.ListAllProjects(opt) 92 | 93 | if err != nil { 94 | t.Errorf("Projects.ListAllProjects returned error: %v", err) 95 | } 96 | 97 | want := []*Project{{ID: Int(1)}, {ID: Int(2)}} 98 | if !reflect.DeepEqual(want, projects) { 99 | t.Errorf("Projects.ListAllProjects returned %+v, want %+v", projects, want) 100 | } 101 | } 102 | 103 | func TestGetProject_byID(t *testing.T) { 104 | mux, server, client := setup() 105 | defer teardown(server) 106 | 107 | mux.HandleFunc("/projects/1", func(w http.ResponseWriter, r *http.Request) { 108 | testMethod(t, r, "GET") 109 | fmt.Fprint(w, `{"id":1}`) 110 | }) 111 | want := &Project{ID: Int(1)} 112 | 113 | project, _, err := client.Projects.GetProject(1) 114 | 115 | if err != nil { 116 | t.Fatalf("Projects.GetProject returns an error: %v", err) 117 | } 118 | 119 | if !reflect.DeepEqual(want, project) { 120 | t.Errorf("Projects.GetProject returned %+v, want %+v", project, want) 121 | } 122 | } 123 | 124 | func TestGetProject_byName(t *testing.T) { 125 | mux, server, client := setup() 126 | defer teardown(server) 127 | 128 | mux.HandleFunc("/projects/", func(w http.ResponseWriter, r *http.Request) { 129 | testUrl(t, r, "/projects/namespace%2Fname") 130 | testMethod(t, r, "GET") 131 | fmt.Fprint(w, `{"id":1}`) 132 | }) 133 | want := &Project{ID: Int(1)} 134 | 135 | project, _, err := client.Projects.GetProject("namespace/name") 136 | 137 | if err != nil { 138 | t.Fatalf("Projects.GetProject returns an error: %v", err) 139 | } 140 | 141 | if !reflect.DeepEqual(want, project) { 142 | t.Errorf("Projects.GetProject returned %+v, want %+v", project, want) 143 | } 144 | } 145 | 146 | func TestSearchProjects(t *testing.T) { 147 | mux, server, client := setup() 148 | defer teardown(server) 149 | 150 | mux.HandleFunc("/projects/search/query", func(w http.ResponseWriter, r *http.Request) { 151 | testMethod(t, r, "GET") 152 | testFormValues(t, r, values{ 153 | "page": "2", 154 | "per_page": "3", 155 | "order_by": "name", 156 | "sort": "asc", 157 | }) 158 | fmt.Fprint(w, `[{"id":1},{"id":2}]`) 159 | }) 160 | 161 | opt := &SearchProjectsOptions{ListOptions{2, 3}, "name", "asc"} 162 | projects, _, err := client.Projects.SearchProjects("query", opt) 163 | 164 | if err != nil { 165 | t.Errorf("Projects.SearchProjects returned error: %v", err) 166 | } 167 | 168 | want := []*Project{{ID: Int(1)}, {ID: Int(2)}} 169 | if !reflect.DeepEqual(want, projects) { 170 | t.Errorf("Projects.SearchProjects returned %+v, want %+v", projects, want) 171 | } 172 | } 173 | 174 | func TestCreateProject(t *testing.T) { 175 | mux, server, client := setup() 176 | defer teardown(server) 177 | 178 | mux.HandleFunc("/projects", func(w http.ResponseWriter, r *http.Request) { 179 | testMethod(t, r, "POST") 180 | testJsonBody(t, r, values{ 181 | "name": "n", 182 | }) 183 | 184 | fmt.Fprint(w, `{"id":1}`) 185 | }) 186 | 187 | opt := &CreateProjectOptions{Name: "n"} 188 | project, _, err := client.Projects.CreateProject(opt) 189 | 190 | if err != nil { 191 | t.Errorf("Projects.CreateProject returned error: %v", err) 192 | } 193 | 194 | want := &Project{ID: Int(1)} 195 | if !reflect.DeepEqual(want, project) { 196 | t.Errorf("Projects.CreateProject returned %+v, want %+v", project, want) 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /api/repositories.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2015, Sander van Harmelen 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | package gitlab 18 | 19 | import ( 20 | "bytes" 21 | "fmt" 22 | "net/url" 23 | ) 24 | 25 | // RepositoriesService handles communication with the repositories related 26 | // methods of the GitLab API. 27 | // 28 | // GitLab API docs: http://doc.gitlab.com/ce/api/repositories.html 29 | type RepositoriesService struct { 30 | client *Client 31 | } 32 | 33 | // Tag represents a GitLab repository tag. 34 | // 35 | // GitLab API docs: 36 | // http://doc.gitlab.com/ce/api/repositories.html#list-project-repository-tags 37 | type Tag struct { 38 | Commit *Commit `json:"commit"` 39 | Name string `json:"name"` 40 | Message string `json:"message"` 41 | } 42 | 43 | func (r Tag) String() string { 44 | return Stringify(r) 45 | } 46 | 47 | // ListTags gets a list of repository tags from a project, sorted by name in 48 | // reverse alphabetical order. 49 | // 50 | // GitLab API docs: 51 | // http://doc.gitlab.com/ce/api/repositories.html#list-project-repository-tags 52 | func (s *RepositoriesService) ListTags(pid interface{}) ([]*Tag, *Response, error) { 53 | project, err := parseID(pid) 54 | if err != nil { 55 | return nil, nil, err 56 | } 57 | u := fmt.Sprintf("projects/%s/repository/tags", url.QueryEscape(project)) 58 | 59 | req, err := s.client.NewRequest("GET", u, nil) 60 | if err != nil { 61 | return nil, nil, err 62 | } 63 | 64 | var t []*Tag 65 | resp, err := s.client.Do(req, &t) 66 | if err != nil { 67 | return nil, resp, err 68 | } 69 | 70 | return t, resp, err 71 | } 72 | 73 | // CreateTagOptions represents the available CreateTag() options. 74 | // 75 | // GitLab API docs: 76 | // http://doc.gitlab.com/ce/api/repositories.html#create-a-new-tag 77 | type CreateTagOptions struct { 78 | TagName string `url:"tag_name,omitempty" json:"tag_name,omitempty"` 79 | Ref string `url:"ref,omitempty" json:"ref,omitempty"` 80 | Message string `url:"message,omitempty" json:"message,omitempty"` 81 | } 82 | 83 | // CreateTag creates a new tag in the repository that points to the supplied ref. 84 | // 85 | // GitLab API docs: 86 | // http://doc.gitlab.com/ce/api/repositories.html#create-a-new-tag 87 | func (s *RepositoriesService) CreateTag( 88 | pid interface{}, 89 | opt *CreateTagOptions) (*Tag, *Response, error) { 90 | project, err := parseID(pid) 91 | if err != nil { 92 | return nil, nil, err 93 | } 94 | u := fmt.Sprintf("projects/%s/repository/tags", url.QueryEscape(project)) 95 | 96 | req, err := s.client.NewRequest("POST", u, opt) 97 | if err != nil { 98 | return nil, nil, err 99 | } 100 | 101 | t := new(Tag) 102 | resp, err := s.client.Do(req, t) 103 | if err != nil { 104 | return nil, resp, err 105 | } 106 | 107 | return t, resp, err 108 | } 109 | 110 | // TreeNode represents a GitLab repository file or directory. 111 | // 112 | // GitLab API docs: 113 | // http://doc.gitlab.com/ce/api/repositories.html#list-repository-tree 114 | type TreeNode struct { 115 | ID string `json:"id"` 116 | Name string `json:"name"` 117 | Type string `json:"type"` 118 | Mode string `json:"mode"` 119 | } 120 | 121 | func (t TreeNode) String() string { 122 | return Stringify(t) 123 | } 124 | 125 | // ListTreeOptions represents the available ListTree() options. 126 | // 127 | // GitLab API docs: 128 | // http://doc.gitlab.com/ce/api/repositories.html#list-repository-tree 129 | type ListTreeOptions struct { 130 | Path string `url:"path,omitempty" json:"path,omitempty"` 131 | RefName string `url:"ref_name,omitempty" json:"ref_name,omitempty"` 132 | } 133 | 134 | // ListTree gets a list of repository files and directories in a project. 135 | // 136 | // GitLab API docs: 137 | // http://doc.gitlab.com/ce/api/repositories.html#list-repository-tree 138 | func (s *RepositoriesService) ListTree( 139 | pid interface{}, 140 | opt *ListTreeOptions) ([]*TreeNode, *Response, error) { 141 | project, err := parseID(pid) 142 | if err != nil { 143 | return nil, nil, err 144 | } 145 | u := fmt.Sprintf("projects/%s/repository/tree", url.QueryEscape(project)) 146 | 147 | req, err := s.client.NewRequest("GET", u, opt) 148 | if err != nil { 149 | return nil, nil, err 150 | } 151 | 152 | var t []*TreeNode 153 | resp, err := s.client.Do(req, &t) 154 | if err != nil { 155 | return nil, resp, err 156 | } 157 | 158 | return t, resp, err 159 | } 160 | 161 | // RawFileContentOptions represents the available RawFileContent() options. 162 | // 163 | // GitLab API docs: 164 | // http://doc.gitlab.com/ce/api/repositories.html#raw-file-content 165 | type RawFileContentOptions struct { 166 | FilePath string `url:"filepath,omitempty" json:"filepath,omitempty"` 167 | } 168 | 169 | // RawFileContent gets the raw file contents for a file by commit SHA and path 170 | // 171 | // GitLab API docs: 172 | // http://doc.gitlab.com/ce/api/repositories.html#raw-file-content 173 | func (s *RepositoriesService) RawFileContent( 174 | pid interface{}, 175 | sha string, 176 | opt *RawFileContentOptions) ([]byte, *Response, error) { 177 | project, err := parseID(pid) 178 | if err != nil { 179 | return nil, nil, err 180 | } 181 | u := fmt.Sprintf("projects/%s/repository/blobs/%s", url.QueryEscape(project), sha) 182 | 183 | req, err := s.client.NewRequest("GET", u, opt) 184 | if err != nil { 185 | return nil, nil, err 186 | } 187 | 188 | var b bytes.Buffer 189 | resp, err := s.client.Do(req, &b) 190 | if err != nil { 191 | return nil, resp, err 192 | } 193 | 194 | return b.Bytes(), resp, err 195 | } 196 | 197 | // RawBlobContent gets the raw file contents for a blob by blob SHA. 198 | // 199 | // GitLab API docs: 200 | // http://doc.gitlab.com/ce/api/repositories.html#raw-blob-content 201 | func (s *RepositoriesService) RawBlobContent( 202 | pid interface{}, 203 | sha string) ([]byte, *Response, error) { 204 | project, err := parseID(pid) 205 | if err != nil { 206 | return nil, nil, err 207 | } 208 | u := fmt.Sprintf("projects/%s/repository/raw_blobs/%s", url.QueryEscape(project), sha) 209 | 210 | req, err := s.client.NewRequest("GET", u, nil) 211 | if err != nil { 212 | return nil, nil, err 213 | } 214 | 215 | var b bytes.Buffer 216 | resp, err := s.client.Do(req, &b) 217 | if err != nil { 218 | return nil, resp, err 219 | } 220 | 221 | return b.Bytes(), resp, err 222 | } 223 | 224 | // ArchiveOptions represents the available Archive() options. 225 | // 226 | // GitLab API docs: 227 | // http://doc.gitlab.com/ce/api/repositories.html#get-file-archive 228 | type ArchiveOptions struct { 229 | SHA string `url:"sha,omitempty" json:"sha,omitempty"` 230 | } 231 | 232 | // Archive gets an archive of the repository. 233 | // 234 | // GitLab API docs: 235 | // http://doc.gitlab.com/ce/api/repositories.html#get-file-archive 236 | func (s *RepositoriesService) Archive( 237 | pid interface{}, 238 | opt *ArchiveOptions) ([]byte, *Response, error) { 239 | project, err := parseID(pid) 240 | if err != nil { 241 | return nil, nil, err 242 | } 243 | u := fmt.Sprintf("projects/%s/repository/archive", url.QueryEscape(project)) 244 | 245 | req, err := s.client.NewRequest("GET", u, opt) 246 | if err != nil { 247 | return nil, nil, err 248 | } 249 | 250 | var b bytes.Buffer 251 | resp, err := s.client.Do(req, &b) 252 | if err != nil { 253 | return nil, resp, err 254 | } 255 | 256 | return b.Bytes(), resp, err 257 | } 258 | 259 | // Compare represents the result of a comparison of branches, tags or commits. 260 | // 261 | // GitLab API docs: 262 | // http://doc.gitlab.com/ce/api/repositories.html#compare-branches-tags-or-commits 263 | type Compare struct { 264 | Commit *Commit `json:"commit"` 265 | Commits []*Commit `json:"commits"` 266 | Diffs []*Diff `json:"diffs"` 267 | CompareTimeout bool `json:"compare_timeout"` 268 | CompareSameRef bool `json:"compare_same_ref"` 269 | } 270 | 271 | func (c Compare) String() string { 272 | return Stringify(c) 273 | } 274 | 275 | // CompareOptions represents the available Compare() options. 276 | // 277 | // GitLab API docs: 278 | // http://doc.gitlab.com/ce/api/repositories.html#compare-branches-tags-or-commits 279 | type CompareOptions struct { 280 | From string `url:"from,omitempty" json:"from,omitempty"` 281 | To string `url:"to,omitempty" json:"to,omitempty"` 282 | } 283 | 284 | // Compare compares branches, tags or commits. 285 | // 286 | // GitLab API docs: 287 | // http://doc.gitlab.com/ce/api/repositories.html#compare-branches-tags-or-commits 288 | func (s *RepositoriesService) Compare( 289 | pid interface{}, 290 | opt *CompareOptions) (*Compare, *Response, error) { 291 | project, err := parseID(pid) 292 | if err != nil { 293 | return nil, nil, err 294 | } 295 | u := fmt.Sprintf("projects/%s/repository/compare", url.QueryEscape(project)) 296 | 297 | req, err := s.client.NewRequest("GET", u, opt) 298 | if err != nil { 299 | return nil, nil, err 300 | } 301 | 302 | c := new(Compare) 303 | resp, err := s.client.Do(req, c) 304 | if err != nil { 305 | return nil, resp, err 306 | } 307 | 308 | return c, resp, err 309 | } 310 | 311 | // Contributor represents a GitLap contributor. 312 | // 313 | // GitLab API docs: http://doc.gitlab.com/ce/api/repositories.html#contributer 314 | type Contributor struct { 315 | Name string `json:"name,omitempty"` 316 | Email string `json:"email,omitempty"` 317 | Commits int `json:"commits,omitempty"` 318 | Additions int `json:"additions,omitempty"` 319 | Deletions int `json:"deletions,omitempty"` 320 | } 321 | 322 | func (c Contributor) String() string { 323 | return Stringify(c) 324 | } 325 | 326 | // Contributors gets the repository contributors list. 327 | // 328 | // GitLab API docs: http://doc.gitlab.com/ce/api/repositories.html#contributer 329 | func (s *RepositoriesService) Contributors(pid interface{}) ([]*Contributor, *Response, error) { 330 | project, err := parseID(pid) 331 | if err != nil { 332 | return nil, nil, err 333 | } 334 | u := fmt.Sprintf("projects/%s/repository/contributors", url.QueryEscape(project)) 335 | 336 | req, err := s.client.NewRequest("GET", u, nil) 337 | if err != nil { 338 | return nil, nil, err 339 | } 340 | 341 | var c []*Contributor 342 | resp, err := s.client.Do(req, &c) 343 | if err != nil { 344 | return nil, resp, err 345 | } 346 | 347 | return c, resp, err 348 | } 349 | -------------------------------------------------------------------------------- /api/repository_files.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2015, Sander van Harmelen 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | package gitlab 18 | 19 | import ( 20 | "fmt" 21 | "net/url" 22 | ) 23 | 24 | // RepositoryFilesService handles communication with the repository files 25 | // related methods of the GitLab API. 26 | // 27 | // GitLab API docs: http://doc.gitlab.com/ce/api/repository_files.html 28 | type RepositoryFilesService struct { 29 | client *Client 30 | } 31 | 32 | // File represents a GitLab repository file. 33 | // 34 | // GitLab API docs: http://doc.gitlab.com/ce/api/repository_files.html 35 | type File struct { 36 | FileName string `json:"file_name"` 37 | FilePath string `json:"file_path"` 38 | Size int `json:"size"` 39 | Encoding string `json:"encoding"` 40 | Content string `json:"content"` 41 | Ref string `json:"ref"` 42 | BlobID string `json:"blob_id"` 43 | CommitID string `json:"commit_id"` 44 | } 45 | 46 | func (r File) String() string { 47 | return Stringify(r) 48 | } 49 | 50 | // GetFileOptions represents the available GetFile() options. 51 | // 52 | // GitLab API docs: 53 | // http://doc.gitlab.com/ce/api/repository_files.html#get-file-from-respository 54 | type GetFileOptions struct { 55 | FilePath string `url:"file_path,omitempty" json:"file_path,omitempty"` 56 | Ref string `url:"ref,omitempty" json:"ref,omitempty"` 57 | } 58 | 59 | // GetFile allows you to receive information about a file in repository like 60 | // name, size, content. Note that file content is Base64 encoded. 61 | // 62 | // GitLab API docs: 63 | // http://doc.gitlab.com/ce/api/repository_files.html#get-file-from-respository 64 | func (s *RepositoryFilesService) GetFile( 65 | pid interface{}, 66 | opt *GetFileOptions) (*File, *Response, error) { 67 | project, err := parseID(pid) 68 | if err != nil { 69 | return nil, nil, err 70 | } 71 | u := fmt.Sprintf("projects/%s/repository/files", url.QueryEscape(project)) 72 | 73 | req, err := s.client.NewRequest("GET", u, opt) 74 | if err != nil { 75 | return nil, nil, err 76 | } 77 | 78 | f := new(File) 79 | resp, err := s.client.Do(req, f) 80 | if err != nil { 81 | return nil, resp, err 82 | } 83 | 84 | return f, resp, err 85 | } 86 | 87 | // FileInfo represents file details of a GitLab repository file. 88 | // 89 | // GitLab API docs: http://doc.gitlab.com/ce/api/repository_files.html 90 | type FileInfo struct { 91 | FilePath string `json:"file_path"` 92 | BranchName string `json:"branch_name"` 93 | } 94 | 95 | func (r FileInfo) String() string { 96 | return Stringify(r) 97 | } 98 | 99 | // CreateFileOptions represents the available CreateFile() options. 100 | // 101 | // GitLab API docs: 102 | // http://doc.gitlab.com/ce/api/repository_files.html#create-new-file-in-repository 103 | type CreateFileOptions struct { 104 | FilePath string `url:"file_path,omitempty" json:"file_path,omitempty"` 105 | BranchName string `url:"branch_name,omitempty" json:"branch_name,omitempty"` 106 | Encoding string `url:"encoding,omitempty" json:"encoding,omitempty"` 107 | Content string `url:"content,omitempty" json:"content,omitempty"` 108 | CommitMessage string `url:"commit_message,omitempty" json:"commit_message,omitempty"` 109 | } 110 | 111 | // CreateFile creates a new file in a repository. 112 | // 113 | // GitLab API docs: 114 | // http://doc.gitlab.com/ce/api/repository_files.html#create-new-file-in-repository 115 | func (s *RepositoryFilesService) CreateFile( 116 | pid interface{}, 117 | opt *CreateFileOptions) (*FileInfo, *Response, error) { 118 | project, err := parseID(pid) 119 | if err != nil { 120 | return nil, nil, err 121 | } 122 | u := fmt.Sprintf("projects/%s/repository/files", url.QueryEscape(project)) 123 | 124 | req, err := s.client.NewRequest("POST", u, opt) 125 | if err != nil { 126 | return nil, nil, err 127 | } 128 | 129 | f := new(FileInfo) 130 | resp, err := s.client.Do(req, f) 131 | if err != nil { 132 | return nil, resp, err 133 | } 134 | 135 | return f, resp, err 136 | } 137 | 138 | // UpdateFileOptions represents the available UpdateFile() options. 139 | // 140 | // GitLab API docs: 141 | // http://doc.gitlab.com/ce/api/repository_files.html#update-existing-file-in-repository 142 | type UpdateFileOptions struct { 143 | FilePath string `url:"file_path,omitempty" json:"file_path,omitempty"` 144 | BranchName string `url:"branch_name,omitempty" json:"branch_name,omitempty"` 145 | Encoding string `url:"encoding,omitempty" json:"encoding,omitempty"` 146 | Content string `url:"content,omitempty" json:"content,omitempty"` 147 | CommitMessage string `url:"commit_message,omitempty" json:"commit_message,omitempty"` 148 | } 149 | 150 | // UpdateFile updates an existing file in a repository 151 | // 152 | // GitLab API docs: 153 | // http://doc.gitlab.com/ce/api/repository_files.html#update-existing-file-in-repository 154 | func (s *RepositoryFilesService) UpdateFile( 155 | pid interface{}, 156 | opt *UpdateFileOptions) (*FileInfo, *Response, error) { 157 | project, err := parseID(pid) 158 | if err != nil { 159 | return nil, nil, err 160 | } 161 | u := fmt.Sprintf("projects/%s/repository/files", url.QueryEscape(project)) 162 | 163 | req, err := s.client.NewRequest("PUT", u, opt) 164 | if err != nil { 165 | return nil, nil, err 166 | } 167 | 168 | f := new(FileInfo) 169 | resp, err := s.client.Do(req, f) 170 | if err != nil { 171 | return nil, resp, err 172 | } 173 | 174 | return f, resp, err 175 | } 176 | 177 | // DeleteFileOptions represents the available DeleteFile() options. 178 | // 179 | // GitLab API docs: 180 | // http://doc.gitlab.com/ce/api/repository_files.html#delete-existing-file-in-repository 181 | type DeleteFileOptions struct { 182 | FilePath string `url:"file_path,omitempty" json:"file_path,omitempty"` 183 | BranchName string `url:"branch_name,omitempty" json:"branch_name,omitempty"` 184 | CommitMessage string `url:"commit_message,omitempty" json:"commit_message,omitempty"` 185 | } 186 | 187 | // DeleteFile deletes an existing file in a repository 188 | // 189 | // GitLab API docs: 190 | // http://doc.gitlab.com/ce/api/repository_files.html#delete-existing-file-in-repository 191 | func (s *RepositoryFilesService) DeleteFile( 192 | pid interface{}, 193 | opt *DeleteFileOptions) (*FileInfo, *Response, error) { 194 | project, err := parseID(pid) 195 | if err != nil { 196 | return nil, nil, err 197 | } 198 | u := fmt.Sprintf("projects/%s/repository/files", url.QueryEscape(project)) 199 | 200 | req, err := s.client.NewRequest("DELETE", u, opt) 201 | if err != nil { 202 | return nil, nil, err 203 | } 204 | 205 | f := new(FileInfo) 206 | resp, err := s.client.Do(req, f) 207 | if err != nil { 208 | return nil, resp, err 209 | } 210 | 211 | return f, resp, err 212 | } 213 | -------------------------------------------------------------------------------- /api/services.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2015, Sander van Harmelen 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | package gitlab 18 | 19 | import ( 20 | "fmt" 21 | "net/url" 22 | "time" 23 | ) 24 | 25 | // ServicesService handles communication with the services related methods of 26 | // the GitLab API. 27 | // 28 | // GitLab API docs: http://doc.gitlab.com/ce/api/services.html 29 | type ServicesService struct { 30 | client *Client 31 | } 32 | 33 | type Service struct { 34 | ID *int `json:"id"` 35 | Title *string `json:"title"` 36 | CreatedAt *time.Time `json:"created_at"` 37 | UpdatedAt *time.Time `json:"created_at"` 38 | Active *bool `json:"active"` 39 | PushEvents *bool `json:"push_events"` 40 | IssuesEvents *bool `json:"issues_events"` 41 | MergeRequestsEvents *bool `json:"merge_requests_events"` 42 | TagPushEvents *bool `json:"tag_push_events"` 43 | NoteEvents *bool `json:"note_events"` 44 | } 45 | 46 | // SetGitLabCIServiceOptions represents the available SetGitLabCIService() 47 | // options. 48 | // 49 | // GitLab API docs: 50 | // http://doc.gitlab.com/ce/api/services.html#edit-gitlab-ci-service 51 | type SetGitLabCIServiceOptions struct { 52 | Token string `url:"token,omitempty" json:"token,omitempty"` 53 | ProjectURL string `url:"project_url,omitempty" json:"project_url,omitempty"` 54 | } 55 | 56 | // SetGitLabCIService sets GitLab CI service for a project. 57 | // 58 | // GitLab API docs: 59 | // http://doc.gitlab.com/ce/api/services.html#edit-gitlab-ci-service 60 | func (s *ServicesService) SetGitLabCIService( 61 | pid interface{}, 62 | opt *SetGitLabCIServiceOptions) (*Response, error) { 63 | project, err := parseID(pid) 64 | if err != nil { 65 | return nil, err 66 | } 67 | u := fmt.Sprintf("projects/%s/services/gitlab-ci", url.QueryEscape(project)) 68 | 69 | req, err := s.client.NewRequest("PUT", u, opt) 70 | if err != nil { 71 | return nil, err 72 | } 73 | 74 | resp, err := s.client.Do(req, nil) 75 | if err != nil { 76 | return resp, err 77 | } 78 | 79 | return resp, err 80 | } 81 | 82 | // DeleteGitLabCIService deletes GitLab CI service settings for a project. 83 | // 84 | // GitLab API docs: 85 | // http://doc.gitlab.com/ce/api/services.html#delete-gitlab-ci-service 86 | func (s *ServicesService) DeleteGitLabCIService(pid interface{}) (*Response, error) { 87 | project, err := parseID(pid) 88 | if err != nil { 89 | return nil, err 90 | } 91 | u := fmt.Sprintf("projects/%s/services/gitlab-ci", url.QueryEscape(project)) 92 | 93 | req, err := s.client.NewRequest("DELETE", u, nil) 94 | if err != nil { 95 | return nil, err 96 | } 97 | 98 | resp, err := s.client.Do(req, nil) 99 | if err != nil { 100 | return resp, err 101 | } 102 | 103 | return resp, err 104 | } 105 | 106 | // SetHipChatServiceOptions represents the available SetHipChatService() 107 | // options. 108 | // 109 | // GitLab API docs: 110 | // http://doc.gitlab.com/ce/api/services.html#edit-hipchat-service 111 | type SetHipChatServiceOptions struct { 112 | Token string `url:"token,omitempty" json:"token,omitempty" ` 113 | Room string `url:"room,omitempty" json:"room,omitempty"` 114 | } 115 | 116 | // SetHipChatService sets HipChat service for a project 117 | // 118 | // GitLab API docs: 119 | // http://doc.gitlab.com/ce/api/services.html#edit-hipchat-service 120 | func (s *ServicesService) SetHipChatService( 121 | pid interface{}, 122 | opt *SetHipChatServiceOptions) (*Response, error) { 123 | project, err := parseID(pid) 124 | if err != nil { 125 | return nil, err 126 | } 127 | u := fmt.Sprintf("projects/%s/services/hipchat", url.QueryEscape(project)) 128 | 129 | req, err := s.client.NewRequest("PUT", u, opt) 130 | if err != nil { 131 | return nil, err 132 | } 133 | 134 | resp, err := s.client.Do(req, nil) 135 | if err != nil { 136 | return resp, err 137 | } 138 | 139 | return resp, err 140 | } 141 | 142 | // DeleteHipChatService deletes HipChat service for project. 143 | // 144 | // GitLab API docs: 145 | // http://doc.gitlab.com/ce/api/services.html#delete-hipchat-service 146 | func (s *ServicesService) DeleteHipChatService(pid interface{}) (*Response, error) { 147 | project, err := parseID(pid) 148 | if err != nil { 149 | return nil, err 150 | } 151 | u := fmt.Sprintf("projects/%s/services/hipchat", url.QueryEscape(project)) 152 | 153 | req, err := s.client.NewRequest("DELETE", u, nil) 154 | if err != nil { 155 | return nil, err 156 | } 157 | 158 | resp, err := s.client.Do(req, nil) 159 | if err != nil { 160 | return resp, err 161 | } 162 | 163 | return resp, err 164 | } 165 | 166 | // SetDroneCIServiceOptions represents the available SetDroneCIService() 167 | // options. 168 | // 169 | // GitLab API docs: 170 | // http://doc.gitlab.com/ce/api/services.html#createedit-drone-ci-service 171 | type SetDroneCIServiceOptions struct { 172 | Token string `url:"token" json:"token" ` 173 | DroneURL string `url:"drone_url" json:"drone_url"` 174 | EnableSSLVerification string `url:"enable_ssl_verification,omitempty" json:"enable_ssl_verification,omitempty"` 175 | } 176 | 177 | // SetDroneCIService sets Drone CI service for a project. 178 | // 179 | // GitLab API docs: 180 | // http://doc.gitlab.com/ce/api/services.html#createedit-drone-ci-service 181 | func (s *ServicesService) SetDroneCIService( 182 | pid interface{}, 183 | opt *SetDroneCIServiceOptions) (*Response, error) { 184 | project, err := parseID(pid) 185 | if err != nil { 186 | return nil, err 187 | } 188 | u := fmt.Sprintf("projects/%s/services/drone-ci", url.QueryEscape(project)) 189 | 190 | req, err := s.client.NewRequest("PUT", u, opt) 191 | if err != nil { 192 | return nil, err 193 | } 194 | 195 | resp, err := s.client.Do(req, nil) 196 | if err != nil { 197 | return resp, err 198 | } 199 | 200 | return resp, err 201 | } 202 | 203 | // DeleteDroneCIService deletes Drone CI service settings for a project. 204 | // 205 | // GitLab API docs: 206 | // http://doc.gitlab.com/ce/api/services.html#delete-drone-ci-service 207 | func (s *ServicesService) DeleteDroneCIService(pid interface{}) (*Response, error) { 208 | project, err := parseID(pid) 209 | if err != nil { 210 | return nil, err 211 | } 212 | u := fmt.Sprintf("projects/%s/services/drone-ci", url.QueryEscape(project)) 213 | 214 | req, err := s.client.NewRequest("DELETE", u, nil) 215 | if err != nil { 216 | return nil, err 217 | } 218 | 219 | resp, err := s.client.Do(req, nil) 220 | if err != nil { 221 | return resp, err 222 | } 223 | 224 | return resp, err 225 | } 226 | 227 | // DroneCIServiceProperties represents Drone CI specific properties. 228 | type DroneCIServiceProperties struct { 229 | Token *string `url:"token" json:"token"` 230 | DroneURL *string `url:"drone_url" json:"drone_url"` 231 | EnableSSLVerification *string `url:"enable_ssl_verification" json:"enable_ssl_verification"` 232 | } 233 | 234 | // DroneCIService represents Drone CI service settings. 235 | type DroneCIService struct { 236 | Service 237 | Properties *DroneCIServiceProperties `json:"properties"` 238 | } 239 | 240 | // GetDroneCIService gets Drone CI service settings for a project. 241 | // 242 | // GitLab API docs: 243 | // http://doc.gitlab.com/ce/api/services.html#get-drone-ci-service-settings 244 | func (s *ServicesService) GetDroneCIService(pid interface{}) (*DroneCIService, *Response, error) { 245 | project, err := parseID(pid) 246 | if err != nil { 247 | return nil, nil, err 248 | } 249 | u := fmt.Sprintf("projects/%s/services/drone-ci", url.QueryEscape(project)) 250 | 251 | req, err := s.client.NewRequest("GET", u, nil) 252 | if err != nil { 253 | return nil, nil, err 254 | } 255 | 256 | opt := new(DroneCIService) 257 | resp, err := s.client.Do(req, opt) 258 | if err != nil { 259 | return nil, resp, err 260 | } 261 | 262 | return opt, resp, err 263 | } 264 | -------------------------------------------------------------------------------- /api/services_test.go: -------------------------------------------------------------------------------- 1 | package gitlab 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "reflect" 7 | "testing" 8 | ) 9 | 10 | func TestSetDroneCIService(t *testing.T) { 11 | mux, server, client := setup() 12 | defer teardown(server) 13 | 14 | mux.HandleFunc("/projects/1/services/drone-ci", func(w http.ResponseWriter, r *http.Request) { 15 | testMethod(t, r, "PUT") 16 | testJsonBody(t, r, values{ 17 | "token": "t", 18 | "drone_url": "u", 19 | "enable_ssl_verification": "true", 20 | }) 21 | }) 22 | 23 | opt := &SetDroneCIServiceOptions{"t", "u", "true"} 24 | _, err := client.Services.SetDroneCIService(1, opt) 25 | 26 | if err != nil { 27 | t.Fatalf("Services.SetDroneCIService returns an error: %v", err) 28 | } 29 | } 30 | 31 | func TestDeleteDroneCIService(t *testing.T) { 32 | mux, server, client := setup() 33 | defer teardown(server) 34 | 35 | mux.HandleFunc("/projects/1/services/drone-ci", func(w http.ResponseWriter, r *http.Request) { 36 | testMethod(t, r, "DELETE") 37 | }) 38 | 39 | _, err := client.Services.DeleteDroneCIService(1) 40 | 41 | if err != nil { 42 | t.Fatalf("Services.DeleteDroneCIService returns an error: %v", err) 43 | } 44 | } 45 | 46 | func TestGetDroneCIService(t *testing.T) { 47 | mux, server, client := setup() 48 | defer teardown(server) 49 | 50 | mux.HandleFunc("/projects/1/services/drone-ci", func(w http.ResponseWriter, r *http.Request) { 51 | testMethod(t, r, "GET") 52 | fmt.Fprint(w, `{"id":1}`) 53 | }) 54 | want := &DroneCIService{Service: Service{ID: Int(1)}} 55 | 56 | service, _, err := client.Services.GetDroneCIService(1) 57 | 58 | if err != nil { 59 | t.Fatalf("Services.GetDroneCIService returns an error: %v", err) 60 | } 61 | 62 | if !reflect.DeepEqual(want, service) { 63 | t.Errorf("Services.GetDroneCIService returned %+v, want %+v", service, want) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /api/session.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2015, Sander van Harmelen 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | package gitlab 18 | 19 | import "time" 20 | 21 | // SessionService handles communication with the session related methods of 22 | // the GitLab API. 23 | // 24 | // GitLab API docs: http://doc.gitlab.com/ce/api/session.html 25 | type SessionService struct { 26 | client *Client 27 | } 28 | 29 | // Session represents a GitLab session. 30 | // 31 | // GitLab API docs: http://doc.gitlab.com/ce/api/session.html#session 32 | type Session struct { 33 | ID int `json:"id"` 34 | Username string `json:"username"` 35 | Email string `json:"email"` 36 | Name string `json:"name"` 37 | PrivateToken string `json:"private_token"` 38 | Blocked bool `json:"blocked"` 39 | CreatedAt time.Time `json:"created_at"` 40 | Bio interface{} `json:"bio"` 41 | Skype string `json:"skype"` 42 | Linkedin string `json:"linkedin"` 43 | Twitter string `json:"twitter"` 44 | WebsiteURL string `json:"website_url"` 45 | DarkScheme bool `json:"dark_scheme"` 46 | ThemeID int `json:"theme_id"` 47 | IsAdmin bool `json:"is_admin"` 48 | CanCreateGroup bool `json:"can_create_group"` 49 | CanCreateTeam bool `json:"can_create_team"` 50 | CanCreateProject bool `json:"can_create_project"` 51 | } 52 | 53 | // GetSessionOptions represents the available Session() options. 54 | // 55 | // GitLab API docs: http://doc.gitlab.com/ce/api/session.html#session 56 | type GetSessionOptions struct { 57 | Login string `url:"login,omitempty" json:"login,omitempty"` 58 | Email string `url:"email,omitempty" json:"email,omitempty"` 59 | Password string `url:"password,omitempty" json:"password,omitempty"` 60 | } 61 | 62 | // GetSession logs in to get private token. 63 | // 64 | // GitLab API docs: http://doc.gitlab.com/ce/api/session.html#session 65 | func (s *SessionService) GetSession(opt *GetSessionOptions) (*Session, *Response, error) { 66 | req, err := s.client.NewRequest("POST", "session", opt) 67 | if err != nil { 68 | return nil, nil, err 69 | } 70 | 71 | session := new(Session) 72 | resp, err := s.client.Do(req, session) 73 | if err != nil { 74 | return nil, resp, err 75 | } 76 | 77 | return session, resp, err 78 | } 79 | -------------------------------------------------------------------------------- /api/settings.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2015, Sander van Harmelen 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | package gitlab 18 | 19 | import "time" 20 | 21 | // SettingsService handles communication with the application SettingsService 22 | // related methods of the GitLab API. 23 | // 24 | // GitLab API docs: http://doc.gitlab.com/ce/api/settings.html 25 | type SettingsService struct { 26 | client *Client 27 | } 28 | 29 | // Settings represents the GitLab application settings. 30 | // 31 | // GitLab API docs: http://doc.gitlab.com/ce/api/settings.html 32 | type Settings struct { 33 | ID int `json:"id"` 34 | DefaultProjectsLimit int `json:"default_projects_limit"` 35 | SignupEnabled bool `json:"signup_enabled"` 36 | SigninEnabled bool `json:"signin_enabled"` 37 | GravatarEnabled bool `json:"gravatar_enabled"` 38 | SignInText string `json:"sign_in_text"` 39 | CreatedAt time.Time `json:"created_at"` 40 | UpdatedAt time.Time `json:"updated_at"` 41 | HomePageURL string `json:"home_page_url"` 42 | DefaultBranchProtection int `json:"default_branch_protection"` 43 | TwitterSharingEnabled bool `json:"twitter_sharing_enabled"` 44 | RestrictedVisibilityLevels []VisibilityLevel `json:"restricted_visibility_levels"` 45 | MaxAttachmentSize int `json:"max_attachment_size"` 46 | SessionExpireDelay int `json:"session_expire_delay"` 47 | DefaultProjectVisibility int `json:"default_project_visibility"` 48 | DefaultSnippetVisibility int `json:"default_snippet_visibility"` 49 | RestrictedSignupDomains []string `json:"restricted_signup_domains"` 50 | UserOauthApplications bool `json:"user_oauth_applications"` 51 | AfterSignOutPath string `json:"after_sign_out_path"` 52 | } 53 | 54 | func (s Settings) String() string { 55 | return Stringify(s) 56 | } 57 | 58 | // GetSettings gets the current application settings. 59 | // 60 | // GitLab API docs: 61 | // http://doc.gitlab.com/ce/api/settings.html#get-current-application.settings 62 | func (s *SettingsService) GetSettings() (*Settings, *Response, error) { 63 | req, err := s.client.NewRequest("GET", "application/settings", nil) 64 | if err != nil { 65 | return nil, nil, err 66 | } 67 | 68 | as := new(Settings) 69 | resp, err := s.client.Do(req, as) 70 | if err != nil { 71 | return nil, resp, err 72 | } 73 | 74 | return as, resp, err 75 | } 76 | 77 | // UpdateSettingsOptions represents the available UpdateSettings() options. 78 | // 79 | // GitLab API docs: 80 | // http://doc.gitlab.com/ce/api/settings.html#change-application.settings 81 | type UpdateSettingsOptions struct { 82 | DefaultProjectsLimit int `url:"default_projects_limit,omitempty" json:"default_projects_limit,omitempty"` 83 | SignupEnabled bool `url:"signup_enabled,omitempty" json:"signup_enabled,omitempty"` 84 | SigninEnabled bool `url:"signin_enabled,omitempty" json:"signin_enabled,omitempty"` 85 | GravatarEnabled bool `url:"gravatar_enabled,omitempty" json:"gravatar_enabled,omitempty"` 86 | SignInText string `url:"sign_in_text,omitempty" json:"sign_in_text,omitempty"` 87 | HomePageURL string `url:"home_page_url,omitempty" json:"home_page_url,omitempty"` 88 | DefaultBranchProtection int `url:"default_branch_protection,omitempty" json:"default_branch_protection,omitempty"` 89 | TwitterSharingEnabled bool `url:"twitter_sharing_enabled,omitempty" json:"twitter_sharing_enabled,omitempty"` 90 | RestrictedVisibilityLevels []VisibilityLevel `url:"restricted_visibility_levels,omitempty" json:"restricted_visibility_levels,omitempty"` 91 | MaxAttachmentSize int `url:"max_attachment_size,omitempty" json:"max_attachment_size,omitempty"` 92 | SessionExpireDelay int `url:"session_expire_delay,omitempty" json:"session_expire_delay,omitempty"` 93 | DefaultProjectVisibility int `url:"default_project_visibility,omitempty" json:"default_project_visibility,omitempty"` 94 | DefaultSnippetVisibility int `url:"default_snippet_visibility,omitempty" json:"default_snippet_visibility,omitempty"` 95 | RestrictedSignupDomains []string `url:"restricted_signup_domains,omitempty" json:"restricted_signup_domains,omitempty"` 96 | UserOauthApplications bool `url:"user_oauth_applications,omitempty" json:"user_oauth_applications,omitempty"` 97 | AfterSignOutPath string `url:"after_sign_out_path,omitempty" json:"after_sign_out_path,omitempty"` 98 | } 99 | 100 | // UpdateSettings updates the application settings. 101 | // 102 | // GitLab API docs: 103 | // http://doc.gitlab.com/ce/api/settings.html#change-application.settings 104 | func (s *SettingsService) UpdateSettings(opt *UpdateSettingsOptions) (*Settings, *Response, error) { 105 | req, err := s.client.NewRequest("PUT", "application/settings", opt) 106 | if err != nil { 107 | return nil, nil, err 108 | } 109 | 110 | as := new(Settings) 111 | resp, err := s.client.Do(req, as) 112 | if err != nil { 113 | return nil, resp, err 114 | } 115 | 116 | return as, resp, err 117 | } 118 | -------------------------------------------------------------------------------- /api/strings.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2015, Sander van Harmelen 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | package gitlab 18 | 19 | import ( 20 | "bytes" 21 | "fmt" 22 | "io" 23 | 24 | "reflect" 25 | ) 26 | 27 | // Stringify attempts to create a reasonable string representation of types in 28 | // the GitHub library. It does things like resolve pointers to their values 29 | // and omits struct fields with nil values. 30 | func Stringify(message interface{}) string { 31 | var buf bytes.Buffer 32 | v := reflect.ValueOf(message) 33 | stringifyValue(&buf, v) 34 | return buf.String() 35 | } 36 | 37 | // stringifyValue was heavily inspired by the goprotobuf library. 38 | func stringifyValue(w io.Writer, val reflect.Value) { 39 | if val.Kind() == reflect.Ptr && val.IsNil() { 40 | w.Write([]byte("")) 41 | return 42 | } 43 | 44 | v := reflect.Indirect(val) 45 | 46 | switch v.Kind() { 47 | case reflect.String: 48 | fmt.Fprintf(w, `"%s"`, v) 49 | case reflect.Slice: 50 | w.Write([]byte{'['}) 51 | for i := 0; i < v.Len(); i++ { 52 | if i > 0 { 53 | w.Write([]byte{' '}) 54 | } 55 | 56 | stringifyValue(w, v.Index(i)) 57 | } 58 | 59 | w.Write([]byte{']'}) 60 | return 61 | case reflect.Struct: 62 | if v.Type().Name() != "" { 63 | w.Write([]byte(v.Type().String())) 64 | } 65 | 66 | w.Write([]byte{'{'}) 67 | 68 | var sep bool 69 | for i := 0; i < v.NumField(); i++ { 70 | fv := v.Field(i) 71 | if fv.Kind() == reflect.Ptr && fv.IsNil() { 72 | continue 73 | } 74 | if fv.Kind() == reflect.Slice && fv.IsNil() { 75 | continue 76 | } 77 | 78 | if sep { 79 | w.Write([]byte(", ")) 80 | } else { 81 | sep = true 82 | } 83 | 84 | w.Write([]byte(v.Type().Field(i).Name)) 85 | w.Write([]byte{':'}) 86 | stringifyValue(w, fv) 87 | } 88 | 89 | w.Write([]byte{'}'}) 90 | default: 91 | if v.CanInterface() { 92 | fmt.Fprint(w, v.Interface()) 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /api/system_hooks.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2015, Sander van Harmelen 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | package gitlab 18 | 19 | import ( 20 | "fmt" 21 | "time" 22 | ) 23 | 24 | // SystemHooksService handles communication with the system hooks related 25 | // methods of the GitLab API. 26 | // 27 | // GitLab API docs: http://doc.gitlab.com/ce/api/system_hooks.html 28 | type SystemHooksService struct { 29 | client *Client 30 | } 31 | 32 | // Hook represents a GitLap system hook. 33 | // 34 | // GitLab API docs: http://doc.gitlab.com/ce/api/system_hooks.html 35 | type Hook struct { 36 | ID int `json:"id"` 37 | URL string `json:"url"` 38 | CreatedAt time.Time `json:"created_at"` 39 | } 40 | 41 | func (h Hook) String() string { 42 | return Stringify(h) 43 | } 44 | 45 | // ListHooks gets a list of system hooks. 46 | // 47 | // GitLab API docs: 48 | // http://doc.gitlab.com/ce/api/system_hooks.html#list-system-hooks 49 | func (s *SystemHooksService) ListHooks() ([]*Hook, *Response, error) { 50 | req, err := s.client.NewRequest("GET", "hooks", nil) 51 | if err != nil { 52 | return nil, nil, err 53 | } 54 | 55 | var h []*Hook 56 | resp, err := s.client.Do(req, &h) 57 | if err != nil { 58 | return nil, resp, err 59 | } 60 | 61 | return h, resp, err 62 | } 63 | 64 | // AddHookOptions represents the available AddHook() options. 65 | // 66 | // GitLab API docs: 67 | // http://doc.gitlab.com/ce/api/system_hooks.html#add-new-system-hook-hook 68 | type AddHookOptions struct { 69 | URL string `url:"url,omitempty" json:"url,omitempty"` 70 | } 71 | 72 | // AddHook adds a new system hook hook. 73 | // 74 | // GitLab API docs: 75 | // http://doc.gitlab.com/ce/api/system_hooks.html#add-new-system-hook-hook 76 | func (s *SystemHooksService) AddHook(opt *AddHookOptions) (*Hook, *Response, error) { 77 | req, err := s.client.NewRequest("POST", "hooks", opt) 78 | if err != nil { 79 | return nil, nil, err 80 | } 81 | 82 | h := new(Hook) 83 | resp, err := s.client.Do(req, h) 84 | if err != nil { 85 | return nil, resp, err 86 | } 87 | 88 | return h, resp, err 89 | } 90 | 91 | // HookEvent represents an event triggert by a GitLab system hook. 92 | // 93 | // GitLab API docs: http://doc.gitlab.com/ce/api/system_hooks.html 94 | type HookEvent struct { 95 | EventName string `json:"event_name"` 96 | Name string `json:"name"` 97 | Path string `json:"path"` 98 | ProjectID int `json:"project_id"` 99 | OwnerName string `json:"owner_name"` 100 | OwnerEmail string `json:"owner_email"` 101 | } 102 | 103 | func (h HookEvent) String() string { 104 | return Stringify(h) 105 | } 106 | 107 | // TestHook tests a system hook. 108 | // 109 | // GitLab API docs: 110 | // http://doc.gitlab.com/ce/api/system_hooks.html#test-system-hook 111 | func (s *SystemHooksService) TestHook(hook int) (*HookEvent, *Response, error) { 112 | u := fmt.Sprintf("hooks/%d", hook) 113 | 114 | req, err := s.client.NewRequest("GET", u, nil) 115 | if err != nil { 116 | return nil, nil, err 117 | } 118 | 119 | h := new(HookEvent) 120 | resp, err := s.client.Do(req, h) 121 | if err != nil { 122 | return nil, resp, err 123 | } 124 | 125 | return h, resp, err 126 | } 127 | 128 | // DeleteHook deletes a system hook. This is an idempotent API function and 129 | // returns 200 OK even if the hook is not available. If the hook is deleted it 130 | // is also returned as JSON. 131 | // 132 | // GitLab API docs: 133 | // http://doc.gitlab.com/ce/api/system_hooks.html#delete-system-hook 134 | func (s *SystemHooksService) DeleteHook(hook int) (*Response, error) { 135 | u := fmt.Sprintf("hooks/%d", hook) 136 | 137 | req, err := s.client.NewRequest("DELETE", u, nil) 138 | if err != nil { 139 | return nil, err 140 | } 141 | 142 | resp, err := s.client.Do(req, nil) 143 | if err != nil { 144 | return resp, err 145 | } 146 | 147 | return resp, err 148 | } 149 | -------------------------------------------------------------------------------- /api/users.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2015, Sander van Harmelen 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | package gitlab 18 | 19 | import ( 20 | "fmt" 21 | "time" 22 | ) 23 | 24 | // UsersService handles communication with the user related methods of 25 | // the GitLab API. 26 | // 27 | // GitLab API docs: http://doc.gitlab.com/ce/api/users.html 28 | type UsersService struct { 29 | client *Client 30 | } 31 | 32 | // User represents a GitLab user. 33 | // 34 | // GitLab API docs: http://doc.gitlab.com/ce/api/users.html 35 | type User struct { 36 | ID int `json:"id"` 37 | Username string `json:"username"` 38 | Email string `json:"email"` 39 | Name string `json:"name"` 40 | State string `json:"state"` 41 | CreatedAt time.Time `json:"created_at"` 42 | Bio string `json:"bio"` 43 | Skype string `json:"skype"` 44 | Linkedin string `json:"linkedin"` 45 | Twitter string `json:"twitter"` 46 | WebsiteURL string `json:"website_url"` 47 | ExternUID string `json:"extern_uid"` 48 | Provider string `json:"provider"` 49 | ThemeID int `json:"theme_id"` 50 | ColorSchemeID int `json:"color_scheme_id"` 51 | IsAdmin bool `json:"is_admin"` 52 | AvatarURL string `json:"avatar_url"` 53 | CanCreateGroup bool `json:"can_create_group"` 54 | CanCreateProject bool `json:"can_create_project"` 55 | ProjectsLimit int `json:"projects_limit"` 56 | CurrentSignInAt *time.Time `json:"current_sign_in_at"` 57 | TwoFactorEnabled bool `json:"two_factor_enabled"` 58 | } 59 | 60 | // ListUsersOptions represents the available ListUsers() options. 61 | // 62 | // GitLab API docs: http://doc.gitlab.com/ce/api/users.html#list-users 63 | type ListUsersOptions struct { 64 | ListOptions 65 | Active bool `url:"active,omitempty" json:"active,omitempty"` 66 | Search string `url:"search,omitempty" json:"search,omitempty"` 67 | } 68 | 69 | // ListUsers gets a list of users. 70 | // 71 | // GitLab API docs: http://doc.gitlab.com/ce/api/users.html#list-users 72 | func (s *UsersService) ListUsers(opt *ListUsersOptions) ([]*User, *Response, error) { 73 | req, err := s.client.NewRequest("GET", "users", opt) 74 | if err != nil { 75 | return nil, nil, err 76 | } 77 | 78 | var usr []*User 79 | resp, err := s.client.Do(req, &usr) 80 | if err != nil { 81 | return nil, resp, err 82 | } 83 | 84 | return usr, resp, err 85 | } 86 | 87 | // GetUser gets a single user. 88 | // 89 | // GitLab API docs: http://doc.gitlab.com/ce/api/users.html#single-user 90 | func (s *UsersService) GetUser(user int) (*User, *Response, error) { 91 | u := fmt.Sprintf("users/%d", user) 92 | 93 | req, err := s.client.NewRequest("GET", u, nil) 94 | if err != nil { 95 | return nil, nil, err 96 | } 97 | 98 | usr := new(User) 99 | resp, err := s.client.Do(req, usr) 100 | if err != nil { 101 | return nil, resp, err 102 | } 103 | 104 | return usr, resp, err 105 | } 106 | 107 | // CreateUserOptions represents the available CreateUser() options. 108 | // 109 | // GitLab API docs: http://doc.gitlab.com/ce/api/users.html#user-creation 110 | type CreateUserOptions struct { 111 | Email string `url:"email,omitempty" json:"email,omitempty"` 112 | Password string `url:"password,omitempty" json:"password,omitempty"` 113 | Username string `url:"username,omitempty" json:"username,omitempty"` 114 | Name string `url:"name,omitempty" json:"name,omitempty"` 115 | Skype string `url:"skype,omitempty" json:"skype,omitempty"` 116 | Linkedin string `url:"linkedin,omitempty" json:"linkedin,omitempty"` 117 | Twitter string `url:"twitter,omitempty" json:"twitter,omitempty"` 118 | WebsiteURL string `url:"website_url,omitempty" json:"website_url,omitempty"` 119 | ProjectsLimit int `url:"projects_limit,omitempty" json:"projects_limit,omitempty"` 120 | ExternUID string `url:"extern_uid,omitempty" json:"extern_uid,omitempty"` 121 | Provider string `url:"provider,omitempty" json:"provider,omitempty"` 122 | Bio string `url:"bio,omitempty" json:"bio,omitempty"` 123 | Admin bool `url:"admin,omitempty" json:"admin,omitempty"` 124 | CanCreateGroup bool `url:"can_create_group,omitempty" json:"can_create_group,omitempty"` 125 | Confirm bool `url:"confirm,omitempty" json:"confirm,omitempty"` 126 | } 127 | 128 | // CreateUser creates a new user. Note only administrators can create new users. 129 | // 130 | // GitLab API docs: http://doc.gitlab.com/ce/api/users.html#user-creation 131 | func (s *UsersService) CreateUser(opt *CreateUserOptions) (*User, *Response, error) { 132 | req, err := s.client.NewRequest("POST", "users", opt) 133 | if err != nil { 134 | return nil, nil, err 135 | } 136 | 137 | usr := new(User) 138 | resp, err := s.client.Do(req, usr) 139 | if err != nil { 140 | return nil, resp, err 141 | } 142 | 143 | return usr, resp, err 144 | } 145 | 146 | // ModifyUserOptions represents the available ModifyUser() options. 147 | // 148 | // GitLab API docs: http://doc.gitlab.com/ce/api/users.html#user-modification 149 | type ModifyUserOptions struct { 150 | Email string `url:"email,omitempty" json:"email,omitempty"` 151 | Password string `url:"password,omitempty" json:"password,omitempty"` 152 | Username string `url:"username,omitempty" json:"username,omitempty"` 153 | Name string `url:"name,omitempty" json:"name,omitempty"` 154 | Skype string `url:"skype,omitempty" json:"skype,omitempty"` 155 | Linkedin string `url:"linkedin,omitempty" json:"linkedin,omitempty"` 156 | Twitter string `url:"twitter,omitempty" json:"twitter,omitempty"` 157 | WebsiteURL string `url:"website_url,omitempty" json:"website_url,omitempty"` 158 | ProjectsLimit int `url:"projects_limit,omitempty" json:"projects_limit,omitempty"` 159 | ExternUID string `url:"extern_uid,omitempty" json:"extern_uid,omitempty"` 160 | Provider string `url:"provider,omitempty" json:"provider,omitempty"` 161 | Bio string `url:"bio,omitempty" json:"bio,omitempty"` 162 | Admin bool `url:"admin,omitempty" json:"admin,omitempty"` 163 | CanCreateGroup bool `url:"can_create_group,omitempty" json:"can_create_group,omitempty"` 164 | } 165 | 166 | // ModifyUser modifies an existing user. Only administrators can change attributes 167 | // of a user. 168 | // 169 | // GitLab API docs: http://doc.gitlab.com/ce/api/users.html#user-modification 170 | func (s *UsersService) ModifyUser(user int, opt *ModifyUserOptions) (*User, *Response, error) { 171 | u := fmt.Sprintf("users/%d", user) 172 | 173 | req, err := s.client.NewRequest("PUT", u, opt) 174 | if err != nil { 175 | return nil, nil, err 176 | } 177 | 178 | usr := new(User) 179 | resp, err := s.client.Do(req, usr) 180 | if err != nil { 181 | return nil, resp, err 182 | } 183 | 184 | return usr, resp, err 185 | } 186 | 187 | // DeleteUser deletes a user. Available only for administrators. This is an 188 | // idempotent function, calling this function for a non-existent user id still 189 | // returns a status code 200 OK. The JSON response differs if the user was 190 | // actually deleted or not. In the former the user is returned and in the 191 | // latter not. 192 | // 193 | // GitLab API docs: http://doc.gitlab.com/ce/api/users.html#user-deletion 194 | func (s *UsersService) DeleteUser(user int) (*Response, error) { 195 | u := fmt.Sprintf("users/%d", user) 196 | 197 | req, err := s.client.NewRequest("DELETE", u, nil) 198 | if err != nil { 199 | return nil, err 200 | } 201 | 202 | resp, err := s.client.Do(req, nil) 203 | if err != nil { 204 | return resp, err 205 | } 206 | 207 | return resp, err 208 | } 209 | 210 | // CurrentUser gets currently authenticated user. 211 | // 212 | // GitLab API docs: http://doc.gitlab.com/ce/api/users.html#current-user 213 | func (s *UsersService) CurrentUser() (*User, *Response, error) { 214 | req, err := s.client.NewRequest("GET", "user", nil) 215 | if err != nil { 216 | return nil, nil, err 217 | } 218 | 219 | usr := new(User) 220 | resp, err := s.client.Do(req, usr) 221 | if err != nil { 222 | return nil, resp, err 223 | } 224 | 225 | return usr, resp, err 226 | } 227 | 228 | // SSHKey represents a SSH key. 229 | // 230 | // GitLab API docs: http://doc.gitlab.com/ce/api/users.html#list-ssh-keys 231 | type SSHKey struct { 232 | ID int `json:"id"` 233 | Title string `json:"title"` 234 | Key string `json:"key"` 235 | CreatedAt time.Time `json:"created_at"` 236 | } 237 | 238 | // ListSSHKeys gets a list of currently authenticated user's SSH keys. 239 | // 240 | // GitLab API docs: http://doc.gitlab.com/ce/api/users.html#list-ssh-keys 241 | func (s *UsersService) ListSSHKeys() ([]*SSHKey, *Response, error) { 242 | req, err := s.client.NewRequest("GET", "user/keys", nil) 243 | if err != nil { 244 | return nil, nil, err 245 | } 246 | 247 | var k []*SSHKey 248 | resp, err := s.client.Do(req, &k) 249 | if err != nil { 250 | return nil, resp, err 251 | } 252 | 253 | return k, resp, err 254 | } 255 | 256 | // ListSSHKeysForUser gets a list of a specified user's SSH keys. Available 257 | // only for admin 258 | // 259 | // GitLab API docs: 260 | // http://doc.gitlab.com/ce/api/users.html#list-ssh-keys-for-user 261 | func (s *UsersService) ListSSHKeysForUser(user int) ([]*SSHKey, *Response, error) { 262 | u := fmt.Sprintf("users/%d/keys", user) 263 | 264 | req, err := s.client.NewRequest("GET", u, nil) 265 | if err != nil { 266 | return nil, nil, err 267 | } 268 | 269 | var k []*SSHKey 270 | resp, err := s.client.Do(req, &k) 271 | if err != nil { 272 | return nil, resp, err 273 | } 274 | 275 | return k, resp, err 276 | } 277 | 278 | // GetSSHKey gets a single key. 279 | // 280 | // GitLab API docs: http://doc.gitlab.com/ce/api/users.html#single-ssh-key 281 | func (s *UsersService) GetSSHKey(kid int) (*SSHKey, *Response, error) { 282 | u := fmt.Sprintf("user/keys/%d", kid) 283 | 284 | req, err := s.client.NewRequest("GET", u, nil) 285 | if err != nil { 286 | return nil, nil, err 287 | } 288 | 289 | k := new(SSHKey) 290 | resp, err := s.client.Do(req, k) 291 | if err != nil { 292 | return nil, resp, err 293 | } 294 | 295 | return k, resp, err 296 | } 297 | 298 | // AddSSHKeyOptions represents the available AddSSHKey() options. 299 | // 300 | // GitLab API docs: http://doc.gitlab.com/ce/api/projects.html#add-ssh-key 301 | type AddSSHKeyOptions struct { 302 | Title string `url:"title,omitempty" json:"title,omitempty"` 303 | Key string `url:"key,omitempty" json:"key,omitempty"` 304 | } 305 | 306 | // AddSSHKey creates a new key owned by the currently authenticated user. 307 | // 308 | // GitLab API docs: http://doc.gitlab.com/ce/api/users.html#add-ssh-key 309 | func (s *UsersService) AddSSHKey(opt *AddSSHKeyOptions) (*SSHKey, *Response, error) { 310 | req, err := s.client.NewRequest("POST", "user/keys", opt) 311 | if err != nil { 312 | return nil, nil, err 313 | } 314 | 315 | k := new(SSHKey) 316 | resp, err := s.client.Do(req, k) 317 | if err != nil { 318 | return nil, resp, err 319 | } 320 | 321 | return k, resp, err 322 | } 323 | 324 | // AddSSHKeyForUser creates new key owned by specified user. Available only for 325 | // admin. 326 | // 327 | // GitLab API docs: http://doc.gitlab.com/ce/api/users.html#add-ssh-key-for-user 328 | func (s *UsersService) AddSSHKeyForUser( 329 | user int, 330 | opt *AddSSHKeyOptions) (*SSHKey, *Response, error) { 331 | u := fmt.Sprintf("users/%d/keys", user) 332 | 333 | req, err := s.client.NewRequest("POST", u, opt) 334 | if err != nil { 335 | return nil, nil, err 336 | } 337 | 338 | k := new(SSHKey) 339 | resp, err := s.client.Do(req, k) 340 | if err != nil { 341 | return nil, resp, err 342 | } 343 | 344 | return k, resp, err 345 | } 346 | 347 | // DeleteSSHKey deletes key owned by currently authenticated user. This is an 348 | // idempotent function and calling it on a key that is already deleted or not 349 | // available results in 200 OK. 350 | // 351 | // GitLab API docs: 352 | // http://doc.gitlab.com/ce/api/users.html#delete-ssh-key-for-current-owner 353 | func (s *UsersService) DeleteSSHKey(kid int) (*Response, error) { 354 | u := fmt.Sprintf("user/keys/%d", kid) 355 | 356 | req, err := s.client.NewRequest("DELETE", u, nil) 357 | if err != nil { 358 | return nil, err 359 | } 360 | 361 | resp, err := s.client.Do(req, nil) 362 | if err != nil { 363 | return resp, err 364 | } 365 | 366 | return resp, err 367 | } 368 | 369 | // DeleteSSHKeyForUser deletes key owned by a specified user. Available only 370 | // for admin. 371 | // 372 | // GitLab API docs: 373 | // http://doc.gitlab.com/ce/api/users.html#delete-ssh-key-for-given-user 374 | func (s *UsersService) DeleteSSHKeyForUser(user int, kid int) (*Response, error) { 375 | u := fmt.Sprintf("users/%d/keys/%d", user, kid) 376 | 377 | req, err := s.client.NewRequest("DELETE", u, nil) 378 | if err != nil { 379 | return nil, err 380 | } 381 | 382 | resp, err := s.client.Do(req, nil) 383 | if err != nil { 384 | return resp, err 385 | } 386 | 387 | return resp, err 388 | } 389 | 390 | // BlockUser blocks the specified user. Available only for admin. 391 | // 392 | // GitLab API docs: http://doc.gitlab.com/ce/api/users.html#block-user 393 | func (s *UsersService) BlockUser(user int) (*User, *Response, error) { 394 | u := fmt.Sprintf("users/%d/block", user) 395 | 396 | req, err := s.client.NewRequest("PUT", u, nil) 397 | if err != nil { 398 | return nil, nil, err 399 | } 400 | 401 | usr := new(User) 402 | resp, err := s.client.Do(req, usr) 403 | if err != nil { 404 | return nil, resp, err 405 | } 406 | 407 | return usr, resp, err 408 | } 409 | 410 | // UnblockUser unblocks the specified user. Available only for admin. 411 | // 412 | // GitLab API docs: http://doc.gitlab.com/ce/api/users.html#unblock-user 413 | func (s *UsersService) UnblockUser(user int) (*User, *Response, error) { 414 | u := fmt.Sprintf("users/%d/unblock", user) 415 | 416 | req, err := s.client.NewRequest("PUT", u, nil) 417 | if err != nil { 418 | return nil, nil, err 419 | } 420 | 421 | usr := new(User) 422 | resp, err := s.client.Do(req, usr) 423 | if err != nil { 424 | return nil, resp, err 425 | } 426 | 427 | return usr, resp, err 428 | } 429 | -------------------------------------------------------------------------------- /cmd/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/requilence/integram" 5 | "github.com/integram-org/gitlab" 6 | "github.com/kelseyhightower/envconfig" 7 | ) 8 | 9 | func main(){ 10 | var cfg gitlab.Config 11 | envconfig.MustProcess("GITLAB", &cfg) 12 | 13 | integram.Register( 14 | cfg, 15 | cfg.BotConfig.Token, 16 | ) 17 | 18 | integram.Run() 19 | } --------------------------------------------------------------------------------