├── .gitignore ├── .travis.yml ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── build_graph.sh ├── build_nightly.sh ├── examples ├── get_id_issue.rs ├── get_id_merge_request.rs ├── get_id_project.rs ├── list_groups.rs ├── list_issues.rs ├── list_merge_requests.rs ├── list_projects.rs └── version.rs ├── gitlab-api-rs.dot ├── gitlab-api-rs.png ├── src ├── gitlab.rs ├── groups │ ├── details.rs │ ├── mod.rs │ ├── owned.rs │ └── projects.rs ├── issues │ ├── group.rs │ ├── mod.rs │ ├── project.rs │ └── single.rs ├── lib.rs ├── merge_requests │ ├── mod.rs │ └── single.rs ├── projects │ ├── all.rs │ ├── id.rs │ ├── id_branch.rs │ ├── id_branches.rs │ ├── id_events.rs │ ├── id_hooks.rs │ ├── id_hooks_id.rs │ ├── mod.rs │ ├── owned.rs │ ├── search.rs │ ├── starred.rs │ └── visible.rs └── serde_types.in.rs └── tools └── create_doc.py /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | target.* 3 | Cargo.lock 4 | *.rs.bk 5 | .tags 6 | .tags1 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | sudo: false 3 | # necessary for `travis-cargo coveralls --no-sudo` 4 | addons: 5 | apt: 6 | packages: 7 | - libcurl4-openssl-dev 8 | - libelf-dev 9 | - libdw-dev 10 | - binutils-dev # optional: only required for the --verify flag of coveralls 11 | cache: cargo 12 | 13 | rust: 14 | - stable 15 | - beta 16 | - nightly 17 | matrix: 18 | allow_failures: 19 | - rust: nightly 20 | - os: osx 21 | 22 | os: 23 | - linux 24 | - osx 25 | 26 | # load travis-cargo 27 | before_script: 28 | - pip install 'travis-cargo<0.2' --user 29 | - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then export PATH=$HOME/.local/bin:$PATH; fi 30 | - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then export PATH=$HOME/Library/Python/2.7/bin:$PATH; fi 31 | # Install OpenSSL through homebrew 32 | - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update && brew install openssl; fi 33 | - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then export OPENSSL_INCLUDE_DIR=`brew --prefix openssl`/include; fi 34 | - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then export OPENSSL_LIB_DIR=`brew --prefix openssl`/lib; fi 35 | - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then export DEP_OPENSSL_INCLUDE=`brew --prefix openssl`/include; fi 36 | 37 | 38 | # the main build 39 | script: 40 | - | 41 | travis-cargo build && 42 | travis-cargo test && 43 | travis-cargo bench && 44 | travis-cargo --only stable doc 45 | 46 | after_success: 47 | # upload the documentation from the build with stable (automatically only actually 48 | # runs on the master branch, not individual PRs) 49 | # - travis-cargo --only stable doc-upload 50 | # measure code coverage and upload to coveralls.io (the verify 51 | # argument mitigates kcov crashes due to malformed debuginfo, at the 52 | # cost of some speed ) 53 | # https://github.com/huonw/travis-cargo/issues/58 54 | # See https://github.com/ujh/iomrascalai/blob/master/.travis.yml 55 | - if [[ "$TRAVIS_OS_NAME" == "linux" && "$TRAVIS_RUST_VERSION" == "stable" ]]; then travis-cargo coveralls --no-sudo --verify; fi 56 | - if [[ "$TRAVIS_OS_NAME" == "linux" && "$TRAVIS_RUST_VERSION" == "stable" ]]; then ./kcov/build/src/kcov --verify --coveralls-id=$TRAVIS_JOB_ID --exclude-pattern=/.cargo,/target target/kcov target/debug/gitlab_api-*; fi 57 | # Upload to codecov.io too 58 | - if [[ "$TRAVIS_OS_NAME" == "linux" && "$TRAVIS_RUST_VERSION" == "stable" ]]; then bash <(curl -s https://codecov.io/bash); fi 59 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "gitlab-api" 3 | version = "0.6.0" 4 | authors = ["Nicolas Bigaouette "] 5 | description = "Wrapper for GitLab API v3" 6 | license = "MIT/Apache-2.0" 7 | repository = "https://github.com/nbigaouette/gitlab-api-rs" 8 | readme = "README.md" 9 | keywords = ["gitlab", "git"] 10 | categories = ["api-bindings"] 11 | 12 | [badges] 13 | travis-ci = { repository = "nbigaouette/gitlab-api-rs" } 14 | 15 | [dev-dependencies] 16 | env_logger = "0.4" 17 | clap = "2.20.1" 18 | 19 | [dependencies] 20 | log = "0.3" 21 | error-chain = "0.8" 22 | regex = "0.2.1" 23 | url = "1.4" 24 | serde = "0.9" 25 | serde_derive = "0.9" 26 | serde_json = "0.9" 27 | serde_urlencoded = "0.4" 28 | 29 | # Compilation on OSX will fail in case you don't do one of these two things: 30 | # 1) Use this instead to link against "security-framework": 31 | # hyper = { version = "0.9.13", default-features = false, features = ["security-framework"] } 32 | # 2) Install OpenSSL using homebrew and export these variables: 33 | # export OPENSSL_INCLUDE_DIR=`brew --prefix openssl`/include 34 | # export OPENSSL_LIB_DIR=`brew --prefix openssl`/lib 35 | # export DEP_OPENSSL_INCLUDE=`brew --prefix openssl`/include 36 | # See http://stackoverflow.com/q/39709542/178154 37 | hyper = "0.9.13" 38 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Nicolas Bigaouette 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gitlab-api-rs 2 | 3 | Rust wrapper to the GitLab API. 4 | 5 | [![Crates.io](https://img.shields.io/crates/v/gitlab-api.svg)](https://crates.io/crates/gitlab-api) 6 | [![Build Status](https://travis-ci.org/nbigaouette/gitlab-api-rs.svg?branch=master)](https://travis-ci.org/nbigaouette/gitlab-api-rs) 7 | [![Documentation](https://docs.rs/gitlab-api/badge.svg)](https://docs.rs/gitlab-api) 8 | [![License](https://img.shields.io/crates/l/gitlab-api.svg)](#licensing) 9 | [![Coverage Status](https://coveralls.io/repos/github/nbigaouette/gitlab-api-rs/badge.svg?branch=master)](https://coveralls.io/github/nbigaouette/gitlab-api-rs?branch=master) 10 | [![Codecov](https://img.shields.io/codecov/c/github/nbigaouette/gitlab-api-rs/master.svg?style=flat)](https://codecov.io/github/nbigaouette/gitlab-api-rs?branch=master) 11 | 12 | 13 | **NOTE**: Requires Rust 1.15 (stable) since this crate is using custom derive for Serde. 14 | 15 | ## Synopsis 16 | 17 | [GitLab](https://about.gitlab.com/) is an amazing tool. For most of the tasks, the web UI is more than enough but for some tasks nothing beats scripting them. The [GitLab API](https://docs.gitlab.com/ce/api/) is there to allow scripting actions on the GitLab server. 18 | 19 | The excellent [python-gitlab](https://github.com/gpocentek/python-gitlab) allows to use the API from Python, but when playing with it I find myself missing [Rust](https://www.rust-lang.org/)'s static typing. Hence this implementation in Rust. 20 | 21 | The (v3) API is quite long, so the parts I need will be implemented first. 22 | 23 | 24 | ## What Works 25 | 26 | * Read-only listing: 27 | * Groups; 28 | * Issues; 29 | * Merge Requests; 30 | * Projects (admin all, user's, specific id, owned, search); 31 | 32 | 33 | ## What Doesn't Work 34 | 35 | * Any _write_ commands (`POST`, `PUT`, etc.) 36 | * Any _Enterprise Edition_-specific features. 37 | * API elements using arrays. 38 | 39 | For example [listing merge requests, filtering with multiple `iid`s](https://docs.gitlab.com/ce/api/merge_requests.html#list-merge-requests): 40 | 41 | ``` 42 | GET /projects/:id/merge_requests?iid[]=42&iid[]=43 43 | ``` 44 | * Some projects listing: 45 | * branch; 46 | * branches; 47 | * events; 48 | * hook; 49 | * hooks; 50 | * starred; 51 | * visible; 52 | 53 | 54 | ## Usage 55 | 56 | 57 | ``` 58 | [dependencies] 59 | gitlab-api = "0.6" 60 | ``` 61 | 62 | This crate uses a builder pattern to add filters to a query. Once the query is built, `list()` will commit it by contacting the GitLab server and performing the request. 63 | 64 | ``` 65 | extern crate gitlab_api as gitlab; 66 | 67 | fn main() { 68 | let gl = gitlab::GitLab::new(&"gitlab.com", &"GITLAB_TOKEN_XXXXXXX").unwrap(); 69 | 70 | // Get GitLab's version. 71 | let gitlab_version = gl.version().unwrap(); 72 | println!("gitlab_version: {:?}", gitlab_version); 73 | 74 | 75 | // Low level methods 76 | 77 | // Get projects, owned by authenticated user and which are archived. 78 | let projects = gl.projects().owned().archived(true).list().unwrap(); 79 | println!("projects: {:?}", projects); 80 | 81 | // Get groups owned by authenticated user. 82 | let owned_groups = gl.groups().owned().list().unwrap(); 83 | println!("owned_groups: {:?}", owned_groups); 84 | 85 | // Get closed issues. 86 | let closed_issues = gl.issues().state(gitlab::issues::State::Closed).list().unwrap(); 87 | println!("closed_issues: {:?}", closed_issues); 88 | 89 | 90 | // Higher level methods 91 | 92 | // Get a specific project 93 | let project = gl.get_project("nbigaouette1", "gitlab-api-rs").chain_err(|| "cannot get project")?; 94 | 95 | // Get a specific issue 96 | let issue = gl.get_issue("nbigaouette1", "gitlab-api-rs", 1).chain_err(|| "cannot get issue")?; 97 | 98 | // Get a specific merge request 99 | let merge_request = gl.get_merge_request("nbigaouette1", "gitlab-api-rs", 1).chain_err(|| "cannot get merge_request")?; 100 | } 101 | ``` 102 | 103 | **NOTES**: 104 | * Crate uses `https` by default. Use `GitLab::new_insecure()` to use `http` (or `port()` and `sheme()` setters on `GitLab` struct). 105 | * Sending your token in clear over `http` is dangerous! 106 | * See [examples/list_projects.rs] for an example of how to load the token (and the hostname) from an environment variable. 107 | * See the `examples` directory for many more examples on how to use this crate. 108 | 109 | 110 | ## Dependencies 111 | 112 | Thanks `cargo-graph` for the graph! 113 | 114 | ![Dependencies](gitlab-api-rs.png) 115 | 116 | 117 | ## Licensing 118 | 119 | gitlab-api-rs is distributed under the terms of both the MIT license and the Apache License (Version 2.0). 120 | 121 | See [LICENSE-APACHE](./LICENSE-APACHE) and [LICENSE-MIT](./LICENSE-MIT) for details. 122 | -------------------------------------------------------------------------------- /build_graph.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # https://github.com/kbknapp/cargo-graph 4 | # cargo install cargo-graph 5 | 6 | cargo graph --optional-line-style dashed --optional-line-color red --optional-shape box --build-shape diamond --build-color green --build-line-color orange > gitlab-api-rs.dot 7 | dot -Tpng > gitlab-api-rs.png gitlab-api-rs.dot 8 | -------------------------------------------------------------------------------- /build_nightly.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | rustup run nightly cargo build --no-default-features --features unstable 4 | -------------------------------------------------------------------------------- /examples/get_id_issue.rs: -------------------------------------------------------------------------------- 1 | extern crate gitlab_api as gitlab; 2 | 3 | use std::env; 4 | #[macro_use] 5 | extern crate log; 6 | extern crate env_logger; 7 | #[macro_use] 8 | extern crate clap; 9 | 10 | 11 | use gitlab::GitLab; 12 | 13 | use gitlab::errors::*; 14 | 15 | 16 | fn main() { 17 | if let Err(ref e) = run() { 18 | println!("error: {}", e); 19 | 20 | for e in e.iter().skip(1) { 21 | println!("caused by: {}", e); 22 | } 23 | 24 | // The backtrace is not always generated. Try to run this example 25 | // with `RUST_BACKTRACE=1`. 26 | if let Some(backtrace) = e.backtrace() { 27 | println!("backtrace: {:?}", backtrace); 28 | } 29 | 30 | ::std::process::exit(1); 31 | } 32 | } 33 | 34 | fn run() -> Result<()> { 35 | env_logger::init().unwrap(); 36 | info!("starting up"); 37 | 38 | let hostname = match env::var("GITLAB_HOSTNAME") { 39 | Ok(val) => val, 40 | Err(_) => { 41 | let default = String::from("gitlab.com"); 42 | println!("Please set environment variable 'GITLAB_HOSTNAME'. Using default '{}'.", 43 | default); 44 | default 45 | } 46 | }; 47 | 48 | let token = match env::var("GITLAB_TOKEN") { 49 | Ok(val) => val, 50 | Err(_) => { 51 | panic!("Please set environment variable 'GITLAB_TOKEN'. Take it from \ 52 | http://{}/profile/account", 53 | hostname); 54 | } 55 | }; 56 | 57 | let gl = GitLab::new(&hostname, &token).chain_err(|| "failure to create GitLab instance")?; 58 | // let gl = GitLab::new(&hostname, &token) 59 | // .chain_err(|| "failure to create GitLab instance")? 60 | // .scheme("http").port(80); 61 | // let gl = gl.scheme("http").port(80); 62 | 63 | let matches = clap::App::new("get_id_issue") 64 | .version("1.0") 65 | .author("Nicolas Bigaouette ") 66 | .about("Get the id of a GitLab issue from namespace/project#iid.") 67 | .arg(clap::Arg::with_name("namespace") 68 | .help("The project's namespace (or group)") 69 | .long("namespace") 70 | .short("n") 71 | .takes_value(true) 72 | .required(true)) 73 | .arg(clap::Arg::with_name("project") 74 | .help("The project's name") 75 | .long("project") 76 | .short("p") 77 | .takes_value(true) 78 | .required(true)) 79 | .arg(clap::Arg::with_name("id") 80 | .help("The issue's id") 81 | .long("id") 82 | .short("i") 83 | .takes_value(true) 84 | .required(true)) 85 | .get_matches(); 86 | 87 | let project_namespace = matches.value_of("namespace").unwrap(); 88 | let project_name = matches.value_of("project").unwrap(); 89 | let issue_iid = value_t!(matches, "id", i64).unwrap_or_else(|e| e.exit()); 90 | 91 | let issue = gl.get_issue(project_namespace, project_name, issue_iid) 92 | .chain_err(|| "cannot get issue")?; 93 | // println!("issue: {:?}", issue); 94 | 95 | println!("Id for {}/{}#{}: {}", 96 | project_namespace, 97 | project_name, 98 | issue_iid, 99 | issue.id); 100 | 101 | Ok(()) 102 | } 103 | -------------------------------------------------------------------------------- /examples/get_id_merge_request.rs: -------------------------------------------------------------------------------- 1 | extern crate gitlab_api as gitlab; 2 | 3 | use std::env; 4 | #[macro_use] 5 | extern crate log; 6 | extern crate env_logger; 7 | #[macro_use] 8 | extern crate clap; 9 | 10 | 11 | use gitlab::GitLab; 12 | 13 | use gitlab::errors::*; 14 | 15 | 16 | fn main() { 17 | if let Err(ref e) = run() { 18 | println!("error: {}", e); 19 | 20 | for e in e.iter().skip(1) { 21 | println!("caused by: {}", e); 22 | } 23 | 24 | // The backtrace is not always generated. Try to run this example 25 | // with `RUST_BACKTRACE=1`. 26 | if let Some(backtrace) = e.backtrace() { 27 | println!("backtrace: {:?}", backtrace); 28 | } 29 | 30 | ::std::process::exit(1); 31 | } 32 | } 33 | 34 | fn run() -> Result<()> { 35 | env_logger::init().unwrap(); 36 | info!("starting up"); 37 | 38 | let hostname = match env::var("GITLAB_HOSTNAME") { 39 | Ok(val) => val, 40 | Err(_) => { 41 | let default = String::from("gitlab.com"); 42 | println!("Please set environment variable 'GITLAB_HOSTNAME'. Using default '{}'.", 43 | default); 44 | default 45 | } 46 | }; 47 | 48 | let token = match env::var("GITLAB_TOKEN") { 49 | Ok(val) => val, 50 | Err(_) => { 51 | panic!("Please set environment variable 'GITLAB_TOKEN'. Take it from \ 52 | http://{}/profile/account", 53 | hostname); 54 | } 55 | }; 56 | 57 | let gl = GitLab::new(&hostname, &token).chain_err(|| "failure to create GitLab instance")?; 58 | // let gl = GitLab::new(&hostname, &token) 59 | // .chain_err(|| "failure to create GitLab instance")? 60 | // .scheme("http").port(80); 61 | // let gl = gl.scheme("http").port(80); 62 | 63 | let matches = clap::App::new("get_id_merge_request") 64 | .version("1.0") 65 | .author("Nicolas Bigaouette ") 66 | .about("Get the id of a GitLab project from namespace/project.") 67 | .arg(clap::Arg::with_name("namespace") 68 | .help("The project's namespace (or group)") 69 | .long("namespace") 70 | .short("n") 71 | .takes_value(true) 72 | .required(true)) 73 | .arg(clap::Arg::with_name("project") 74 | .help("The project's name") 75 | .long("project") 76 | .short("p") 77 | .takes_value(true) 78 | .required(true)) 79 | .arg(clap::Arg::with_name("id") 80 | .help("The merge request's id") 81 | .long("id") 82 | .short("i") 83 | .takes_value(true) 84 | .required(true)) 85 | .get_matches(); 86 | 87 | let project_namespace = matches.value_of("namespace").unwrap(); 88 | let project_name = matches.value_of("project").unwrap(); 89 | let merge_request_iid = value_t!(matches, "id", i64).unwrap_or_else(|e| e.exit()); 90 | 91 | let merge_request = gl.get_merge_request(project_namespace, project_name, merge_request_iid) 92 | .chain_err(|| "cannot get merge request")?; 93 | // println!("merge_request: {:?}", merge_request); 94 | 95 | println!("Id for {}/{}!{}: {}", 96 | project_namespace, 97 | project_name, 98 | merge_request_iid, 99 | merge_request.id); 100 | 101 | Ok(()) 102 | } 103 | -------------------------------------------------------------------------------- /examples/get_id_project.rs: -------------------------------------------------------------------------------- 1 | extern crate gitlab_api as gitlab; 2 | 3 | use std::env; 4 | #[macro_use] 5 | extern crate log; 6 | extern crate env_logger; 7 | extern crate clap; 8 | 9 | 10 | use gitlab::GitLab; 11 | 12 | use gitlab::errors::*; 13 | 14 | 15 | fn main() { 16 | if let Err(ref e) = run() { 17 | println!("error: {}", e); 18 | 19 | for e in e.iter().skip(1) { 20 | println!("caused by: {}", e); 21 | } 22 | 23 | // The backtrace is not always generated. Try to run this example 24 | // with `RUST_BACKTRACE=1`. 25 | if let Some(backtrace) = e.backtrace() { 26 | println!("backtrace: {:?}", backtrace); 27 | } 28 | 29 | ::std::process::exit(1); 30 | } 31 | } 32 | 33 | fn run() -> Result<()> { 34 | env_logger::init().unwrap(); 35 | info!("starting up"); 36 | 37 | let hostname = match env::var("GITLAB_HOSTNAME") { 38 | Ok(val) => val, 39 | Err(_) => { 40 | let default = String::from("gitlab.com"); 41 | println!("Please set environment variable 'GITLAB_HOSTNAME'. Using default '{}'.", 42 | default); 43 | default 44 | } 45 | }; 46 | 47 | let token = match env::var("GITLAB_TOKEN") { 48 | Ok(val) => val, 49 | Err(_) => { 50 | panic!("Please set environment variable 'GITLAB_TOKEN'. Take it from \ 51 | http://{}/profile/account", 52 | hostname); 53 | } 54 | }; 55 | 56 | let gl = GitLab::new(&hostname, &token).chain_err(|| "failure to create GitLab instance")?; 57 | // let gl = GitLab::new(&hostname, &token) 58 | // .chain_err(|| "failure to create GitLab instance")? 59 | // .scheme("http").port(80); 60 | // let gl = gl.scheme("http").port(80); 61 | 62 | let matches = clap::App::new("get_id_project") 63 | .version("1.0") 64 | .author("Nicolas Bigaouette ") 65 | .about("Get the id of a GitLab project from namespace/project.") 66 | .arg(clap::Arg::with_name("namespace") 67 | .help("The project's namespace (or group)") 68 | .long("namespace") 69 | .short("n") 70 | .takes_value(true) 71 | .required(true)) 72 | .arg(clap::Arg::with_name("project") 73 | .help("The project's name") 74 | .long("project") 75 | .short("p") 76 | .takes_value(true) 77 | .required(true)) 78 | .get_matches(); 79 | 80 | let project_namespace = matches.value_of("namespace").unwrap(); 81 | let project_name = matches.value_of("project").unwrap(); 82 | 83 | let project = gl.get_project(project_namespace, project_name) 84 | .chain_err(|| "cannot get project")?; 85 | // println!("project: {:?}", project); 86 | 87 | println!("Id for {}/{}: {}", 88 | project_namespace, 89 | project_name, 90 | project.id); 91 | 92 | Ok(()) 93 | } 94 | -------------------------------------------------------------------------------- /examples/list_groups.rs: -------------------------------------------------------------------------------- 1 | extern crate gitlab_api as gitlab; 2 | 3 | use std::env; 4 | #[macro_use] 5 | extern crate log; 6 | extern crate env_logger; 7 | 8 | use gitlab::errors::*; 9 | 10 | 11 | fn main() { 12 | if let Err(ref e) = run() { 13 | println!("error: {}", e); 14 | 15 | for e in e.iter().skip(1) { 16 | println!("caused by: {}", e); 17 | } 18 | 19 | // The backtrace is not always generated. Try to run this example 20 | // with `RUST_BACKTRACE=1`. 21 | if let Some(backtrace) = e.backtrace() { 22 | println!("backtrace: {:?}", backtrace); 23 | } 24 | 25 | ::std::process::exit(1); 26 | } 27 | } 28 | 29 | fn run() -> Result<()> { 30 | env_logger::init().unwrap(); 31 | info!("starting up"); 32 | 33 | let hostname = match env::var("GITLAB_HOSTNAME") { 34 | Ok(val) => val, 35 | Err(_) => { 36 | let default = String::from("gitlab.com"); 37 | println!("Please set environment variable 'GITLAB_HOSTNAME'. Using default '{}'.", 38 | default); 39 | default 40 | } 41 | }; 42 | 43 | let token = match env::var("GITLAB_TOKEN") { 44 | Ok(val) => val, 45 | Err(_) => { 46 | panic!("Please set environment variable 'GITLAB_TOKEN'. Take it from \ 47 | http://{}/profile/account", 48 | hostname); 49 | } 50 | }; 51 | 52 | let gl = 53 | gitlab::GitLab::new(&hostname, &token).chain_err(|| "failure to create GitLab instance")?; 54 | // let mut gl = gitlab::GitLab::new(&hostname, &token) 55 | // .chain_err(|| "failure to create GitLab instance")? 56 | // .scheme("http").port(80); 57 | // gl = gl.scheme("http").port(80); 58 | 59 | println!("gl: {:?}", gl); 60 | 61 | let groups = gl.groups().list().chain_err(|| "cannot get groups")?; 62 | println!("groups: {:?}", groups); 63 | 64 | let groups = gl.groups() 65 | .details(gitlab::groups::ListingId::Id(21)) 66 | .list() 67 | .chain_err(|| "cannot get groups")?; 68 | println!("groups: {:?}", groups); 69 | 70 | // let groups = gl.groups(gitlab::groups::Listing::new().skip_groups(vec![1, 2, 3]).clone()); 71 | // println!("groups: {:?}", groups); 72 | // 73 | let owned_groups = gl.groups().owned().list().chain_err(|| "cannot get groups")?; 74 | println!("owned_groups: {:?}", owned_groups); 75 | 76 | let projects_groups = gl.groups().projects(21).list().chain_err(|| "cannot get groups")?; 77 | println!("projects_groups: {:?}", projects_groups); 78 | 79 | Ok(()) 80 | } 81 | -------------------------------------------------------------------------------- /examples/list_issues.rs: -------------------------------------------------------------------------------- 1 | extern crate gitlab_api as gitlab; 2 | 3 | use std::env; 4 | #[macro_use] 5 | extern crate log; 6 | extern crate env_logger; 7 | 8 | use gitlab::GitLab; 9 | // use gitlab::Pagination; 10 | use gitlab::issues; 11 | use gitlab::Lister; 12 | 13 | use gitlab::errors::*; 14 | 15 | 16 | fn main() { 17 | if let Err(ref e) = run() { 18 | println!("error: {}", e); 19 | 20 | for e in e.iter().skip(1) { 21 | println!("caused by: {}", e); 22 | } 23 | 24 | // The backtrace is not always generated. Try to run this example 25 | // with `RUST_BACKTRACE=1`. 26 | if let Some(backtrace) = e.backtrace() { 27 | println!("backtrace: {:?}", backtrace); 28 | } 29 | 30 | ::std::process::exit(1); 31 | } 32 | } 33 | 34 | fn run() -> Result<()> { 35 | env_logger::init().unwrap(); 36 | info!("starting up"); 37 | 38 | let hostname = match env::var("GITLAB_HOSTNAME") { 39 | Ok(val) => val, 40 | Err(_) => { 41 | let default = String::from("gitlab.com"); 42 | println!("Please set environment variable 'GITLAB_HOSTNAME'. Using default '{}'.", 43 | default); 44 | default 45 | } 46 | }; 47 | 48 | let token = match env::var("GITLAB_TOKEN") { 49 | Ok(val) => val, 50 | Err(_) => { 51 | panic!("Please set environment variable 'GITLAB_TOKEN'. Take it from \ 52 | http://{}/profile/account", 53 | hostname); 54 | } 55 | }; 56 | 57 | let gl = GitLab::new(&hostname, &token).chain_err(|| "failure to create GitLab instance")?; 58 | // let gl = GitLab::new(&hostname, &token) 59 | // .chain_err(|| "failure to create GitLab instance")? 60 | // .scheme("http").port(80); 61 | // let gl = gl.scheme("http").port(80); 62 | 63 | let issues = gl.issues().list().chain_err(|| "cannot get issues")?; 64 | println!("issues: {:?}", issues); 65 | 66 | let opened_issues = 67 | gl.issues().state(issues::State::Opened).list().chain_err(|| "cannot get issues")?; 68 | println!("opened_issues: {:?}", opened_issues); 69 | 70 | let closed_issues = 71 | gl.issues().state(issues::State::Closed).list().chain_err(|| "cannot get issues")?; 72 | println!("closed_issues: {:?}", closed_issues); 73 | 74 | let issue = gl.issues().single(142, 739).list().chain_err(|| "cannot get issues")?; 75 | println!("issue: {:?}", issue); 76 | 77 | let group_issues = gl.issues() 78 | .group(21) 79 | .state(issues::State::Closed) 80 | .list() 81 | .chain_err(|| "cannot get issues")?; 82 | println!("group_issues: {:?}", group_issues); 83 | 84 | let project_issues = gl.issues() 85 | .project(142) 86 | .state(issues::State::Opened) 87 | .list() 88 | .chain_err(|| "cannot get issues")?; 89 | println!("project_issues: {:?}", project_issues); 90 | 91 | Ok(()) 92 | } 93 | -------------------------------------------------------------------------------- /examples/list_merge_requests.rs: -------------------------------------------------------------------------------- 1 | extern crate gitlab_api as gitlab; 2 | 3 | use std::env; 4 | #[macro_use] 5 | extern crate log; 6 | extern crate env_logger; 7 | 8 | use gitlab::GitLab; 9 | use gitlab::Lister; 10 | 11 | use gitlab::errors::*; 12 | 13 | 14 | fn main() { 15 | if let Err(ref e) = run() { 16 | println!("error: {}", e); 17 | 18 | for e in e.iter().skip(1) { 19 | println!("caused by: {}", e); 20 | } 21 | 22 | // The backtrace is not always generated. Try to run this example 23 | // with `RUST_BACKTRACE=1`. 24 | if let Some(backtrace) = e.backtrace() { 25 | println!("backtrace: {:?}", backtrace); 26 | } 27 | 28 | ::std::process::exit(1); 29 | } 30 | } 31 | 32 | fn run() -> Result<()> { 33 | env_logger::init().unwrap(); 34 | info!("starting up"); 35 | 36 | let hostname = match env::var("GITLAB_HOSTNAME") { 37 | Ok(val) => val, 38 | Err(_) => { 39 | let default = String::from("gitlab.com"); 40 | println!("Please set environment variable 'GITLAB_HOSTNAME'. Using default '{}'.", 41 | default); 42 | default 43 | } 44 | }; 45 | 46 | let token = match env::var("GITLAB_TOKEN") { 47 | Ok(val) => val, 48 | Err(_) => { 49 | panic!("Please set environment variable 'GITLAB_TOKEN'. Take it from \ 50 | http://{}/profile/account", 51 | hostname); 52 | } 53 | }; 54 | 55 | let gl = GitLab::new(&hostname, &token).chain_err(|| "failure to create GitLab instance")?; 56 | // let gl = GitLab::new(&hostname, &token) 57 | // .chain_err(|| "failure to create GitLab instance")? 58 | // .scheme("http").port(80); 59 | // let gl = gl.scheme("http").port(80); 60 | 61 | let project_id = 142; 62 | let merge_requests_ids = vec![409, 410]; 63 | let merge_requests_iids = vec![3, 4]; 64 | 65 | let merge_requests = 66 | gl.merge_requests(project_id).list().chain_err(|| "cannot get merge request")?; 67 | println!("merge_requests: {:?}", merge_requests); 68 | 69 | let merge_request = gl.merge_requests(project_id) 70 | .single(merge_requests_ids[0]) 71 | .list() 72 | .chain_err(|| "cannot get merge request")?; 73 | println!("merge_request: {:?}", merge_request); 74 | 75 | let merge_requests = gl.merge_requests(project_id) 76 | .iid(vec![merge_requests_iids[0]]) 77 | .list() 78 | .chain_err(|| "cannot get merge request")?; 79 | println!("merge_requests: {:?}", merge_requests); 80 | 81 | // let merge_requests = gl.merge_requests(project_id) 82 | // .iid(merge_requests_iids) 83 | // .list() 84 | // .chain_err(|| "cannot get merge request")?; 85 | // println!("merge_requests: {:?}", merge_requests); 86 | 87 | Ok(()) 88 | } 89 | -------------------------------------------------------------------------------- /examples/list_projects.rs: -------------------------------------------------------------------------------- 1 | 2 | extern crate gitlab_api as gitlab; 3 | 4 | use std::env; 5 | #[macro_use] 6 | extern crate log; 7 | extern crate env_logger; 8 | 9 | use gitlab::GitLab; 10 | // use gitlab::Pagination; 11 | use gitlab::Lister; 12 | 13 | use gitlab::errors::*; 14 | 15 | 16 | fn main() { 17 | if let Err(ref e) = run() { 18 | println!("error: {}", e); 19 | 20 | for e in e.iter().skip(1) { 21 | println!("caused by: {}", e); 22 | } 23 | 24 | // The backtrace is not always generated. Try to run this example 25 | // with `RUST_BACKTRACE=1`. 26 | if let Some(backtrace) = e.backtrace() { 27 | println!("backtrace: {:?}", backtrace); 28 | } 29 | 30 | ::std::process::exit(1); 31 | } 32 | } 33 | 34 | fn run() -> Result<()> { 35 | env_logger::init().unwrap(); 36 | info!("starting up"); 37 | 38 | let hostname = match env::var("GITLAB_HOSTNAME") { 39 | Ok(val) => val, 40 | Err(_) => { 41 | let default = String::from("gitlab.com"); 42 | println!("Please set environment variable 'GITLAB_HOSTNAME'. Using default '{}'.", 43 | default); 44 | default 45 | } 46 | }; 47 | 48 | let token = match env::var("GITLAB_TOKEN") { 49 | Ok(val) => val, 50 | Err(_) => { 51 | panic!("Please set environment variable 'GITLAB_TOKEN'. Take it from \ 52 | http://{}/profile/account", 53 | hostname); 54 | } 55 | }; 56 | 57 | let gl = GitLab::new(&hostname, &token).chain_err(|| "failure to create GitLab instance")?; 58 | // let gl = GitLab::new(&hostname, &token) 59 | // .chain_err(|| "failure to create GitLab instance")? 60 | // .scheme("http") 61 | // .port(80); 62 | // let gl = gl.scheme("http").port(80); 63 | 64 | let projects = gl.projects().list().chain_err(|| "cannot get projects")?; 65 | println!("projects: {:?}", projects); 66 | 67 | let projects = gl.projects().archived(false).list().chain_err(|| "cannot get projects")?; 68 | println!("projects: {:?}", projects); 69 | 70 | let projects = 71 | gl.projects().owned().archived(false).list().chain_err(|| "cannot get projects")?; 72 | println!("projects: {:?}", projects); 73 | 74 | let projects = gl.projects() 75 | .all() 76 | .order_by(gitlab::projects::ListingOrderBy::Name) 77 | .list() 78 | .chain_err(|| "cannot get projects")?; 79 | println!("projects: {:?}", projects); 80 | 81 | let project = gl.projects() 82 | .id(gitlab::projects::ListingId::Id(9)) 83 | .list() 84 | .chain_err(|| "failure to find project")?; 85 | println!("project: {:?}", project); 86 | 87 | let issues = project.issues(&gl) 88 | .list() 89 | .chain_err(|| "failure to find project's issues")?; 90 | println!("issues: {:?}", issues); 91 | 92 | let merge_requests = project.merge_requests(&gl) 93 | .list() 94 | .chain_err(|| "failure to find project's merge_requests")?; 95 | println!("merge_requests: {:?}", merge_requests); 96 | 97 | let issues = gl.projects() 98 | .id(gitlab::projects::ListingId::Id(9)) 99 | .issues() 100 | .chain_err(|| "failure to build a gitlab::issues::project::IssuesLister")? 101 | .list() 102 | .chain_err(|| "failure to find issues")?; 103 | println!("issues: {:?}", issues); 104 | 105 | let merge_requests = gl.projects() 106 | .id(gitlab::projects::ListingId::Id(9)) 107 | .merge_requests() 108 | .chain_err(|| "failure to build a gitlab::merge_requests::project::IssuesLister")? 109 | .list() 110 | .chain_err(|| "failure to find merge requests")?; 111 | println!("merge_requests: {:?}", merge_requests); 112 | 113 | Ok(()) 114 | } 115 | -------------------------------------------------------------------------------- /examples/version.rs: -------------------------------------------------------------------------------- 1 | 2 | extern crate gitlab_api as gitlab; 3 | 4 | use std::env; 5 | #[macro_use] 6 | extern crate log; 7 | extern crate env_logger; 8 | 9 | use gitlab::GitLab; 10 | 11 | use gitlab::errors::*; 12 | 13 | 14 | fn main() { 15 | if let Err(ref e) = run() { 16 | println!("error: {}", e); 17 | 18 | for e in e.iter().skip(1) { 19 | println!("caused by: {}", e); 20 | } 21 | 22 | // The backtrace is not always generated. Try to run this example 23 | // with `RUST_BACKTRACE=1`. 24 | if let Some(backtrace) = e.backtrace() { 25 | println!("backtrace: {:?}", backtrace); 26 | } 27 | 28 | ::std::process::exit(1); 29 | } 30 | } 31 | 32 | fn run() -> Result<()> { 33 | env_logger::init().unwrap(); 34 | info!("starting up"); 35 | 36 | let hostname = match env::var("GITLAB_HOSTNAME") { 37 | Ok(val) => val, 38 | Err(_) => { 39 | let default = String::from("gitlab.com"); 40 | println!("Please set environment variable 'GITLAB_HOSTNAME'. Using default '{}'.", 41 | default); 42 | default 43 | } 44 | }; 45 | 46 | let token = match env::var("GITLAB_TOKEN") { 47 | Ok(val) => val, 48 | Err(_) => { 49 | panic!("Please set environment variable 'GITLAB_TOKEN'. Take it from \ 50 | http://{}/profile/account", 51 | hostname); 52 | } 53 | }; 54 | 55 | let gl = GitLab::new(&hostname, &token).chain_err(|| "failure to create GitLab instance")?; 56 | // let gl = GitLab::new(&hostname, &token).scheme("http").port(80); 57 | // let gl = gl.scheme("http").port(80); 58 | let version = gl.version().chain_err(|| "cannot get version")?; 59 | 60 | println!("version: {:?}", version); 61 | 62 | Ok(()) 63 | } 64 | -------------------------------------------------------------------------------- /gitlab-api-rs.dot: -------------------------------------------------------------------------------- 1 | digraph dependencies { 2 | N0[label="gitlab-api",shape=diamond,color=green]; 3 | N1[label="clippy",shape=box]; 4 | N2[label="error-chain",shape=diamond,color=green]; 5 | N3[label="hyper",shape=diamond,color=green]; 6 | N4[label="log",shape=diamond,color=green]; 7 | N5[label="regex",shape=diamond,color=green]; 8 | N6[label="serde",shape=diamond,color=green]; 9 | N7[label="serde_codegen",shape=diamond,color=green]; 10 | N8[label="serde_derive",shape=box]; 11 | N9[label="serde_json",shape=diamond,color=green]; 12 | N10[label="serde_urlencoded",shape=diamond,color=green]; 13 | N11[label="url",shape=diamond,color=green]; 14 | N12[label="aho-corasick",shape=diamond,color=green]; 15 | N13[label="memchr",shape=diamond,color=green]; 16 | N14[label="backtrace",shape=diamond,color=green]; 17 | N15[label="backtrace-sys",shape=diamond,color=green]; 18 | N16[label="cfg-if",shape=diamond,color=green]; 19 | N17[label="dbghelp-sys",shape=diamond,color=green]; 20 | N18[label="kernel32-sys",shape=diamond,color=green]; 21 | N19[label="libc",shape=diamond,color=green]; 22 | N20[label="rustc-demangle",shape=diamond,color=green]; 23 | N21[label="winapi",shape=diamond,color=green]; 24 | N22[label="gcc",shape=diamond,color=green]; 25 | N23[label="bitflags",shape=diamond,color=green]; 26 | N24[label="cargo_metadata",shape=box]; 27 | N25[label="clippy_lints",shape=box]; 28 | N26[label="matches",shape=diamond,color=green]; 29 | N27[label="quine-mc_cluskey",shape=box]; 30 | N28[label="regex-syntax",shape=diamond,color=green]; 31 | N29[label="semver v0.2.3",shape=box]; 32 | N30[label="toml",shape=box]; 33 | N31[label="unicode-normalization",shape=diamond,color=green]; 34 | N32[label="cookie",shape=diamond,color=green]; 35 | N33[label="openssl",shape=diamond,color=green]; 36 | N34[label="rustc-serialize",shape=diamond,color=green]; 37 | N35[label="time",shape=diamond,color=green]; 38 | N36[label="winapi-build",shape=diamond,color=green]; 39 | N37[label="dtoa v0.3.1",shape=diamond,color=green]; 40 | N38[label="dtoa v0.4.0",shape=diamond,color=green]; 41 | N39[label="gdi32-sys",shape=diamond,color=green]; 42 | N40[label="hpack",shape=diamond,color=green]; 43 | N41[label="httparse",shape=diamond,color=green]; 44 | N42[label="language-tags",shape=diamond,color=green]; 45 | N43[label="mime",shape=diamond,color=green]; 46 | N44[label="num_cpus",shape=diamond,color=green]; 47 | N45[label="openssl-verify",shape=diamond,color=green]; 48 | N46[label="solicit",shape=diamond,color=green]; 49 | N47[label="traitobject",shape=diamond,color=green]; 50 | N48[label="typeable",shape=diamond,color=green]; 51 | N49[label="unicase",shape=diamond,color=green]; 52 | N50[label="idna",shape=diamond,color=green]; 53 | N51[label="unicode-bidi",shape=diamond,color=green]; 54 | N52[label="itoa v0.2.1",shape=diamond,color=green]; 55 | N53[label="itoa v0.3.0",shape=diamond,color=green]; 56 | N54[label="lazy_static",shape=diamond,color=green]; 57 | N55[label="libressl-pnacl-sys",shape=diamond,color=green]; 58 | N56[label="pnacl-build-helper",shape=diamond,color=green]; 59 | N57[label="nom",shape=box]; 60 | N58[label="num-traits",shape=diamond,color=green]; 61 | N59[label="openssl-sys",shape=diamond,color=green]; 62 | N60[label="openssl-sys-extras",shape=diamond,color=green]; 63 | N61[label="pkg-config",shape=diamond,color=green]; 64 | N62[label="user32-sys",shape=diamond,color=green]; 65 | N63[label="tempdir",shape=diamond,color=green]; 66 | N64[label="quote",shape=box]; 67 | N65[label="rand",shape=diamond,color=green]; 68 | N66[label="redox_syscall",shape=diamond,color=green]; 69 | N67[label="thread_local",shape=diamond,color=green]; 70 | N68[label="utf8-ranges",shape=diamond,color=green]; 71 | N69[label="rustc_version",shape=diamond,color=green]; 72 | N70[label="semver v0.1.20",shape=diamond,color=green]; 73 | N71[label="serde_codegen_internals v0.11.3",shape=diamond,color=green]; 74 | N72[label="syn v0.10.8",shape=diamond,color=green]; 75 | N73[label="syntex",shape=diamond,color=green]; 76 | N74[label="syntex_syntax",shape=diamond,color=green]; 77 | N75[label="serde_codegen_internals v0.12.0",shape=box]; 78 | N76[label="syn v0.11.4",shape=box]; 79 | N77[label="unicode-xid",shape=box]; 80 | N78[label="syntex_errors",shape=diamond,color=green]; 81 | N79[label="syntex_pos",shape=diamond,color=green]; 82 | N80[label="term",shape=diamond,color=green]; 83 | N81[label="thread-id",shape=diamond,color=green]; 84 | N82[label="unreachable",shape=diamond,color=green]; 85 | N0 -> N1[label="",style=dashed,color=red]; 86 | N0 -> N2[label="",color=orange]; 87 | N0 -> N3[label="",color=orange]; 88 | N0 -> N4[label="",color=orange]; 89 | N0 -> N5[label="",color=orange]; 90 | N0 -> N6[label="",color=orange]; 91 | N0 -> N7[label=""]; 92 | N0 -> N8[label="",style=dashed,color=red]; 93 | N0 -> N9[label="",color=orange]; 94 | N0 -> N10[label="",color=orange]; 95 | N0 -> N11[label="",color=orange]; 96 | N1 -> N24[label="",style=dashed,color=red]; 97 | N1 -> N25[label="",style=dashed,color=red]; 98 | N2 -> N14[label="",color=orange]; 99 | N3 -> N4[label="",color=orange]; 100 | N3 -> N11[label="",color=orange]; 101 | N3 -> N32[label="",color=orange]; 102 | N3 -> N33[label="",color=orange]; 103 | N3 -> N34[label="",color=orange]; 104 | N3 -> N35[label="",color=orange]; 105 | N3 -> N41[label="",color=orange]; 106 | N3 -> N42[label="",color=orange]; 107 | N3 -> N43[label="",color=orange]; 108 | N3 -> N44[label="",color=orange]; 109 | N3 -> N45[label="",color=orange]; 110 | N3 -> N46[label="",color=orange]; 111 | N3 -> N47[label="",color=orange]; 112 | N3 -> N48[label="",color=orange]; 113 | N3 -> N49[label="",color=orange]; 114 | N5 -> N12[label="",color=orange]; 115 | N5 -> N13[label="",color=orange]; 116 | N5 -> N28[label="",color=orange]; 117 | N5 -> N67[label="",color=orange]; 118 | N5 -> N68[label="",color=orange]; 119 | N7 -> N64[label=""]; 120 | N7 -> N71[label=""]; 121 | N7 -> N72[label=""]; 122 | N7 -> N73[label=""]; 123 | N7 -> N74[label=""]; 124 | N8 -> N64[label="",style=dashed,color=red]; 125 | N8 -> N75[label="",style=dashed,color=red]; 126 | N8 -> N76[label="",style=dashed,color=red]; 127 | N9 -> N6[label="",color=orange]; 128 | N9 -> N37[label="",color=orange]; 129 | N9 -> N52[label="",color=orange]; 130 | N9 -> N58[label="",color=orange]; 131 | N10 -> N6[label="",color=orange]; 132 | N10 -> N11[label="",color=orange]; 133 | N10 -> N38[label="",color=orange]; 134 | N10 -> N53[label="",color=orange]; 135 | N11 -> N26[label="",color=orange]; 136 | N11 -> N50[label="",color=orange]; 137 | N12 -> N13[label="",color=orange]; 138 | N13 -> N19[label="",color=orange]; 139 | N14 -> N15[label="",color=orange]; 140 | N14 -> N16[label="",color=orange]; 141 | N14 -> N17[label="",color=orange]; 142 | N14 -> N18[label="",color=orange]; 143 | N14 -> N19[label="",color=orange]; 144 | N14 -> N20[label="",color=orange]; 145 | N14 -> N21[label="",color=orange]; 146 | N15 -> N19[label="",color=orange]; 147 | N15 -> N22[label="",color=orange]; 148 | N17 -> N21[label="",color=orange]; 149 | N17 -> N36[label="",color=orange]; 150 | N18 -> N21[label="",color=orange]; 151 | N18 -> N36[label="",color=orange]; 152 | N24 -> N6[label="",style=dashed,color=red]; 153 | N24 -> N8[label="",style=dashed,color=red]; 154 | N24 -> N9[label="",style=dashed,color=red]; 155 | N25 -> N26[label="",style=dashed,color=red]; 156 | N25 -> N27[label="",style=dashed,color=red]; 157 | N25 -> N28[label="",style=dashed,color=red]; 158 | N25 -> N29[label="",style=dashed,color=red]; 159 | N25 -> N30[label="",style=dashed,color=red]; 160 | N25 -> N31[label="",style=dashed,color=red]; 161 | N29 -> N57[label="",style=dashed,color=red]; 162 | N30 -> N34[label="",style=dashed,color=red]; 163 | N32 -> N11[label="",color=orange]; 164 | N32 -> N33[label="",color=orange]; 165 | N32 -> N34[label="",color=orange]; 166 | N32 -> N35[label="",color=orange]; 167 | N33 -> N19[label="",color=orange]; 168 | N33 -> N22[label="",color=orange]; 169 | N33 -> N23[label="",color=orange]; 170 | N33 -> N54[label="",color=orange]; 171 | N33 -> N59[label="",color=orange]; 172 | N33 -> N60[label="",color=orange]; 173 | N35 -> N18[label="",color=orange]; 174 | N35 -> N19[label="",color=orange]; 175 | N35 -> N21[label="",color=orange]; 176 | N35 -> N66[label="",color=orange]; 177 | N39 -> N21[label="",color=orange]; 178 | N39 -> N36[label="",color=orange]; 179 | N40 -> N4[label="",color=orange]; 180 | N43 -> N4[label="",color=orange]; 181 | N44 -> N19[label="",color=orange]; 182 | N45 -> N33[label="",color=orange]; 183 | N46 -> N4[label="",color=orange]; 184 | N46 -> N40[label="",color=orange]; 185 | N49 -> N69[label="",color=orange]; 186 | N50 -> N26[label="",color=orange]; 187 | N50 -> N31[label="",color=orange]; 188 | N50 -> N51[label="",color=orange]; 189 | N51 -> N26[label="",color=orange]; 190 | N55 -> N56[label=""]; 191 | N56 -> N63[label=""]; 192 | N59 -> N19[label="",color=orange]; 193 | N59 -> N39[label="",color=orange]; 194 | N59 -> N55[label="",color=orange]; 195 | N59 -> N61[label="",color=orange]; 196 | N59 -> N62[label="",color=orange]; 197 | N60 -> N19[label="",color=orange]; 198 | N60 -> N22[label="",color=orange]; 199 | N60 -> N59[label="",color=orange]; 200 | N62 -> N21[label="",color=orange]; 201 | N62 -> N36[label="",color=orange]; 202 | N63 -> N65[label=""]; 203 | N65 -> N19[label=""]; 204 | N67 -> N81[label="",color=orange]; 205 | N67 -> N82[label="",color=orange]; 206 | N69 -> N70[label="",color=orange]; 207 | N71 -> N72[label=""]; 208 | N72 -> N64[label=""]; 209 | N72 -> N77[label=""]; 210 | N73 -> N74[label=""]; 211 | N73 -> N78[label=""]; 212 | N74 -> N4[label=""]; 213 | N74 -> N19[label=""]; 214 | N74 -> N23[label=""]; 215 | N74 -> N34[label=""]; 216 | N74 -> N77[label=""]; 217 | N74 -> N78[label=""]; 218 | N74 -> N79[label=""]; 219 | N74 -> N80[label=""]; 220 | N75 -> N76[label="",style=dashed,color=red]; 221 | N76 -> N64[label="",style=dashed,color=red]; 222 | N76 -> N77[label="",style=dashed,color=red]; 223 | N78 -> N4[label=""]; 224 | N78 -> N19[label=""]; 225 | N78 -> N34[label=""]; 226 | N78 -> N77[label=""]; 227 | N78 -> N79[label=""]; 228 | N78 -> N80[label=""]; 229 | N79 -> N34[label=""]; 230 | N80 -> N18[label=""]; 231 | N80 -> N21[label=""]; 232 | N81 -> N18[label="",color=orange]; 233 | N81 -> N19[label="",color=orange]; 234 | } 235 | -------------------------------------------------------------------------------- /gitlab-api-rs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nbigaouette/gitlab-api-rs/e84c871ad6f852072a373cd950ede546525913eb/gitlab-api-rs.png -------------------------------------------------------------------------------- /src/groups/details.rs: -------------------------------------------------------------------------------- 1 | //! Details of a group 2 | //! 3 | //! https://docs.gitlab.com/ce/api/groups.html#details-of-a-group 4 | //! 5 | //! # Details of a group 6 | //! 7 | //! Get all details of a group. 8 | //! 9 | //! ```text 10 | //! GET /groups/ID 11 | //! ``` 12 | //! 13 | //! Parameters: 14 | //! 15 | //! | Attribute | Type | Required | Description | 16 | //! | --------- | ---- | -------- | ----------- | 17 | //! | `id` | integer/string | yes | The ID or path of a group | 18 | //! 19 | 20 | 21 | use BuildQuery; 22 | use Group; 23 | 24 | use ::errors::*; 25 | 26 | 27 | #[derive(Debug, Clone)] 28 | pub struct GroupLister<'a> { 29 | gl: &'a ::GitLab, 30 | /// The ID of a project 31 | id: ::groups::ListingId, 32 | } 33 | 34 | 35 | impl<'a> GroupLister<'a> { 36 | pub fn new(gl: &'a ::GitLab, id: ::groups::ListingId) -> GroupLister { 37 | GroupLister { gl: gl, id: id } 38 | } 39 | 40 | /// Commit the lister: Query GitLab and return a group. 41 | pub fn list(&self) -> Result { 42 | let query = self.build_query(); 43 | debug!("query: {:?}", query); 44 | 45 | self.gl.get(&query, None, None).chain_err(|| format!("cannot get query {}", query)) 46 | } 47 | } 48 | 49 | 50 | impl<'a> BuildQuery for GroupLister<'a> { 51 | fn build_query(&self) -> String { 52 | let mut query = String::from("groups/"); 53 | 54 | query.push_str(&match self.id { 55 | ::groups::ListingId::Id(id) => id.to_string(), 56 | ::groups::ListingId::NamespaceProject(ref s) => s.replace("/", "%2F"), 57 | }); 58 | 59 | query 60 | } 61 | } 62 | 63 | 64 | #[cfg(test)] 65 | mod tests { 66 | use BuildQuery; 67 | 68 | const TEST_GROUP_ID_I64: i64 = 123; 69 | const TEST_GROUP_ID_STRING: &'static str = "group/project"; 70 | 71 | 72 | #[test] 73 | fn build_query_default_i64() { 74 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 75 | // let gl: ::GitLab = Default::default(); 76 | 77 | let expected_string = format!("groups/{}", TEST_GROUP_ID_I64); 78 | 79 | let lister = gl.groups(); 80 | let lister = lister.details(::groups::ListingId::Id(TEST_GROUP_ID_I64)); 81 | let query = lister.build_query(); 82 | assert_eq!(query, expected_string); 83 | 84 | let lister = gl.groups().details(::groups::ListingId::Id(TEST_GROUP_ID_I64)); 85 | let query = lister.build_query(); 86 | assert_eq!(query, expected_string); 87 | 88 | let query = gl.groups().details(::groups::ListingId::Id(TEST_GROUP_ID_I64)).build_query(); 89 | assert_eq!(query, expected_string); 90 | } 91 | 92 | 93 | #[test] 94 | fn build_query_default_str() { 95 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 96 | // let gl: ::GitLab = Default::default(); 97 | 98 | let expected_string = format!("groups/{}", TEST_GROUP_ID_STRING.replace("/", "%2F")); 99 | 100 | let lister = gl.groups(); 101 | let lister = 102 | lister.details(::groups::ListingId::NamespaceProject(TEST_GROUP_ID_STRING.to_string())); 103 | let query = lister.build_query(); 104 | assert_eq!(query, expected_string); 105 | 106 | let lister = gl.groups() 107 | .details(::groups::ListingId::NamespaceProject(TEST_GROUP_ID_STRING.to_string())); 108 | let query = lister.build_query(); 109 | assert_eq!(query, expected_string); 110 | 111 | let query = gl.groups() 112 | .details(::groups::ListingId::NamespaceProject(TEST_GROUP_ID_STRING.to_string())) 113 | .build_query(); 114 | assert_eq!(query, expected_string); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/groups/mod.rs: -------------------------------------------------------------------------------- 1 | //! List groups 2 | //! 3 | //! https://docs.gitlab.com/ce/api/groups.html#list-groups 4 | //! 5 | //! # List groups 6 | //! 7 | //! Get a list of groups. (As user: my groups or all available, as admin: all groups). 8 | //! 9 | //! ```text 10 | //! GET /groups 11 | //! ``` 12 | //! 13 | //! Parameters: 14 | //! 15 | //! | Attribute | Type | Required | Description | 16 | //! | --------- | ---- | -------- | ----------- | 17 | //! | `skip_groups` | array of integers | no | Skip the group IDs passes | 18 | //! | `all_available` | boolean | no | Show all the groups you have access to | 19 | //! | `search` | string | no | Return list of authorized groups matching the search criteria | 20 | //! | `order_by` | string | no | Order groups by `name` or `path`. Default is `name` | 21 | //! | `sort` | string | no | Order groups in `asc` or `desc` order. Default is `asc` | 22 | //! 23 | //! You can search for groups by name or path. 24 | //! 25 | //! **NOTE**: The _Search for group_ (from 26 | //! https://docs.gitlab.com/ce/api/groups.html#search-for-group) is performed in this module. 27 | //! 28 | //! 29 | 30 | 31 | // use serde_urlencoded; 32 | 33 | use BuildQuery; 34 | use Groups; 35 | 36 | pub mod owned; 37 | pub mod projects; 38 | pub mod details; 39 | 40 | use ::errors::*; 41 | 42 | 43 | #[derive(Debug, Clone, Copy, Serialize, Deserialize)] 44 | pub enum ListingOrderBy { 45 | #[serde(rename = "name")] 46 | Name, 47 | #[serde(rename = "path")] 48 | Path, 49 | } 50 | 51 | 52 | #[derive(Debug, Clone, Serialize, Deserialize)] 53 | pub enum ListingId { 54 | Id(i64), 55 | NamespaceProject(String), 56 | } 57 | 58 | #[derive(Default, Debug, Clone, Serialize, Deserialize)] 59 | struct GroupsListerInternal { 60 | /// Skip the group IDs passes 61 | skip_groups: Option>, 62 | /// Show all the groups you have access to 63 | all_available: Option, 64 | /// Return list of authorized groups matching the search criteria 65 | search: Option, 66 | /// Order groups by `name` or `path`. Default is `name` 67 | order_by: Option, 68 | /// Order groups in `asc` or `desc` order. Default is `asc` 69 | sort: Option<::ListingSort>, 70 | } 71 | 72 | #[derive(Default, Debug, Clone, Serialize, Deserialize)] 73 | struct ProjectsListerInternal { 74 | /// Limit by archived status. 75 | archived: Option, 76 | /// Limit by visibility 77 | visibility: Option<::ListingVisibility>, 78 | /// Return requests ordered by. Default is `ListingOrderBy::CreatedAt`. 79 | order_by: Option<::projects::ListingOrderBy>, 80 | /// Return requests sorted. Default is `::ListingSort::Desc`. 81 | sort: Option<::ListingSort>, 82 | /// Return list of authorized projects according to a search criteria. 83 | search: Option, 84 | /// Return projects ordered by `ci_enabled` flag. Projects with enabled GitLab CI go first. 85 | ci_enabled_first: Option, 86 | } 87 | 88 | 89 | 90 | #[derive(Debug, Clone)] 91 | pub struct GroupsLister<'a> { 92 | gl: &'a ::GitLab, 93 | internal: GroupsListerInternal, 94 | } 95 | 96 | 97 | impl<'a> GroupsLister<'a> { 98 | pub fn new(gl: &'a ::GitLab) -> GroupsLister { 99 | GroupsLister { 100 | gl: gl, 101 | internal: GroupsListerInternal { 102 | skip_groups: None, 103 | all_available: None, 104 | search: None, 105 | order_by: None, 106 | sort: None, 107 | }, 108 | } 109 | } 110 | 111 | 112 | pub fn details(self, id: ListingId) -> details::GroupLister<'a> { 113 | // assert_eq!(self, GroupLister::new(self.gl)); 114 | details::GroupLister::new(self.gl, id) 115 | } 116 | 117 | pub fn owned(self) -> owned::GroupsLister<'a> { 118 | // assert_eq!(self, GroupsLister::new(self.gl)); 119 | owned::GroupsLister::new(self.gl) 120 | } 121 | 122 | pub fn projects(self, id: i64) -> projects::ProjectsLister<'a> { 123 | // assert_eq!(self, ProjectsLister::new(self.gl)); 124 | projects::ProjectsLister::new(self.gl, id) 125 | } 126 | 127 | 128 | pub fn skip_groups(&'a mut self, skip_groups: Vec) -> &'a mut GroupsLister { 129 | self.internal.skip_groups = Some(skip_groups); 130 | self 131 | } 132 | 133 | pub fn all_available(&'a mut self, all_available: bool) -> &'a mut GroupsLister { 134 | self.internal.all_available = Some(all_available); 135 | self 136 | } 137 | 138 | pub fn search(&'a mut self, search: String) -> &'a mut GroupsLister { 139 | self.internal.search = Some(search); 140 | self 141 | } 142 | 143 | pub fn order_by(&'a mut self, order_by: ListingOrderBy) -> &'a mut GroupsLister { 144 | self.internal.order_by = Some(order_by); 145 | self 146 | } 147 | 148 | pub fn sort(&'a mut self, sort: ::ListingSort) -> &'a mut GroupsLister { 149 | self.internal.sort = Some(sort); 150 | self 151 | } 152 | 153 | 154 | /// Commit the lister: Query GitLab and return a list of groups. 155 | pub fn list(&self) -> Result { 156 | let query = self.build_query(); 157 | debug!("query: {:?}", query); 158 | 159 | self.gl.get(&query, None, None).chain_err(|| format!("cannot get query {}", query)) 160 | } 161 | } 162 | 163 | 164 | impl<'a> BuildQuery for GroupsLister<'a> { 165 | fn build_query(&self) -> String { 166 | 167 | // NOTE: Can't use `serde_urlencoded` since it cannot serialize a Vec 168 | // See https://github.com/nox/serde_urlencoded/issues/6 169 | // let encoded = serde_urlencoded::to_string(&self.internal).unwrap(); 170 | 171 | let mut query = String::from("groups"); 172 | 173 | let amp_char = "&"; 174 | let none_char = ""; 175 | let mut split_char = &none_char; 176 | 177 | // Append a "?", only if one of the `Option` is `Some(_)` 178 | query.push_str(match (&self.internal.skip_groups, 179 | &self.internal.all_available, 180 | &self.internal.search, 181 | &self.internal.order_by, 182 | &self.internal.sort) { 183 | (&None, &None, &None, &None, &None) => "", 184 | _ => "?", 185 | }); 186 | 187 | self.internal.skip_groups.as_ref().map(|skip_groups| { 188 | query.push_str(split_char); 189 | split_char = &_char; 190 | 191 | let mut array_split_char = &none_char; 192 | for skip_group in skip_groups { 193 | query.push_str(array_split_char); 194 | query.push_str("skip_groups[]="); 195 | query.push_str(&skip_group.to_string()); 196 | array_split_char = &_char; 197 | } 198 | }); 199 | 200 | self.internal.all_available.map(|all_available| { 201 | query.push_str(split_char); 202 | split_char = &_char; 203 | 204 | if all_available { 205 | query.push_str("all_available=true") 206 | } else { 207 | query.push_str("all_available=false") 208 | } 209 | }); 210 | 211 | self.internal.search.as_ref().map(|search| { 212 | query.push_str(split_char); 213 | split_char = &_char; 214 | 215 | query.push_str("search="); 216 | query.push_str(search); 217 | }); 218 | 219 | self.internal.order_by.map(|order_by| { 220 | query.push_str(split_char); 221 | split_char = &_char; 222 | 223 | query.push_str("order_by="); 224 | query.push_str(match order_by { 225 | ListingOrderBy::Name => "name", 226 | ListingOrderBy::Path => "path", 227 | }); 228 | }); 229 | 230 | self.internal.sort.map(|sort| { 231 | query.push_str(split_char); 232 | split_char = &_char; 233 | 234 | query.push_str("sort="); 235 | query.push_str(match sort { 236 | ::ListingSort::Asc => "asc", 237 | ::ListingSort::Desc => "desc", 238 | }); 239 | }); 240 | 241 | query 242 | } 243 | } 244 | 245 | 246 | #[cfg(test)] 247 | mod tests { 248 | use BuildQuery; 249 | 250 | 251 | #[test] 252 | fn groups_build_query_default() { 253 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 254 | // let gl: ::GitLab = Default::default(); 255 | 256 | let expected_string = "groups"; 257 | let lister = gl.groups(); 258 | let query = lister.build_query(); 259 | assert_eq!(query, expected_string); 260 | 261 | let expected_string = "groups"; 262 | let query = gl.groups().build_query(); 263 | assert_eq!(query, expected_string); 264 | } 265 | 266 | 267 | #[test] 268 | fn groups_build_query_skip_groups() { 269 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 270 | // let gl: ::GitLab = Default::default(); 271 | 272 | let expected_string = "groups?skip_groups[]=1&skip_groups[]=2&skip_groups[]=3"; 273 | let query = gl.groups().skip_groups(vec![1, 2, 3]).build_query(); 274 | assert_eq!(query, expected_string); 275 | } 276 | 277 | 278 | #[test] 279 | fn groups_build_query_all_available() { 280 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 281 | // let gl: ::GitLab = Default::default(); 282 | 283 | let expected_string = "groups?all_available=true"; 284 | let query = gl.groups().all_available(true).build_query(); 285 | assert_eq!(query, expected_string); 286 | 287 | let expected_string = "groups?all_available=false"; 288 | let query = gl.groups().all_available(false).build_query(); 289 | assert_eq!(query, expected_string); 290 | } 291 | 292 | 293 | #[test] 294 | fn groups_build_query_search() { 295 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 296 | // let gl: ::GitLab = Default::default(); 297 | 298 | let expected_string = "groups?search=SearchPattern"; 299 | let query = gl.groups().search(String::from("SearchPattern")).build_query(); 300 | assert_eq!(query, expected_string); 301 | } 302 | 303 | 304 | #[test] 305 | fn groups_build_query_order_by_name() { 306 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 307 | // let gl: ::GitLab = Default::default(); 308 | 309 | let expected_string = "groups?order_by=name"; 310 | let query = gl.groups().order_by(::groups::ListingOrderBy::Name).build_query(); 311 | assert_eq!(query, expected_string); 312 | } 313 | 314 | 315 | #[test] 316 | fn groups_build_query_order_by_path() { 317 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 318 | // let gl: ::GitLab = Default::default(); 319 | 320 | let expected_string = "groups?order_by=path"; 321 | let query = gl.groups().order_by(::groups::ListingOrderBy::Path).build_query(); 322 | assert_eq!(query, expected_string); 323 | } 324 | 325 | 326 | #[test] 327 | fn groups_build_query_sort() { 328 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 329 | // let gl: ::GitLab = Default::default(); 330 | 331 | let expected_string = "groups?sort=asc"; 332 | let query = gl.groups().sort(::ListingSort::Asc).build_query(); 333 | assert_eq!(query, expected_string); 334 | 335 | let expected_string = "groups?sort=desc"; 336 | let query = gl.groups().sort(::ListingSort::Desc).build_query(); 337 | assert_eq!(query, expected_string); 338 | } 339 | 340 | 341 | #[test] 342 | fn groups_build_query_search_order_by_path() { 343 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 344 | // let gl: ::GitLab = Default::default(); 345 | 346 | let expected_string = "groups?search=SearchPattern&order_by=path"; 347 | let query = gl.groups() 348 | .order_by(::groups::ListingOrderBy::Path) 349 | .search(String::from("SearchPattern")) 350 | .build_query(); 351 | assert_eq!(query, expected_string); 352 | let query = gl.groups() 353 | .search(String::from("SearchPattern")) 354 | .order_by(::groups::ListingOrderBy::Path) 355 | .build_query(); 356 | assert_eq!(query, expected_string); 357 | } 358 | } 359 | -------------------------------------------------------------------------------- /src/groups/owned.rs: -------------------------------------------------------------------------------- 1 | //! List owned groups 2 | //! 3 | //! https://docs.gitlab.com/ce/api/groups.html#list-owned-groups 4 | //! 5 | //! # List owned groups 6 | //! 7 | //! Get a list of groups which are owned by the authenticated user. 8 | //! 9 | //! ```text 10 | //! GET /groups/owned 11 | //! ``` 12 | 13 | 14 | use BuildQuery; 15 | use Groups; 16 | 17 | use ::errors::*; 18 | 19 | 20 | #[derive(Debug, Clone)] 21 | pub struct GroupsLister<'a> { 22 | gl: &'a ::GitLab, 23 | } 24 | 25 | 26 | impl<'a> GroupsLister<'a> { 27 | pub fn new(gl: &'a ::GitLab) -> GroupsLister { 28 | GroupsLister { gl: gl } 29 | } 30 | 31 | /// Commit the lister: Query GitLab and return a list of groups. 32 | pub fn list(&self) -> Result { 33 | let query = self.build_query(); 34 | debug!("query: {:?}", query); 35 | 36 | self.gl.get(&query, None, None).chain_err(|| format!("cannot get query {}", query)) 37 | } 38 | } 39 | 40 | 41 | impl<'a> BuildQuery for GroupsLister<'a> { 42 | fn build_query(&self) -> String { 43 | String::from("groups/owned") 44 | } 45 | } 46 | 47 | 48 | #[cfg(test)] 49 | mod tests { 50 | use BuildQuery; 51 | 52 | #[test] 53 | fn build_query_default_split0() { 54 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 55 | // let gl: ::GitLab = Default::default(); 56 | 57 | let expected_string = "groups/owned"; 58 | 59 | let lister = gl.groups(); 60 | let lister = lister.owned(); 61 | let query = lister.build_query(); 62 | assert_eq!(query, expected_string); 63 | } 64 | 65 | #[test] 66 | fn build_query_default_split1() { 67 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 68 | // let gl: ::GitLab = Default::default(); 69 | 70 | let expected_string = "groups/owned"; 71 | 72 | let lister = gl.groups().owned(); 73 | let query = lister.build_query(); 74 | assert_eq!(query, expected_string); 75 | } 76 | 77 | #[test] 78 | fn build_query_default() { 79 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 80 | // let gl: ::GitLab = Default::default(); 81 | 82 | let expected_string = "groups/owned"; 83 | 84 | let query = gl.groups().owned().build_query(); 85 | assert_eq!(query, expected_string); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/groups/projects.rs: -------------------------------------------------------------------------------- 1 | //! List a group's projects 2 | //! 3 | //! https://docs.gitlab.com/ce/api/groups.html#list-a-group-s-projects 4 | //! 5 | //! # List a group's projects 6 | //! 7 | //! Get a list of projects in this group. 8 | //! 9 | //! ```text 10 | //! GET /groups/ID/projects 11 | //! ``` 12 | //! 13 | //! Parameters: 14 | //! 15 | //! - `archived` (optional) - if passed, limit by archived status 16 | //! - `visibility` (optional) - if passed, limit by visibility `public`, `internal`, `private` 17 | //! - `order_by` (optional) - Return requests ordered by `id`, `name`, `path`, `created_at`, 18 | //! `updated_at` or `last_activity_at` fields. Default is `created_at` 19 | //! - `sort` (optional) - Return requests sorted in `asc` or `desc` order. Default is `desc` 20 | //! - `search` (optional) - Return list of authorized projects according to a search criteria 21 | //! - `ci_enabled_first` - Return projects ordered by `ci_enabled` flag. Projects with enabled 22 | //! CI go first 23 | //! 24 | //! 25 | 26 | 27 | use serde_urlencoded; 28 | 29 | use BuildQuery; 30 | 31 | use groups::ProjectsListerInternal; 32 | 33 | use ::errors::*; 34 | 35 | 36 | #[derive(Debug, Clone)] 37 | pub struct ProjectsLister<'a> { 38 | gl: &'a ::GitLab, 39 | id: i64, 40 | internal: ProjectsListerInternal, 41 | } 42 | 43 | 44 | impl<'a> ProjectsLister<'a> { 45 | pub fn new(gl: &'a ::GitLab, id: i64) -> ProjectsLister { 46 | ProjectsLister { 47 | gl: gl, 48 | id: id, 49 | internal: ProjectsListerInternal { 50 | archived: None, 51 | visibility: None, 52 | order_by: None, 53 | sort: None, 54 | search: None, 55 | ci_enabled_first: None, 56 | }, 57 | } 58 | } 59 | 60 | 61 | pub fn archived(&'a mut self, archived: bool) -> &'a mut ProjectsLister { 62 | self.internal.archived = Some(archived); 63 | self 64 | } 65 | 66 | pub fn visibility(&'a mut self, visibility: ::ListingVisibility) -> &'a mut ProjectsLister { 67 | self.internal.visibility = Some(visibility); 68 | self 69 | } 70 | 71 | pub fn order_by(&'a mut self, order_by: ::projects::ListingOrderBy) -> &'a mut ProjectsLister { 72 | self.internal.order_by = Some(order_by); 73 | self 74 | } 75 | 76 | pub fn sort(&'a mut self, sort: ::ListingSort) -> &'a mut ProjectsLister { 77 | self.internal.sort = Some(sort); 78 | self 79 | } 80 | 81 | pub fn search(&'a mut self, search: String) -> &'a mut ProjectsLister { 82 | self.internal.search = Some(search); 83 | self 84 | } 85 | 86 | pub fn ci_enabled_first(&'a mut self, ci_enabled_first: bool) -> &'a mut ProjectsLister { 87 | self.internal.ci_enabled_first = Some(ci_enabled_first); 88 | self 89 | } 90 | 91 | 92 | /// Commit the lister: Query GitLab and return a list of projects. 93 | pub fn list(&self) -> Result<::projects::Projects> { 94 | let query = self.build_query(); 95 | debug!("query: {:?}", query); 96 | 97 | self.gl.get(&query, None, None).chain_err(|| format!("cannot get query {}", query)) 98 | } 99 | } 100 | 101 | 102 | impl<'a> BuildQuery for ProjectsLister<'a> { 103 | fn build_query(&self) -> String { 104 | let encoded = serde_urlencoded::to_string(&self.internal).unwrap(); 105 | 106 | let mut query = format!("groups/{}/projects", self.id); 107 | if !encoded.is_empty() { 108 | query.push_str("?"); 109 | query.push_str(&encoded); 110 | } 111 | 112 | query 113 | } 114 | } 115 | 116 | 117 | #[cfg(test)] 118 | mod tests { 119 | use BuildQuery; 120 | 121 | const TEST_PROJECT_ID: i64 = 123; 122 | 123 | 124 | #[test] 125 | fn build_query_default_split0() { 126 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 127 | // let gl: ::GitLab = Default::default(); 128 | 129 | let expected_string = format!("groups/{}/projects", TEST_PROJECT_ID); 130 | 131 | let lister = gl.groups(); 132 | let lister = lister.projects(TEST_PROJECT_ID); 133 | let query = lister.build_query(); 134 | assert_eq!(query, expected_string); 135 | } 136 | 137 | #[test] 138 | fn build_query_default_split1() { 139 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 140 | // let gl: ::GitLab = Default::default(); 141 | 142 | let expected_string = format!("groups/{}/projects", TEST_PROJECT_ID); 143 | 144 | let lister = gl.groups().projects(TEST_PROJECT_ID); 145 | let query = lister.build_query(); 146 | assert_eq!(query, expected_string); 147 | } 148 | 149 | #[test] 150 | fn build_query_default() { 151 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 152 | // let gl: ::GitLab = Default::default(); 153 | 154 | let expected_string = format!("groups/{}/projects", TEST_PROJECT_ID); 155 | 156 | let query = gl.groups().projects(TEST_PROJECT_ID).build_query(); 157 | assert_eq!(query, expected_string); 158 | } 159 | 160 | 161 | #[test] 162 | fn build_query_archived() { 163 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 164 | // let gl: ::GitLab = Default::default(); 165 | 166 | let expected_string = format!("groups/{}/projects?archived=true", TEST_PROJECT_ID); 167 | let query = gl.groups().projects(TEST_PROJECT_ID).archived(true).build_query(); 168 | assert_eq!(query, expected_string); 169 | 170 | let expected_string = format!("groups/{}/projects?archived=false", TEST_PROJECT_ID); 171 | let query = gl.groups().projects(TEST_PROJECT_ID).archived(false).build_query(); 172 | assert_eq!(query, expected_string); 173 | } 174 | 175 | 176 | #[test] 177 | fn build_query_visibility() { 178 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 179 | // let gl: ::GitLab = Default::default(); 180 | 181 | let expected_string = format!("groups/{}/projects?visibility=public", TEST_PROJECT_ID); 182 | let query = gl.groups() 183 | .projects(TEST_PROJECT_ID) 184 | .visibility(::ListingVisibility::Public) 185 | .build_query(); 186 | assert_eq!(query, expected_string); 187 | 188 | let expected_string = format!("groups/{}/projects?visibility=internal", TEST_PROJECT_ID); 189 | let query = gl.groups() 190 | .projects(TEST_PROJECT_ID) 191 | .visibility(::ListingVisibility::Internal) 192 | .build_query(); 193 | assert_eq!(query, expected_string); 194 | 195 | let expected_string = format!("groups/{}/projects?visibility=private", TEST_PROJECT_ID); 196 | let query = gl.groups() 197 | .projects(TEST_PROJECT_ID) 198 | .visibility(::ListingVisibility::Private) 199 | .build_query(); 200 | assert_eq!(query, expected_string); 201 | } 202 | 203 | 204 | #[test] 205 | fn build_query_order_by() { 206 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 207 | // let gl: ::GitLab = Default::default(); 208 | 209 | let expected_string = format!("groups/{}/projects?order_by=id", TEST_PROJECT_ID); 210 | let query = gl.groups() 211 | .projects(TEST_PROJECT_ID) 212 | .order_by(::projects::ListingOrderBy::Id) 213 | .build_query(); 214 | assert_eq!(query, expected_string); 215 | 216 | let expected_string = format!("groups/{}/projects?order_by=name", TEST_PROJECT_ID); 217 | let query = gl.groups() 218 | .projects(TEST_PROJECT_ID) 219 | .order_by(::projects::ListingOrderBy::Name) 220 | .build_query(); 221 | assert_eq!(query, expected_string); 222 | 223 | let expected_string = format!("groups/{}/projects?order_by=path", TEST_PROJECT_ID); 224 | let query = gl.groups() 225 | .projects(TEST_PROJECT_ID) 226 | .order_by(::projects::ListingOrderBy::Path) 227 | .build_query(); 228 | assert_eq!(query, expected_string); 229 | 230 | let expected_string = format!("groups/{}/projects?order_by=created_at", TEST_PROJECT_ID); 231 | let query = gl.groups() 232 | .projects(TEST_PROJECT_ID) 233 | .order_by(::projects::ListingOrderBy::CreatedAt) 234 | .build_query(); 235 | assert_eq!(query, expected_string); 236 | 237 | let expected_string = format!("groups/{}/projects?order_by=updated_at", TEST_PROJECT_ID); 238 | let query = gl.groups() 239 | .projects(TEST_PROJECT_ID) 240 | .order_by(::projects::ListingOrderBy::UpdatedAt) 241 | .build_query(); 242 | assert_eq!(query, expected_string); 243 | 244 | let expected_string = format!("groups/{}/projects?order_by=last_activity_at", 245 | TEST_PROJECT_ID); 246 | let query = gl.groups() 247 | .projects(TEST_PROJECT_ID) 248 | .order_by(::projects::ListingOrderBy::LastActivityAt) 249 | .build_query(); 250 | assert_eq!(query, expected_string); 251 | } 252 | 253 | 254 | #[test] 255 | fn build_query_sort() { 256 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 257 | // let gl: ::GitLab = Default::default(); 258 | 259 | let expected_string = format!("groups/{}/projects?sort=asc", TEST_PROJECT_ID); 260 | let query = gl.groups().projects(TEST_PROJECT_ID).sort(::ListingSort::Asc).build_query(); 261 | assert_eq!(query, expected_string); 262 | 263 | let expected_string = format!("groups/{}/projects?sort=desc", TEST_PROJECT_ID); 264 | let query = gl.groups().projects(TEST_PROJECT_ID).sort(::ListingSort::Desc).build_query(); 265 | assert_eq!(query, expected_string); 266 | } 267 | 268 | 269 | #[test] 270 | fn build_query_search() { 271 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 272 | // let gl: ::GitLab = Default::default(); 273 | 274 | let expected_string = format!("groups/{}/projects?search=SearchPattern", TEST_PROJECT_ID); 275 | let query = gl.groups() 276 | .projects(TEST_PROJECT_ID) 277 | .search(String::from("SearchPattern")) 278 | .build_query(); 279 | assert_eq!(query, expected_string); 280 | } 281 | 282 | 283 | #[test] 284 | fn build_query_ci_enabled_first() { 285 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 286 | // let gl: ::GitLab = Default::default(); 287 | 288 | let expected_string = format!("groups/{}/projects?ci_enabled_first=true", TEST_PROJECT_ID); 289 | let query = gl.groups().projects(TEST_PROJECT_ID).ci_enabled_first(true).build_query(); 290 | assert_eq!(query, expected_string); 291 | 292 | let expected_string = format!("groups/{}/projects?ci_enabled_first=false", TEST_PROJECT_ID); 293 | let query = gl.groups().projects(TEST_PROJECT_ID).ci_enabled_first(false).build_query(); 294 | assert_eq!(query, expected_string); 295 | } 296 | 297 | 298 | #[test] 299 | fn build_query_multiple() { 300 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 301 | // let gl: ::GitLab = Default::default(); 302 | 303 | let expected_string = format!("groups/{}/projects?archived=true&ci_enabled_first=true", 304 | TEST_PROJECT_ID); 305 | let query = gl.groups() 306 | .projects(TEST_PROJECT_ID) 307 | .archived(true) 308 | .ci_enabled_first(true) 309 | .build_query(); 310 | assert_eq!(query, expected_string); 311 | } 312 | } 313 | -------------------------------------------------------------------------------- /src/issues/group.rs: -------------------------------------------------------------------------------- 1 | //! List group issues 2 | //! 3 | //! https://docs.gitlab.com/ce/api/issues.html#list-group-issues 4 | //! 5 | //! # List group issues 6 | //! 7 | //! Get a list of a group's issues. 8 | //! 9 | //! ```text 10 | //! GET /groups/ID/issues 11 | //! GET /groups/ID/issues?state=opened 12 | //! GET /groups/ID/issues?state=closed 13 | //! GET /groups/ID/issues?labels=foo 14 | //! GET /groups/ID/issues?labels=foo,bar 15 | //! GET /groups/ID/issues?labels=foo,bar&state=opened 16 | //! GET /groups/ID/issues?milestone=1.0.0 17 | //! GET /groups/ID/issues?milestone=1.0.0&state=opened 18 | //! ``` 19 | //! 20 | //! | Attribute | Type | Required | Description | 21 | //! | --------- | ---- | -------- | ----------- | 22 | //! | `id` | integer | yes | The ID of a group | 23 | //! | `state` | string | no | Return all issues or just those that are `opened` or `closed`| 24 | //! | `labels` | string | no | Comma-separated list of label names, issues must have all labels to be returned | 25 | //! | `milestone` | string| no | The milestone title | 26 | //! | `order_by`| string | no | Return requests ordered by `created_at` or `updated_at` fields. Default is `created_at` | 27 | //! | `sort` | string | no | Return requests sorted in `asc` or `desc` order. Default is `desc` | 28 | //! 29 | 30 | 31 | use serde_urlencoded; 32 | 33 | use BuildQuery; 34 | 35 | use ::errors::*; 36 | 37 | 38 | #[derive(Debug, Clone)] 39 | pub struct IssuesLister<'a> { 40 | gl: &'a ::GitLab, 41 | /// The ID of a group 42 | id: i64, 43 | internal: ::issues::GroupIssuesListerInternal, 44 | } 45 | 46 | 47 | impl<'a> IssuesLister<'a> { 48 | pub fn new(gl: &'a ::GitLab, id: i64) -> IssuesLister { 49 | IssuesLister { 50 | gl: gl, 51 | id: id, 52 | internal: ::issues::GroupIssuesListerInternal { 53 | state: None, 54 | labels: None, 55 | milestone: None, 56 | order_by: None, 57 | sort: None, 58 | }, 59 | } 60 | } 61 | 62 | 63 | pub fn state(&'a mut self, state: ::issues::State) -> &'a mut IssuesLister { 64 | self.internal.state = Some(state); 65 | self 66 | } 67 | 68 | pub fn milestone(&'a mut self, milestone: String) -> &'a mut IssuesLister { 69 | self.internal.milestone = Some(milestone); 70 | self 71 | } 72 | 73 | pub fn labels(&'a mut self, labels: Vec) -> &'a mut IssuesLister { 74 | self.internal.labels = Some(labels); 75 | self 76 | } 77 | 78 | pub fn order_by(&'a mut self, order_by: ::issues::ListingOrderBy) -> &'a mut IssuesLister { 79 | self.internal.order_by = Some(order_by); 80 | self 81 | } 82 | 83 | pub fn sort(&'a mut self, sort: ::ListingSort) -> &'a mut IssuesLister { 84 | self.internal.sort = Some(sort); 85 | self 86 | } 87 | 88 | 89 | /// Commit the lister: Query GitLab and return a list of issues. 90 | pub fn list(&self) -> Result<::issues::Issues> { 91 | let query = self.build_query(); 92 | debug!("query: {:?}", query); 93 | 94 | self.gl.get(&query, None, None).chain_err(|| format!("cannot get query {}", query)) 95 | } 96 | } 97 | 98 | 99 | 100 | impl<'a> BuildQuery for IssuesLister<'a> { 101 | fn build_query(&self) -> String { 102 | 103 | // NOTE: Can't use `serde_urlencoded` since it cannot serialize a Vec 104 | // See https://github.com/nox/serde_urlencoded/issues/6 105 | // let encoded = serde_urlencoded::to_string(&self.internal).unwrap(); 106 | 107 | let mut query = format!("groups/{}/issues", self.id); 108 | let amp_char = "&"; 109 | let comma_char = ","; 110 | let none_char = ""; 111 | let mut split_char = &none_char; 112 | 113 | // Append a "?" only if at least one of the `Option` is `Some(_)` or if 114 | // strings contain something. 115 | query.push_str(match (&self.internal.state, 116 | &self.internal.labels, 117 | &self.internal.milestone, 118 | &self.internal.order_by, 119 | &self.internal.sort) { 120 | (&None, &None, &None, &None, &None) => "", 121 | _ => "?", 122 | }); 123 | 124 | self.internal.state.map(|state| { 125 | query.push_str(split_char); 126 | split_char = &_char; 127 | 128 | query.push_str("state="); 129 | query.push_str(match state { 130 | ::issues::State::Opened => "opened", 131 | ::issues::State::Closed => "closed", 132 | }); 133 | }); 134 | 135 | self.internal.labels.as_ref().map(|labels| { 136 | query.push_str(split_char); 137 | split_char = &_char; 138 | 139 | query.push_str("labels="); 140 | 141 | let mut array_split_char = &none_char; 142 | for label in labels { 143 | query.push_str(array_split_char); 144 | query.push_str(&label.to_string()); 145 | array_split_char = &comma_char; 146 | } 147 | }); 148 | 149 | self.internal.milestone.as_ref().map(|milestone| { 150 | query.push_str(split_char); 151 | split_char = &_char; 152 | 153 | let params = &[("milestone", milestone)]; 154 | query.push_str(&serde_urlencoded::to_string(¶ms).unwrap()); 155 | }); 156 | 157 | self.internal.order_by.map(|order_by| { 158 | query.push_str(split_char); 159 | split_char = &_char; 160 | 161 | query.push_str("order_by="); 162 | query.push_str(match order_by { 163 | ::issues::ListingOrderBy::CreatedAt => "created_at", 164 | ::issues::ListingOrderBy::UpdatedAt => "updated_at", 165 | }); 166 | }); 167 | 168 | self.internal.sort.map(|sort| { 169 | query.push_str(split_char); 170 | split_char = &_char; 171 | 172 | query.push_str("sort="); 173 | query.push_str(match sort { 174 | ::ListingSort::Asc => "asc", 175 | ::ListingSort::Desc => "desc", 176 | }); 177 | }); 178 | 179 | query 180 | } 181 | } 182 | 183 | 184 | #[cfg(test)] 185 | mod tests { 186 | use BuildQuery; 187 | 188 | const TEST_PROJECT_ID: i64 = 123; 189 | 190 | #[test] 191 | fn build_query_default() { 192 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 193 | // let gl: ::GitLab = Default::default(); 194 | 195 | let expected_string = format!("groups/{}/issues", TEST_PROJECT_ID); 196 | 197 | let lister = gl.issues(); 198 | let lister = lister.group(TEST_PROJECT_ID); 199 | let query = lister.build_query(); 200 | assert_eq!(query, expected_string); 201 | 202 | let lister = gl.issues().group(TEST_PROJECT_ID); 203 | let query = lister.build_query(); 204 | assert_eq!(query, expected_string); 205 | 206 | let query = gl.issues().group(TEST_PROJECT_ID).build_query(); 207 | assert_eq!(query, expected_string); 208 | } 209 | 210 | 211 | #[test] 212 | fn build_query_state() { 213 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 214 | // let gl: ::GitLab = Default::default(); 215 | 216 | let expected_string = "groups/123/issues?state=opened"; 217 | let query = gl.issues().group(TEST_PROJECT_ID).state(::issues::State::Opened).build_query(); 218 | assert_eq!(query, expected_string); 219 | 220 | let expected_string = "groups/123/issues?state=closed"; 221 | let query = gl.issues().group(TEST_PROJECT_ID).state(::issues::State::Closed).build_query(); 222 | assert_eq!(query, expected_string); 223 | } 224 | 225 | 226 | #[test] 227 | fn build_query_milestone() { 228 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 229 | // let gl: ::GitLab = Default::default(); 230 | 231 | let expected_string = "groups/123/issues?milestone=Test+Milestone"; 232 | let query = gl.issues() 233 | .group(TEST_PROJECT_ID) 234 | .milestone("Test Milestone".to_string()) 235 | .build_query(); 236 | assert_eq!(query, expected_string); 237 | } 238 | 239 | 240 | #[test] 241 | fn build_query_skip_groups() { 242 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 243 | // let gl: ::GitLab = Default::default(); 244 | 245 | let expected_string = "groups/123/issues?labels=label1,label2,label3"; 246 | let query = gl.issues() 247 | .group(TEST_PROJECT_ID) 248 | .labels(vec![String::from("label1"), String::from("label2"), String::from("label3")]) 249 | .build_query(); 250 | assert_eq!(query, expected_string); 251 | } 252 | 253 | 254 | #[test] 255 | fn build_query_order_by() { 256 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 257 | // let gl: ::GitLab = Default::default(); 258 | 259 | let expected_string = "groups/123/issues?order_by=created_at"; 260 | let query = gl.issues() 261 | .group(TEST_PROJECT_ID) 262 | .order_by(::issues::ListingOrderBy::CreatedAt) 263 | .build_query(); 264 | assert_eq!(query, expected_string); 265 | 266 | let expected_string = "groups/123/issues?order_by=updated_at"; 267 | let query = gl.issues() 268 | .group(TEST_PROJECT_ID) 269 | .order_by(::issues::ListingOrderBy::UpdatedAt) 270 | .build_query(); 271 | assert_eq!(query, expected_string); 272 | } 273 | 274 | 275 | #[test] 276 | fn build_query_sort() { 277 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 278 | // let gl: ::GitLab = Default::default(); 279 | 280 | let expected_string = "groups/123/issues?sort=asc"; 281 | let query = gl.issues().group(TEST_PROJECT_ID).sort(::ListingSort::Asc).build_query(); 282 | assert_eq!(query, expected_string); 283 | 284 | let expected_string = "groups/123/issues?sort=desc"; 285 | let query = gl.issues().group(TEST_PROJECT_ID).sort(::ListingSort::Desc).build_query(); 286 | assert_eq!(query, expected_string); 287 | } 288 | 289 | 290 | #[test] 291 | fn build_query_multiple() { 292 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 293 | // let gl: ::GitLab = Default::default(); 294 | 295 | let expected_string = "groups/123/issues?order_by=created_at&sort=asc"; 296 | let query = gl.issues() 297 | .group(TEST_PROJECT_ID) 298 | .sort(::ListingSort::Asc) 299 | .order_by(::issues::ListingOrderBy::CreatedAt) 300 | .build_query(); 301 | assert_eq!(query, expected_string); 302 | } 303 | } 304 | -------------------------------------------------------------------------------- /src/issues/mod.rs: -------------------------------------------------------------------------------- 1 | //! List issues 2 | //! 3 | //! https://docs.gitlab.com/ce/api/issues.html#list-issues 4 | //! 5 | //! # List issues 6 | //! 7 | //! Get all issues created by the authenticated user. 8 | //! 9 | //! ```text 10 | //! GET /issues 11 | //! ``` 12 | //! 13 | //! | Attribute | Type | Required | Description | 14 | //! | --------- | ---- | -------- | ----------- | 15 | //! | `state` | string | no | Return all issues or just those that are `opened` or `closed`| 16 | //! | `labels` | string | no | Comma-separated list of label names, issues with any of the labels will be returned | 17 | //! | `order_by`| string | no | Return requests ordered by `created_at` or `updated_at` fields. Default is `created_at` | 18 | //! | `sort` | string | no | Return requests sorted in `asc` or `desc` order. Default is `desc` | 19 | //! 20 | 21 | 22 | // use serde_urlencoded; 23 | 24 | use BuildQuery; 25 | use Lister; 26 | 27 | pub mod group; 28 | pub mod project; 29 | pub mod single; 30 | 31 | use ::errors::*; 32 | 33 | 34 | #[derive(Debug, Copy, Clone, Serialize, Deserialize)] 35 | pub enum State { 36 | #[serde(rename = "opened")] 37 | Opened, 38 | #[serde(rename = "closed")] 39 | Closed, 40 | } 41 | 42 | 43 | #[derive(Debug, Clone, Copy, Serialize, Deserialize)] 44 | pub enum ListingOrderBy { 45 | #[serde(rename = "created_at")] 46 | CreatedAt, 47 | #[serde(rename = "updated_at")] 48 | UpdatedAt, 49 | } 50 | 51 | 52 | #[derive(Default, Debug, Clone, Serialize, Deserialize)] 53 | struct IssuesListerInternal { 54 | /// State of issues to return. 55 | state: Option, 56 | /// Labels of issues to return. 57 | labels: Option>, 58 | /// Return requests ordered by. Default is `ListingOrderBy::CreatedAt`. 59 | order_by: Option, 60 | /// Return requests sorted. Default is `::ListingSort::Desc`. 61 | sort: Option<::ListingSort>, 62 | } 63 | 64 | 65 | #[derive(Default, Debug, Clone, Serialize, Deserialize)] 66 | struct GroupIssuesListerInternal { 67 | /// State of issues to return. 68 | state: Option, 69 | /// Labels of issues to return. 70 | labels: Option>, 71 | /// The milestone title 72 | milestone: Option, 73 | /// Return requests ordered by. Default is `ListingOrderBy::CreatedAt`. 74 | order_by: Option, 75 | /// Return requests sorted. Default is `::ListingSort::Desc`. 76 | sort: Option<::ListingSort>, 77 | } 78 | 79 | 80 | #[derive(Default, Debug, Clone, Serialize, Deserialize)] 81 | struct ProjectsIssuesListerInternal { 82 | iid: Option, 83 | /// State of issues to return. 84 | state: Option, 85 | /// Labels of issues to return. 86 | labels: Option>, 87 | /// The milestone title 88 | milestone: Option, 89 | /// Return requests ordered by. Default is `ListingOrderBy::CreatedAt`. 90 | order_by: Option, 91 | /// Return requests sorted. Default is `::ListingSort::Desc`. 92 | sort: Option<::ListingSort>, 93 | } 94 | 95 | 96 | #[derive(Debug, Serialize, Deserialize)] 97 | pub enum IssueState { 98 | #[serde(rename = "opened")] 99 | Opened, 100 | #[serde(rename = "closed")] 101 | Closed, 102 | #[serde(rename = "reopened")] 103 | Reopened, 104 | } 105 | 106 | 107 | #[derive(Debug, Serialize, Deserialize)] 108 | pub struct Issue { 109 | pub id: i64, 110 | pub iid: i64, 111 | pub project_id: i64, 112 | pub title: String, 113 | pub description: String, 114 | pub state: IssueState, 115 | pub created_at: String, // FIXME: Use date type? 116 | pub updated_at: String, // FIXME: Use date type? 117 | pub labels: Vec, 118 | pub milestone: Option<::Milestone>, 119 | pub assignee: Option<::User>, 120 | pub author: ::User, 121 | pub subscribed: bool, 122 | pub user_notes_count: i64, 123 | pub upvotes: i64, 124 | pub downvotes: i64, 125 | pub due_date: Option, // FIXME: Use date type? 126 | pub confidential: bool, 127 | pub web_url: Option 128 | } 129 | 130 | 131 | pub type Issues = Vec; 132 | 133 | 134 | #[derive(Debug, Clone)] 135 | pub struct IssuesLister<'a> { 136 | gl: &'a ::GitLab, 137 | internal: IssuesListerInternal, 138 | } 139 | 140 | 141 | impl<'a> Lister for IssuesLister<'a> { 142 | /// Commit the lister: Query GitLab and return a list of issues. 143 | fn list(&self) -> Result { 144 | let query = self.build_query(); 145 | debug!("query: {:?}", query); 146 | 147 | self.gl.get(&query, None, None).chain_err(|| format!("cannot get query {}", query)) 148 | } 149 | 150 | /// Commit the lister: Query GitLab and return a list of issues. 151 | fn list_paginated(&self, page: u16, per_page: u16) -> Result { 152 | let query = self.build_query(); 153 | debug!("query: {:?}", query); 154 | 155 | self.gl.get(&query, page, per_page).chain_err(|| format!("cannot get query {}", query)) 156 | } 157 | } 158 | 159 | 160 | impl<'a> IssuesLister<'a> { 161 | pub fn new(gl: &'a ::GitLab) -> IssuesLister { 162 | IssuesLister { 163 | gl: gl, 164 | internal: IssuesListerInternal { 165 | state: None, 166 | labels: None, 167 | order_by: None, 168 | sort: None, 169 | }, 170 | } 171 | } 172 | 173 | 174 | pub fn group(self, id: i64) -> group::IssuesLister<'a> { 175 | // assert_eq!(self, IssuesLister::new(self.gl)); 176 | group::IssuesLister::new(self.gl, id) 177 | } 178 | 179 | pub fn project(self, id: i64) -> project::IssuesLister<'a> { 180 | // assert_eq!(self, IssuesLister::new(self.gl)); 181 | project::IssuesLister::new(self.gl, id) 182 | } 183 | 184 | pub fn single(self, id: i64, issue_id: i64) -> single::IssueLister<'a> { 185 | // assert_eq!(self, IssuesLister::new(self.gl)); 186 | single::IssueLister::new(self.gl, id, issue_id) 187 | } 188 | 189 | 190 | pub fn state(&'a mut self, state: State) -> &'a mut IssuesLister { 191 | self.internal.state = Some(state); 192 | self 193 | } 194 | 195 | pub fn labels(&'a mut self, labels: Vec) -> &'a mut IssuesLister { 196 | self.internal.labels = Some(labels); 197 | self 198 | } 199 | 200 | pub fn order_by(&'a mut self, order_by: ListingOrderBy) -> &'a mut IssuesLister { 201 | self.internal.order_by = Some(order_by); 202 | self 203 | } 204 | 205 | pub fn sort(&'a mut self, sort: ::ListingSort) -> &'a mut IssuesLister { 206 | self.internal.sort = Some(sort); 207 | self 208 | } 209 | 210 | 211 | // /// Commit the lister: Query GitLab and return a list of issues. 212 | // pub fn list(&self) -> Result { 213 | // let query = self.build_query(); 214 | // debug!("query: {:?}", query); 215 | // 216 | // self.gl.get(&query, None, None).chain_err(|| format!("cannot get query {}", query)) 217 | // } 218 | } 219 | 220 | 221 | impl<'a> BuildQuery for IssuesLister<'a> { 222 | fn build_query(&self) -> String { 223 | 224 | // NOTE: Can't use `serde_urlencoded` since it cannot serialize a Vec 225 | // See https://github.com/nox/serde_urlencoded/issues/6 226 | // let encoded = serde_urlencoded::to_string(&self.internal).unwrap(); 227 | 228 | let mut query = String::from("issues"); 229 | 230 | let amp_char = "&"; 231 | let comma_char = ","; 232 | let none_char = ""; 233 | let mut split_char = &none_char; 234 | 235 | // Append a "?" only if at least one of the `Option` is `Some(_)` or if 236 | // strings contain something. 237 | query.push_str(match (&self.internal.state, 238 | &self.internal.labels, 239 | &self.internal.order_by, 240 | &self.internal.sort) { 241 | (&None, &None, &None, &None) => "", 242 | _ => "?", 243 | }); 244 | 245 | self.internal.state.map(|state| { 246 | query.push_str(split_char); 247 | split_char = &_char; 248 | 249 | query.push_str("state="); 250 | query.push_str(match state { 251 | State::Opened => "opened", 252 | State::Closed => "closed", 253 | }); 254 | }); 255 | 256 | self.internal.labels.as_ref().map(|labels| { 257 | query.push_str(split_char); 258 | split_char = &_char; 259 | 260 | query.push_str("labels="); 261 | 262 | let mut array_split_char = &none_char; 263 | for label in labels { 264 | query.push_str(array_split_char); 265 | query.push_str(&label.to_string()); 266 | array_split_char = &comma_char; 267 | } 268 | }); 269 | 270 | self.internal.order_by.map(|order_by| { 271 | query.push_str(split_char); 272 | split_char = &_char; 273 | 274 | query.push_str("order_by="); 275 | query.push_str(match order_by { 276 | ListingOrderBy::CreatedAt => "created_at", 277 | ListingOrderBy::UpdatedAt => "updated_at", 278 | }); 279 | }); 280 | 281 | self.internal.sort.map(|sort| { 282 | query.push_str(split_char); 283 | split_char = &_char; 284 | 285 | query.push_str("sort="); 286 | query.push_str(match sort { 287 | ::ListingSort::Asc => "asc", 288 | ::ListingSort::Desc => "desc", 289 | }); 290 | }); 291 | 292 | query 293 | } 294 | } 295 | 296 | 297 | #[cfg(test)] 298 | mod tests { 299 | use BuildQuery; 300 | 301 | 302 | #[test] 303 | fn build_query_default() { 304 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 305 | // let gl: ::GitLab = Default::default(); 306 | 307 | let expected_string = "issues"; 308 | let lister = gl.issues(); 309 | let query = lister.build_query(); 310 | assert_eq!(query, expected_string); 311 | 312 | let expected_string = "issues"; 313 | let query = gl.issues().build_query(); 314 | assert_eq!(query, expected_string); 315 | } 316 | 317 | 318 | #[test] 319 | fn build_query_state() { 320 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 321 | // let gl: ::GitLab = Default::default(); 322 | 323 | let expected_string = "issues?state=opened"; 324 | let query = gl.issues().state(::issues::State::Opened).build_query(); 325 | assert_eq!(query, expected_string); 326 | 327 | let expected_string = "issues?state=closed"; 328 | let query = gl.issues().state(::issues::State::Closed).build_query(); 329 | assert_eq!(query, expected_string); 330 | } 331 | 332 | 333 | #[test] 334 | fn build_query_skip_groups() { 335 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 336 | // let gl: ::GitLab = Default::default(); 337 | 338 | let expected_string = "issues?labels=label1,label2,label3"; 339 | let query = gl.issues() 340 | .labels(vec![String::from("label1"), String::from("label2"), String::from("label3")]) 341 | .build_query(); 342 | assert_eq!(query, expected_string); 343 | } 344 | 345 | 346 | #[test] 347 | fn build_query_order_by() { 348 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 349 | // let gl: ::GitLab = Default::default(); 350 | 351 | let expected_string = "issues?order_by=created_at"; 352 | let query = gl.issues().order_by(::issues::ListingOrderBy::CreatedAt).build_query(); 353 | assert_eq!(query, expected_string); 354 | 355 | let expected_string = "issues?order_by=updated_at"; 356 | let query = gl.issues().order_by(::issues::ListingOrderBy::UpdatedAt).build_query(); 357 | assert_eq!(query, expected_string); 358 | } 359 | 360 | 361 | #[test] 362 | fn build_query_sort() { 363 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 364 | // let gl: ::GitLab = Default::default(); 365 | 366 | let expected_string = "issues?sort=asc"; 367 | let query = gl.issues().sort(::ListingSort::Asc).build_query(); 368 | assert_eq!(query, expected_string); 369 | 370 | let expected_string = "issues?sort=desc"; 371 | let query = gl.issues().sort(::ListingSort::Desc).build_query(); 372 | assert_eq!(query, expected_string); 373 | } 374 | 375 | 376 | #[test] 377 | fn build_query_multiple() { 378 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 379 | // let gl: ::GitLab = Default::default(); 380 | 381 | let expected_string = "issues?order_by=created_at&sort=asc"; 382 | let query = gl.issues() 383 | .sort(::ListingSort::Asc) 384 | .order_by(::issues::ListingOrderBy::CreatedAt) 385 | .build_query(); 386 | assert_eq!(query, expected_string); 387 | } 388 | } 389 | -------------------------------------------------------------------------------- /src/issues/project.rs: -------------------------------------------------------------------------------- 1 | //! List project issues 2 | //! 3 | //! https://docs.gitlab.com/ce/api/issues.html#list-project-issues 4 | //! 5 | //! # List project issues 6 | //! 7 | //! Get a list of a project's issues. 8 | //! 9 | //! ```text 10 | //! GET /projects/ID/issues 11 | //! GET /projects/ID/issues?state=opened 12 | //! GET /projects/ID/issues?state=closed 13 | //! GET /projects/ID/issues?labels=foo 14 | //! GET /projects/ID/issues?labels=foo,bar 15 | //! GET /projects/ID/issues?labels=foo,bar&state=opened 16 | //! GET /projects/ID/issues?milestone=1.0.0 17 | //! GET /projects/ID/issues?milestone=1.0.0&state=opened 18 | //! GET /projects/ID/issues?iid=42 19 | //! ``` 20 | //! 21 | //! | Attribute | Type | Required | Description | 22 | //! | --------- | ---- | -------- | ----------- | 23 | //! | `id` | integer | yes | The ID of a project | 24 | //! | `iid` | integer | no | Return the issue having the given `iid` | 25 | //! | `state` | string | no | Return all issues or just those that are `opened` or `closed`| 26 | //! | `labels` | string | no | Comma-separated list of label names, issues with any of the labels will be returned | 27 | //! | `milestone` | string| no | The milestone title | 28 | //! | `order_by`| string | no | Return requests ordered by `created_at` or `updated_at` fields. Default is `created_at` | 29 | //! | `sort` | string | no | Return requests sorted in `asc` or `desc` order. Default is `desc` | 30 | //! 31 | 32 | 33 | use serde_urlencoded; 34 | 35 | use BuildQuery; 36 | use Lister; 37 | 38 | use ::errors::*; 39 | 40 | 41 | #[derive(Debug, Clone)] 42 | pub struct IssuesLister<'a> { 43 | gl: &'a ::GitLab, 44 | /// The ID of a group 45 | id: i64, 46 | internal: ::issues::ProjectsIssuesListerInternal, 47 | } 48 | 49 | 50 | impl<'a> Lister<::issues::Issues> for IssuesLister<'a> { 51 | /// Commit the lister: Query GitLab and return a list of issues. 52 | fn list(&self) -> Result<::issues::Issues> { 53 | let query = self.build_query(); 54 | debug!("query: {:?}", query); 55 | 56 | self.gl.get(&query, None, None).chain_err(|| format!("cannot get query {}", query)) 57 | } 58 | 59 | fn list_paginated(&self, page: u16, per_page: u16) -> Result<::issues::Issues> { 60 | let query = self.build_query(); 61 | debug!("query: {:?}", query); 62 | 63 | self.gl.get(&query, page, per_page).chain_err(|| format!("cannot get query {}", query)) 64 | } 65 | } 66 | 67 | 68 | impl<'a> IssuesLister<'a> { 69 | pub fn new(gl: &'a ::GitLab, id: i64) -> IssuesLister { 70 | IssuesLister { 71 | gl: gl, 72 | id: id, 73 | internal: ::issues::ProjectsIssuesListerInternal { 74 | iid: None, 75 | state: None, 76 | labels: None, 77 | milestone: None, 78 | order_by: None, 79 | sort: None, 80 | }, 81 | } 82 | } 83 | 84 | 85 | pub fn iid(&'a mut self, iid: i64) -> &'a mut IssuesLister { 86 | self.internal.iid = Some(iid); 87 | self 88 | } 89 | 90 | pub fn state(&'a mut self, state: ::issues::State) -> &'a mut IssuesLister { 91 | self.internal.state = Some(state); 92 | self 93 | } 94 | 95 | pub fn milestone(&'a mut self, milestone: String) -> &'a mut IssuesLister { 96 | self.internal.milestone = Some(milestone); 97 | self 98 | } 99 | 100 | pub fn labels(&'a mut self, labels: Vec) -> &'a mut IssuesLister { 101 | self.internal.labels = Some(labels); 102 | self 103 | } 104 | 105 | pub fn order_by(&'a mut self, order_by: ::issues::ListingOrderBy) -> &'a mut IssuesLister { 106 | self.internal.order_by = Some(order_by); 107 | self 108 | } 109 | 110 | pub fn sort(&'a mut self, sort: ::ListingSort) -> &'a mut IssuesLister { 111 | self.internal.sort = Some(sort); 112 | self 113 | } 114 | } 115 | 116 | 117 | 118 | impl<'a> BuildQuery for IssuesLister<'a> { 119 | fn build_query(&self) -> String { 120 | // NOTE: Can't use `serde_urlencoded` since it cannot serialize a Vec 121 | // See https://github.com/nox/serde_urlencoded/issues/6 122 | // let encoded = serde_urlencoded::to_string(&self.internal).unwrap(); 123 | 124 | let mut query = format!("projects/{}/issues", self.id); 125 | let amp_char = "&"; 126 | let comma_char = ","; 127 | let none_char = ""; 128 | let mut split_char = &none_char; 129 | 130 | // Append a "?" only if at least one of the `Option` is `Some(_)` or if 131 | // strings contain something. 132 | query.push_str(match (&self.internal.iid, 133 | &self.internal.state, 134 | &self.internal.labels, 135 | &self.internal.milestone, 136 | &self.internal.order_by, 137 | &self.internal.sort) { 138 | (&None, &None, &None, &None, &None, &None) => "", 139 | _ => "?", 140 | }); 141 | 142 | self.internal.iid.as_ref().map(|iid| { 143 | query.push_str(split_char); 144 | split_char = &_char; 145 | 146 | query.push_str("iid="); 147 | query.push_str(&iid.to_string()); 148 | }); 149 | 150 | self.internal.state.map(|state| { 151 | query.push_str(split_char); 152 | split_char = &_char; 153 | 154 | query.push_str("state="); 155 | query.push_str(match state { 156 | ::issues::State::Opened => "opened", 157 | ::issues::State::Closed => "closed", 158 | }); 159 | }); 160 | 161 | self.internal.labels.as_ref().map(|labels| { 162 | query.push_str(split_char); 163 | split_char = &_char; 164 | 165 | query.push_str("labels="); 166 | 167 | let mut array_split_char = &none_char; 168 | for label in labels { 169 | query.push_str(array_split_char); 170 | query.push_str(&label.to_string()); 171 | array_split_char = &comma_char; 172 | } 173 | }); 174 | 175 | self.internal.milestone.as_ref().map(|milestone| { 176 | query.push_str(split_char); 177 | split_char = &_char; 178 | 179 | let params = &[("milestone", milestone)]; 180 | query.push_str(&serde_urlencoded::to_string(¶ms).unwrap()); 181 | }); 182 | 183 | self.internal.order_by.map(|order_by| { 184 | query.push_str(split_char); 185 | split_char = &_char; 186 | 187 | query.push_str("order_by="); 188 | query.push_str(match order_by { 189 | ::issues::ListingOrderBy::CreatedAt => "created_at", 190 | ::issues::ListingOrderBy::UpdatedAt => "updated_at", 191 | }); 192 | }); 193 | 194 | self.internal.sort.map(|sort| { 195 | query.push_str(split_char); 196 | split_char = &_char; 197 | 198 | query.push_str("sort="); 199 | query.push_str(match sort { 200 | ::ListingSort::Asc => "asc", 201 | ::ListingSort::Desc => "desc", 202 | }); 203 | }); 204 | 205 | query 206 | } 207 | } 208 | 209 | 210 | 211 | #[cfg(test)] 212 | mod tests { 213 | use BuildQuery; 214 | 215 | const TEST_PROJECT_ID: i64 = 123; 216 | 217 | 218 | #[test] 219 | fn build_query_default() { 220 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 221 | // let gl: ::GitLab = Default::default(); 222 | 223 | let expected_string = format!("projects/{}/issues", TEST_PROJECT_ID); 224 | 225 | let lister = gl.issues(); 226 | let lister = lister.project(TEST_PROJECT_ID); 227 | let query = lister.build_query(); 228 | assert_eq!(query, expected_string); 229 | 230 | let lister = gl.issues().project(TEST_PROJECT_ID); 231 | let query = lister.build_query(); 232 | assert_eq!(query, expected_string); 233 | 234 | let query = gl.issues().project(TEST_PROJECT_ID).build_query(); 235 | assert_eq!(query, expected_string); 236 | } 237 | 238 | 239 | #[test] 240 | fn build_query_iid() { 241 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 242 | // let gl: ::GitLab = Default::default(); 243 | 244 | let expected_string = format!("projects/{}/issues?iid=42", TEST_PROJECT_ID); 245 | let query = gl.issues().project(TEST_PROJECT_ID).iid(42).build_query(); 246 | assert_eq!(query, expected_string); 247 | } 248 | 249 | 250 | #[test] 251 | fn build_query_milestone() { 252 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 253 | // let gl: ::GitLab = Default::default(); 254 | 255 | let expected_string = format!("projects/{}/issues?milestone=Test+Milestone", 256 | TEST_PROJECT_ID); 257 | let query = gl.issues() 258 | .project(TEST_PROJECT_ID) 259 | .milestone("Test Milestone".to_string()) 260 | .build_query(); 261 | assert_eq!(query, expected_string); 262 | } 263 | 264 | 265 | #[test] 266 | fn build_query_state() { 267 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 268 | // let gl: ::GitLab = Default::default(); 269 | 270 | let expected_string = format!("projects/{}/issues?state=opened", TEST_PROJECT_ID); 271 | let query = 272 | gl.issues().project(TEST_PROJECT_ID).state(::issues::State::Opened).build_query(); 273 | assert_eq!(query, expected_string); 274 | 275 | let expected_string = format!("projects/{}/issues?state=closed", TEST_PROJECT_ID); 276 | let query = 277 | gl.issues().project(TEST_PROJECT_ID).state(::issues::State::Closed).build_query(); 278 | assert_eq!(query, expected_string); 279 | } 280 | 281 | 282 | #[test] 283 | fn build_query_skip_groups() { 284 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 285 | // let gl: ::GitLab = Default::default(); 286 | 287 | let expected_string = format!("projects/{}/issues?labels=label1,label2,label3", 288 | TEST_PROJECT_ID); 289 | let query = gl.issues() 290 | .project(TEST_PROJECT_ID) 291 | .labels(vec![String::from("label1"), String::from("label2"), String::from("label3")]) 292 | .build_query(); 293 | assert_eq!(query, expected_string); 294 | } 295 | 296 | 297 | #[test] 298 | fn build_query_order_by() { 299 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 300 | // let gl: ::GitLab = Default::default(); 301 | 302 | let expected_string = format!("projects/{}/issues?order_by=created_at", TEST_PROJECT_ID); 303 | let query = gl.issues() 304 | .project(TEST_PROJECT_ID) 305 | .order_by(::issues::ListingOrderBy::CreatedAt) 306 | .build_query(); 307 | assert_eq!(query, expected_string); 308 | 309 | let expected_string = format!("projects/{}/issues?order_by=updated_at", TEST_PROJECT_ID); 310 | let query = gl.issues() 311 | .project(TEST_PROJECT_ID) 312 | .order_by(::issues::ListingOrderBy::UpdatedAt) 313 | .build_query(); 314 | assert_eq!(query, expected_string); 315 | } 316 | 317 | 318 | #[test] 319 | fn build_query_sort() { 320 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 321 | // let gl: ::GitLab = Default::default(); 322 | 323 | let expected_string = format!("projects/{}/issues?sort=asc", TEST_PROJECT_ID); 324 | let query = gl.issues().project(TEST_PROJECT_ID).sort(::ListingSort::Asc).build_query(); 325 | assert_eq!(query, expected_string); 326 | 327 | let expected_string = format!("projects/{}/issues?sort=desc", TEST_PROJECT_ID); 328 | let query = gl.issues().project(TEST_PROJECT_ID).sort(::ListingSort::Desc).build_query(); 329 | assert_eq!(query, expected_string); 330 | } 331 | 332 | 333 | #[test] 334 | fn build_query_multiple() { 335 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 336 | // let gl: ::GitLab = Default::default(); 337 | 338 | let expected_string = format!("projects/{}/issues?order_by=created_at&sort=asc", 339 | TEST_PROJECT_ID); 340 | let query = gl.issues() 341 | .project(TEST_PROJECT_ID) 342 | .sort(::ListingSort::Asc) 343 | .order_by(::issues::ListingOrderBy::CreatedAt) 344 | .build_query(); 345 | assert_eq!(query, expected_string); 346 | } 347 | } 348 | -------------------------------------------------------------------------------- /src/issues/single.rs: -------------------------------------------------------------------------------- 1 | //! Single issue 2 | //! 3 | //! https://docs.gitlab.com/ce/api/issues.html#single-issue 4 | //! 5 | //! # Single issue 6 | //! 7 | //! Get a single project issue. 8 | //! 9 | //! ```text 10 | //! GET /projects/ID/issues/ISSUE_ID 11 | //! ``` 12 | //! 13 | //! | Attribute | Type | Required | Description | 14 | //! | --------- | ---- | -------- | ----------- | 15 | //! | `id` | integer | yes | The ID of a project | 16 | //! | `issue_id`| integer | yes | The ID of a project's issue | 17 | //! 18 | //! 19 | 20 | 21 | use BuildQuery; 22 | 23 | use ::errors::*; 24 | 25 | 26 | #[derive(Debug, Clone)] 27 | pub struct IssueLister<'a> { 28 | gl: &'a ::GitLab, 29 | /// The ID of a project 30 | id: i64, 31 | /// The ID of a project's issue 32 | issue_id: i64, 33 | } 34 | 35 | 36 | impl<'a> IssueLister<'a> { 37 | pub fn new(gl: &'a ::GitLab, id: i64, issue_id: i64) -> IssueLister { 38 | IssueLister { 39 | gl: gl, 40 | id: id, 41 | issue_id: issue_id, 42 | } 43 | } 44 | 45 | /// Commit the lister: Query GitLab and return a list of projects. 46 | pub fn list(&self) -> Result<::issues::Issue> { 47 | let query = self.build_query(); 48 | debug!("query: {:?}", query); 49 | 50 | self.gl.get(&query, None, None).chain_err(|| format!("cannot get query {}", query)) 51 | } 52 | } 53 | 54 | 55 | impl<'a> BuildQuery for IssueLister<'a> { 56 | fn build_query(&self) -> String { 57 | format!("projects/{}/issues/{}", self.id, self.issue_id) 58 | } 59 | } 60 | 61 | 62 | #[cfg(test)] 63 | mod tests { 64 | use BuildQuery; 65 | 66 | const TEST_PROJECT_ID: i64 = 123; 67 | const TEST_ISSUE_ID: i64 = 456; 68 | 69 | 70 | #[test] 71 | fn build_query_default() { 72 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 73 | // let gl: ::GitLab = Default::default(); 74 | 75 | let expected_string = format!("projects/{}/issues/{}", TEST_PROJECT_ID, TEST_ISSUE_ID); 76 | 77 | let lister = gl.issues(); 78 | let lister = lister.single(TEST_PROJECT_ID, TEST_ISSUE_ID); 79 | let query = lister.build_query(); 80 | assert_eq!(query, expected_string); 81 | 82 | let lister = gl.issues().single(TEST_PROJECT_ID, TEST_ISSUE_ID); 83 | let query = lister.build_query(); 84 | assert_eq!(query, expected_string); 85 | 86 | let query = gl.issues().single(TEST_PROJECT_ID, TEST_ISSUE_ID).build_query(); 87 | assert_eq!(query, expected_string); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate serde_derive; 3 | extern crate serde; 4 | extern crate serde_json; 5 | extern crate serde_urlencoded; 6 | 7 | #[macro_use] 8 | extern crate error_chain; 9 | 10 | // We'll put our errors in an `errors` module, and other modules in 11 | // this crate will `use errors::*;` to get access to everything 12 | // `error_chain!` creates. 13 | pub mod errors { 14 | // Create the Error, ErrorKind, ResultExt, and Result types 15 | error_chain!{} 16 | } 17 | 18 | use ::errors::*; 19 | 20 | #[macro_use] 21 | extern crate log; 22 | extern crate hyper; 23 | 24 | extern crate regex; 25 | extern crate url; 26 | 27 | 28 | pub mod gitlab; 29 | pub mod groups; 30 | pub mod projects; 31 | pub mod issues; 32 | pub mod merge_requests; 33 | 34 | // Re-export those structs 35 | pub use gitlab::GitLab; 36 | // pub use projects::Project; 37 | // Re-export those traits 38 | 39 | 40 | #[derive(Debug, Clone, Copy, Serialize, Deserialize)] 41 | pub enum ListingSort { 42 | #[serde(rename = "asc")] 43 | Asc, 44 | #[serde(rename = "desc")] 45 | Desc, 46 | } 47 | 48 | 49 | #[derive(Debug, Clone, Copy, Serialize, Deserialize)] 50 | pub enum ListingVisibility { 51 | #[serde(rename = "public")] 52 | Public, 53 | #[serde(rename = "internal")] 54 | Internal, 55 | #[serde(rename = "private")] 56 | Private, 57 | } 58 | 59 | 60 | #[derive(Debug, Serialize, Deserialize)] 61 | pub enum UserState { 62 | #[serde(rename = "active")] 63 | Active, 64 | 65 | #[serde(rename = "blocked")] 66 | Blocked, 67 | } 68 | 69 | 70 | #[derive(Debug, Serialize, Deserialize)] 71 | pub enum MilestoneState { 72 | #[serde(rename = "active")] 73 | Active, 74 | 75 | #[serde(rename = "closed")] 76 | Closed, 77 | } 78 | 79 | 80 | #[derive(Debug, Serialize, Deserialize)] 81 | pub struct Version { 82 | pub version: String, 83 | pub revision: String, 84 | } 85 | 86 | 87 | #[derive(Debug, Serialize, Deserialize)] 88 | pub struct Group { 89 | pub id: i64, 90 | pub name: String, 91 | pub path: String, 92 | pub description: String, 93 | pub visibility_level: i64, 94 | pub lfs_enabled: bool, 95 | pub avatar_url: Option, 96 | pub web_url: String, 97 | pub request_access_enabled: bool, 98 | } 99 | 100 | pub type Groups = Vec; 101 | 102 | 103 | #[derive(Debug, Serialize, Deserialize)] 104 | pub struct Milestone { 105 | pub id: i64, 106 | pub iid: i64, 107 | pub project_id: i64, 108 | pub title: String, 109 | pub description: String, 110 | pub state: MilestoneState, 111 | pub created_at: String, // FIXME: Use date type? 112 | pub updated_at: String, // FIXME: Use date type? 113 | pub due_date: Option // FIXME: Use date type? 114 | } 115 | 116 | 117 | #[derive(Debug, Serialize, Deserialize)] 118 | pub struct User { 119 | pub name: String, 120 | pub username: String, 121 | pub id: i64, 122 | pub state: UserState, 123 | pub avatar_url: Option, 124 | pub web_url: Option 125 | } 126 | 127 | 128 | 129 | trait BuildQuery { 130 | fn build_query(&self) -> String; 131 | } 132 | 133 | pub trait Lister { 134 | fn list(&self) -> Result; 135 | fn list_paginated(&self, page: u16, per_page: u16) -> Result; 136 | } 137 | 138 | 139 | #[cfg(test)] 140 | mod tests { 141 | // use gitlab::GitLab; 142 | // use hyper; 143 | use serde_json; 144 | 145 | // #[test] 146 | // fn unauthorized() { 147 | // let gl = GitLab::new("http", "gitlab.com", 80, "XXXXXXXXXXXXX").unwrap(); 148 | // println!("gl: {:?}", gl); 149 | // assert_eq!(gl.attempt_connection().unwrap().status, 150 | // hyper::status::StatusCode::Unauthorized); 151 | // } 152 | 153 | #[test] 154 | fn deserialize_project() { 155 | let json_reply = r##"[ 156 | { 157 | "id": 517564, 158 | "description": "GitLab API library and client in Rust", 159 | "default_branch": "master", 160 | "tag_list": [], 161 | "public": false, 162 | "archived": false, 163 | "visibility_level": 0, 164 | "ssh_url_to_repo": "git@gitlab.com:nbigaouette1/gitlab-api-rs.git", 165 | "http_url_to_repo": "https://gitlab.com/nbigaouette1/gitlab-api-rs.git", 166 | "web_url": "https://gitlab.com/nbigaouette1/gitlab-api-rs", 167 | "owner": { 168 | "name": "Nicolas Bigaouette", 169 | "username": "nbigaouette1", 170 | "id": 163821, 171 | "state": "active", 172 | "avatar_url": "https://secure.gravatar.com/avatar/3325e461df2fda8738f35a8bf4fd735e?s=80&d=identicon", 173 | "web_url": "https://gitlab.com/nbigaouette1" 174 | }, 175 | "name": "gitlab-api-rs", 176 | "name_with_namespace": "Nicolas Bigaouette / gitlab-api-rs", 177 | "path": "gitlab-api-rs", 178 | "path_with_namespace": "nbigaouette1/gitlab-api-rs", 179 | "container_registry_enabled": null, 180 | "issues_enabled": true, 181 | "merge_requests_enabled": true, 182 | "wiki_enabled": true, 183 | "builds_enabled": false, 184 | "snippets_enabled": false, 185 | "created_at": "2015-10-09T00:32:18.646Z", 186 | "last_activity_at": "2017-01-31T14:46:26.638Z", 187 | "shared_runners_enabled": true, 188 | "lfs_enabled": true, 189 | "creator_id": 163821, 190 | "namespace": { 191 | "id": 193119, 192 | "name": "nbigaouette1", 193 | "path": "nbigaouette1", 194 | "kind": "user" 195 | }, 196 | "avatar_url": null, 197 | "star_count": 0, 198 | "forks_count": 0, 199 | "open_issues_count": 1, 200 | "public_builds": true, 201 | "shared_with_groups": [], 202 | "only_allow_merge_if_build_succeeds": false, 203 | "request_access_enabled": true, 204 | "only_allow_merge_if_all_discussions_are_resolved": null, 205 | "approvals_before_merge": 0 206 | } 207 | ]"##; 208 | 209 | let _: ::projects::Projects = serde_json::from_str(json_reply) 210 | .expect("JSON deserialization failed"); 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /src/merge_requests/mod.rs: -------------------------------------------------------------------------------- 1 | //! List merge requests 2 | //! 3 | //! [https://docs.gitlab.com/ce/api/merge\_requests.html#list-merge-requests](https://docs.gitlab.com/ce/api/merge_requests.html#list-merge-requests) 4 | //! 5 | //! # List merge requests 6 | //! 7 | //! Get all merge requests for this project. 8 | //! The `state` parameter can be used to get only merge requests with a given state (`opened`, `closed`, or `merged`) or all of them (`all`). 9 | //! The pagination parameters `page` and `per_page` can be used to restrict the list of merge requests. 10 | //! 11 | //! ```text 12 | //! GET /projects/ID/merge_requests 13 | //! GET /projects/ID/merge_requests?state=opened 14 | //! GET /projects/ID/merge_requests?state=all 15 | //! GET /projects/ID/merge_requests?iid=42 16 | //! GET /projects/ID/merge_requests?iid[]=42&iid[]=43 17 | //! ``` 18 | //! 19 | //! Parameters: 20 | //! 21 | //! - `id` (required) - The ID of a project 22 | //! - `iid` (optional) - Return the request having the given `iid` 23 | //! - `state` (optional) - Return `all` requests or just those that are `merged`, `opened` or `closed` 24 | //! - `order_by` (optional) - Return requests ordered by `created_at` or `updated_at` fields. Default is `created_at` 25 | //! - `sort` (optional) - Return requests sorted in `asc` or `desc` order. Default is `desc` 26 | //! 27 | //! 28 | 29 | 30 | // use serde_urlencoded; 31 | 32 | use BuildQuery; 33 | use Lister; 34 | 35 | pub mod single; 36 | 37 | use ::errors::*; 38 | 39 | 40 | #[derive(Debug, Copy, Clone, Serialize, Deserialize)] 41 | pub enum State { 42 | #[serde(rename = "merged")] 43 | Merged, 44 | #[serde(rename = "opened")] 45 | Opened, 46 | #[serde(rename = "closed")] 47 | Closed, 48 | #[serde(rename = "all")] 49 | All, 50 | } 51 | 52 | #[derive(Debug, Serialize, Deserialize)] 53 | pub enum Status { 54 | #[serde(rename = "can_be_merged")] 55 | CanBeMerged, 56 | #[serde(rename = "cannot_be_merged")] 57 | CannotBeMerged, 58 | #[serde(rename = "unchecked")] 59 | Unchecked, 60 | } 61 | 62 | 63 | #[derive(Debug, Clone, Copy, Serialize, Deserialize)] 64 | pub enum ListingOrderBy { 65 | #[serde(rename = "created_at")] 66 | CreatedAt, 67 | #[serde(rename = "updated_at")] 68 | UpdatedAt, 69 | } 70 | 71 | 72 | 73 | 74 | #[derive(Default, Debug, Clone, Serialize, Deserialize)] 75 | struct MergeRequestsListerInternal { 76 | /// Merge request's IID 77 | iid: Option>, 78 | /// State of the requests 79 | state: Option, 80 | /// Return requests ordered by. Default is `ListingOrderBy::CreatedAt`. 81 | order_by: Option, 82 | /// Return requests sorted. Default is `ListingSort::Desc`. 83 | sort: Option<::ListingSort>, 84 | } 85 | 86 | 87 | 88 | #[derive(Debug, Serialize, Deserialize)] 89 | pub struct MergeRequest { 90 | pub id: i64, 91 | pub iid: i64, 92 | pub project_id: i64, 93 | pub title: String, 94 | pub description: String, 95 | pub state: State, 96 | pub created_at: String, // FIXME: Use chrono crate 97 | pub updated_at: String, // FIXME: Use chrono crate 98 | pub target_branch: String, 99 | pub source_branch: String, 100 | pub upvotes: i64, 101 | pub downvotes: i64, 102 | pub author: ::User, 103 | pub assignee: Option<::User>, 104 | pub source_project_id: i64, 105 | pub target_project_id: i64, 106 | pub labels: Vec, 107 | pub work_in_progress: bool, 108 | pub milestone: Option<::Milestone>, 109 | pub merge_when_build_succeeds: bool, 110 | pub merge_status: Status, 111 | pub sha: Option, 112 | pub merge_commit_sha: Option, 113 | pub subscribed: bool, 114 | pub user_notes_count: i64, 115 | pub should_remove_source_branch: Option, 116 | pub force_remove_source_branch: Option, 117 | pub web_url: String 118 | } 119 | 120 | pub type MergeRequests = Vec; 121 | 122 | 123 | #[derive(Debug, Clone)] 124 | pub struct MergeRequestsLister<'a> { 125 | gl: &'a ::GitLab, 126 | id: i64, 127 | internal: MergeRequestsListerInternal, 128 | } 129 | 130 | 131 | impl<'a> Lister for MergeRequestsLister<'a> { 132 | /// Commit the lister: Query GitLab and return a list of projects. 133 | fn list(&self) -> Result { 134 | let query = self.build_query(); 135 | debug!("query: {:?}", query); 136 | 137 | self.gl.get(&query, None, None).chain_err(|| format!("cannot get query {}", query)) 138 | } 139 | 140 | /// Commit the lister: Query GitLab and return a list of issues. 141 | fn list_paginated(&self, page: u16, per_page: u16) -> Result { 142 | let query = self.build_query(); 143 | debug!("query: {:?}", query); 144 | 145 | self.gl.get(&query, page, per_page).chain_err(|| format!("cannot get query {}", query)) 146 | } 147 | } 148 | 149 | 150 | #[allow(dead_code)] 151 | impl<'a> MergeRequestsLister<'a> { 152 | pub fn new(gl: &'a ::GitLab, id: i64) -> MergeRequestsLister { 153 | MergeRequestsLister { 154 | gl: gl, 155 | id: id, 156 | internal: MergeRequestsListerInternal { 157 | iid: None, 158 | state: None, 159 | order_by: None, 160 | sort: None, 161 | }, 162 | } 163 | } 164 | 165 | 166 | pub fn single(self, merge_request_id: i64) -> single::MergeRequestLister<'a> { 167 | // assert_eq!(self, MergeRequestLister::new(self.gl)); 168 | single::MergeRequestLister::new(self.gl, self.id, merge_request_id) 169 | } 170 | 171 | 172 | pub fn iid(&'a mut self, iid: Vec) -> &'a mut MergeRequestsLister { 173 | info!("Using 'idd' fails when there is more than one element!"); 174 | if iid.len() > 1 { 175 | println!("Using 'idd' fails when there is more than one element!"); 176 | } 177 | self.internal.iid = Some(iid); 178 | self 179 | } 180 | pub fn state(&'a mut self, state: State) -> &'a mut MergeRequestsLister { 181 | self.internal.state = Some(state); 182 | self 183 | } 184 | pub fn order_by(&'a mut self, order_by: ListingOrderBy) -> &'a mut MergeRequestsLister { 185 | self.internal.order_by = Some(order_by); 186 | self 187 | } 188 | fn sort(&'a mut self, sort: ::ListingSort) -> &'a mut MergeRequestsLister { 189 | self.internal.sort = Some(sort); 190 | self 191 | } 192 | } 193 | 194 | 195 | impl<'a> BuildQuery for MergeRequestsLister<'a> { 196 | fn build_query(&self) -> String { 197 | 198 | // NOTE: Can't use `serde_urlencoded` since it cannot serialize a Vec 199 | // See https://github.com/nox/serde_urlencoded/issues/6 200 | // let encoded = serde_urlencoded::to_string(&self.internal).unwrap(); 201 | 202 | let mut query = format!("projects/{}/merge_requests", self.id); 203 | 204 | let amp_char = "&"; 205 | let none_char = ""; 206 | let mut split_char = &none_char; 207 | 208 | // Append a "?" only if at least one of the `Option` is `Some(_)` or if 209 | // strings contain something. 210 | query.push_str(match (&self.internal.iid, 211 | &self.internal.state, 212 | &self.internal.order_by, 213 | &self.internal.sort) { 214 | (&None, &None, &None, &None) => "", 215 | _ => "?", 216 | }); 217 | 218 | self.internal.iid.as_ref().map(|iid| { 219 | query.push_str(split_char); 220 | split_char = &_char; 221 | 222 | if iid.len() == 1 { 223 | query.push_str("iid="); 224 | query.push_str(&iid[0].to_string()); 225 | } else { 226 | let mut array_split_char = &none_char; 227 | for iid in iid { 228 | query.push_str(array_split_char); 229 | query.push_str("iid[]="); 230 | query.push_str(&iid.to_string()); 231 | array_split_char = &_char; 232 | } 233 | } 234 | }); 235 | 236 | self.internal.state.map(|state| { 237 | query.push_str(split_char); 238 | split_char = &_char; 239 | 240 | query.push_str("state="); 241 | query.push_str(match state { 242 | State::Merged => "merged", 243 | State::Opened => "opened", 244 | State::Closed => "closed", 245 | State::All => "all", 246 | }); 247 | }); 248 | 249 | self.internal.order_by.map(|order_by| { 250 | query.push_str(split_char); 251 | split_char = &_char; 252 | 253 | query.push_str("order_by="); 254 | query.push_str(match order_by { 255 | ListingOrderBy::CreatedAt => "created_at", 256 | ListingOrderBy::UpdatedAt => "updated_at", 257 | }); 258 | }); 259 | 260 | self.internal.sort.map(|sort| { 261 | query.push_str(split_char); 262 | split_char = &_char; 263 | 264 | query.push_str("sort="); 265 | query.push_str(match sort { 266 | ::ListingSort::Asc => "asc", 267 | ::ListingSort::Desc => "desc", 268 | }); 269 | }); 270 | 271 | query 272 | } 273 | } 274 | 275 | 276 | #[cfg(test)] 277 | mod tests { 278 | use BuildQuery; 279 | 280 | 281 | const TEST_PROJECT_ID: i64 = 123; 282 | 283 | 284 | #[test] 285 | fn build_query_default() { 286 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 287 | // let gl: ::GitLab = Default::default(); 288 | 289 | let expected_string = format!("projects/{}/merge_requests", TEST_PROJECT_ID); 290 | let lister = gl.merge_requests(TEST_PROJECT_ID); 291 | let query = lister.build_query(); 292 | assert_eq!(query, expected_string); 293 | 294 | let expected_string = format!("projects/{}/merge_requests", TEST_PROJECT_ID); 295 | let query = gl.merge_requests(TEST_PROJECT_ID).build_query(); 296 | assert_eq!(query, expected_string); 297 | } 298 | 299 | 300 | #[test] 301 | fn build_query_iid() { 302 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 303 | // let gl: ::GitLab = Default::default(); 304 | 305 | let expected_string = format!("projects/{}/merge_requests?iid=456", TEST_PROJECT_ID); 306 | let query = gl.merge_requests(TEST_PROJECT_ID).iid(vec![456]).build_query(); 307 | assert_eq!(query, expected_string); 308 | 309 | let expected_string = format!("projects/123/merge_requests?iid[]=456&iid[]=789"); 310 | let query = gl.merge_requests(TEST_PROJECT_ID).iid(vec![456, 789]).build_query(); 311 | assert_eq!(query, expected_string); 312 | } 313 | 314 | 315 | #[test] 316 | fn build_query_state() { 317 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 318 | // let gl: ::GitLab = Default::default(); 319 | 320 | let expected_string = format!("projects/{}/merge_requests?state=merged", TEST_PROJECT_ID); 321 | let query = 322 | gl.merge_requests(TEST_PROJECT_ID).state(::merge_requests::State::Merged).build_query(); 323 | assert_eq!(query, expected_string); 324 | 325 | let expected_string = format!("projects/{}/merge_requests?state=opened", TEST_PROJECT_ID); 326 | let query = 327 | gl.merge_requests(TEST_PROJECT_ID).state(::merge_requests::State::Opened).build_query(); 328 | assert_eq!(query, expected_string); 329 | 330 | let expected_string = format!("projects/{}/merge_requests?state=closed", TEST_PROJECT_ID); 331 | let query = 332 | gl.merge_requests(TEST_PROJECT_ID).state(::merge_requests::State::Closed).build_query(); 333 | assert_eq!(query, expected_string); 334 | 335 | let expected_string = format!("projects/{}/merge_requests?state=all", TEST_PROJECT_ID); 336 | let query = 337 | gl.merge_requests(TEST_PROJECT_ID).state(::merge_requests::State::All).build_query(); 338 | assert_eq!(query, expected_string); 339 | } 340 | 341 | 342 | #[test] 343 | fn build_query_order_by() { 344 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 345 | // let gl: ::GitLab = Default::default(); 346 | 347 | let expected_string = format!("projects/{}/merge_requests?order_by=created_at", 348 | TEST_PROJECT_ID); 349 | let query = gl.merge_requests(TEST_PROJECT_ID) 350 | .order_by(::merge_requests::ListingOrderBy::CreatedAt) 351 | .build_query(); 352 | assert_eq!(query, expected_string); 353 | 354 | let expected_string = format!("projects/{}/merge_requests?order_by=updated_at", 355 | TEST_PROJECT_ID); 356 | let query = gl.merge_requests(TEST_PROJECT_ID) 357 | .order_by(::merge_requests::ListingOrderBy::UpdatedAt) 358 | .build_query(); 359 | assert_eq!(query, expected_string); 360 | } 361 | 362 | 363 | #[test] 364 | fn build_query_sort() { 365 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 366 | // let gl: ::GitLab = Default::default(); 367 | 368 | let expected_string = format!("projects/{}/merge_requests?sort=asc", TEST_PROJECT_ID); 369 | let query = gl.merge_requests(TEST_PROJECT_ID).sort(::ListingSort::Asc).build_query(); 370 | assert_eq!(query, expected_string); 371 | 372 | let expected_string = format!("projects/{}/merge_requests?sort=desc", TEST_PROJECT_ID); 373 | let query = gl.merge_requests(TEST_PROJECT_ID).sort(::ListingSort::Desc).build_query(); 374 | assert_eq!(query, expected_string); 375 | } 376 | 377 | 378 | #[test] 379 | fn build_query_multiple() { 380 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 381 | // let gl: ::GitLab = Default::default(); 382 | 383 | let expected_string = format!("projects/{}/merge_requests?\ 384 | iid[]=456&iid[]=789&order_by=created_at&sort=asc", 385 | TEST_PROJECT_ID); 386 | let query = gl.merge_requests(TEST_PROJECT_ID) 387 | .iid(vec![456, 789]) 388 | .sort(::ListingSort::Asc) 389 | .order_by(::merge_requests::ListingOrderBy::CreatedAt) 390 | .build_query(); 391 | assert_eq!(query, expected_string); 392 | } 393 | } 394 | -------------------------------------------------------------------------------- /src/merge_requests/single.rs: -------------------------------------------------------------------------------- 1 | //! Get single MR 2 | //! 3 | //! [https://docs.gitlab.com/ce/api/merge\_requests.html#get-single-mr](https://docs.gitlab.com/ce/api/merge_requests.html#get-single-mr) 4 | //! 5 | //! # Get single MR 6 | //! 7 | //! Shows information about a single merge request. 8 | //! 9 | //! ```text 10 | //! GET /projects/ID/merge_requests/MERGE_REQUEST_ID 11 | //! ``` 12 | //! 13 | //! Parameters: 14 | //! 15 | //! - `id` (required) - The ID of a project 16 | //! - `merge_request_id` (required) - The ID of MR 17 | //! 18 | //! 19 | 20 | 21 | // use serde_urlencoded; 22 | 23 | use BuildQuery; 24 | 25 | use merge_requests::MergeRequest; 26 | 27 | use ::errors::*; 28 | 29 | 30 | #[derive(Debug, Clone)] 31 | pub struct MergeRequestLister<'a> { 32 | gl: &'a ::GitLab, 33 | id: i64, 34 | mr_id: i64, 35 | } 36 | 37 | 38 | #[allow(dead_code)] 39 | impl<'a> MergeRequestLister<'a> { 40 | pub fn new(gl: &'a ::GitLab, id: i64, mr_id: i64) -> MergeRequestLister { 41 | MergeRequestLister { 42 | gl: gl, 43 | id: id, 44 | mr_id: mr_id, 45 | } 46 | } 47 | 48 | /// Commit the lister: Query GitLab and return a list of merge requests. 49 | pub fn list(&self) -> Result { 50 | let query = self.build_query(); 51 | debug!("query: {:?}", query); 52 | 53 | self.gl.get(&query, None, None).chain_err(|| format!("cannot get query {}", query)) 54 | } 55 | } 56 | 57 | 58 | impl<'a> BuildQuery for MergeRequestLister<'a> { 59 | fn build_query(&self) -> String { 60 | format!("projects/{}/merge_requests/{}", self.id, self.mr_id) 61 | } 62 | } 63 | 64 | 65 | #[cfg(test)] 66 | mod tests { 67 | use BuildQuery; 68 | 69 | const TEST_PROJECT_ID: i64 = 123; 70 | const TEST_MR_ID: i64 = 456; 71 | 72 | #[test] 73 | fn build_query_default() { 74 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 75 | // let gl: ::GitLab = Default::default(); 76 | 77 | let expected_string = format!("projects/{}/merge_requests/{}", TEST_PROJECT_ID, TEST_MR_ID); 78 | 79 | let lister = gl.merge_requests(TEST_PROJECT_ID); 80 | let lister = lister.single(TEST_MR_ID); 81 | let query = lister.build_query(); 82 | assert_eq!(query, expected_string); 83 | 84 | let lister = gl.merge_requests(TEST_PROJECT_ID).single(TEST_MR_ID); 85 | let query = lister.build_query(); 86 | assert_eq!(query, expected_string); 87 | 88 | let query = gl.merge_requests(TEST_PROJECT_ID).single(TEST_MR_ID).build_query(); 89 | assert_eq!(query, expected_string); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/projects/all.rs: -------------------------------------------------------------------------------- 1 | //! List ALL projects 2 | //! 3 | //! https://docs.gitlab.com/ce/api/projects.html#list-all-projects 4 | //! 5 | //! # List ALL projects 6 | //! 7 | //! Get a list of all projects (admin only). 8 | //! 9 | //! ```text 10 | //! GET /projects/all 11 | //! ``` 12 | //! 13 | //! Parameters: 14 | //! 15 | //! | Attribute | Type | Required | Description | 16 | //! | --------- | ---- | -------- | ----------- | 17 | //! | `archived` | boolean | no | Limit by archived status | 18 | //! | `visibility` | string | no | Limit by visibility `public`, `internal`, or `private` | 19 | //! | `order_by` | string | no | Return projects ordered by `id`, `name`, `path`, `created_at`, `updated_at`, or `last_activity_at` fields. Default is `created_at` | 20 | //! | `sort` | string | no | Return projects sorted in `asc` or `desc` order. Default is `desc` | 21 | //! | `search` | string | no | Return list of authorized projects matching the search criteria | 22 | 23 | 24 | use serde_urlencoded; 25 | 26 | use BuildQuery; 27 | 28 | use ::errors::*; 29 | 30 | 31 | #[derive(Debug, Clone)] 32 | pub struct ProjectsLister<'a> { 33 | gl: &'a ::GitLab, 34 | internal: ::projects::AllProjectListerInternal, 35 | } 36 | 37 | impl<'a> ProjectsLister<'a> { 38 | pub fn new(gl: &'a ::GitLab) -> ProjectsLister { 39 | ProjectsLister { 40 | gl: gl, 41 | internal: ::projects::AllProjectListerInternal { 42 | archived: None, 43 | visibility: None, 44 | order_by: None, 45 | sort: None, 46 | search: None, 47 | }, 48 | } 49 | } 50 | 51 | pub fn archived(&'a mut self, archived: bool) -> &'a mut ProjectsLister { 52 | self.internal.archived = Some(archived); 53 | self 54 | } 55 | 56 | pub fn visibility(&'a mut self, visibility: ::ListingVisibility) -> &'a mut ProjectsLister { 57 | self.internal.visibility = Some(visibility); 58 | self 59 | } 60 | 61 | pub fn order_by(&'a mut self, order_by: ::projects::ListingOrderBy) -> &'a mut ProjectsLister { 62 | self.internal.order_by = Some(order_by); 63 | self 64 | } 65 | 66 | pub fn sort(&'a mut self, sort: ::ListingSort) -> &'a mut ProjectsLister { 67 | self.internal.sort = Some(sort); 68 | self 69 | } 70 | 71 | pub fn search(&'a mut self, search: String) -> &'a mut ProjectsLister { 72 | self.internal.search = Some(search); 73 | self 74 | } 75 | 76 | /// Commit the lister: Query GitLab and return a list of projects. 77 | pub fn list(&self) -> Result<::projects::Projects> { 78 | // let query = serde_urlencoded::to_string(&self); 79 | let query = self.build_query(); 80 | debug!("query: {:?}", query); 81 | 82 | self.gl.get(&query, None, None).chain_err(|| format!("cannot get query {}", query)) 83 | } 84 | } 85 | 86 | 87 | impl<'a> BuildQuery for ProjectsLister<'a> { 88 | fn build_query(&self) -> String { 89 | 90 | let encoded = serde_urlencoded::to_string(&self.internal).unwrap(); 91 | let mut query = String::from("projects/all"); 92 | if !encoded.is_empty() { 93 | query.push_str("?"); 94 | query.push_str(&encoded); 95 | } 96 | debug!("query: {}", query); 97 | 98 | query 99 | } 100 | } 101 | 102 | 103 | #[cfg(test)] 104 | mod tests { 105 | use BuildQuery; 106 | 107 | 108 | #[test] 109 | fn build_query_default() { 110 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 111 | // let gl: ::GitLab = Default::default(); 112 | 113 | let expected_string = "projects/all"; 114 | let projects_lister = gl.projects().all(); 115 | let query = projects_lister.build_query(); 116 | assert_eq!(query, expected_string); 117 | 118 | let expected_string = "projects/all"; 119 | let query = gl.projects().all().build_query(); 120 | assert_eq!(query, expected_string); 121 | } 122 | 123 | 124 | #[test] 125 | fn build_query_archived() { 126 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 127 | // let gl: ::GitLab = Default::default(); 128 | 129 | let expected_string = "projects/all?archived=true"; 130 | let query = gl.projects().all().archived(true).build_query(); 131 | assert_eq!(query, expected_string); 132 | 133 | let expected_string = "projects/all?archived=false"; 134 | let query = gl.projects().all().archived(false).build_query(); 135 | assert_eq!(query, expected_string); 136 | } 137 | 138 | 139 | #[test] 140 | fn build_query_visibility() { 141 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 142 | // let gl: ::GitLab = Default::default(); 143 | 144 | let expected_string = "projects/all?visibility=public"; 145 | let query = gl.projects().all().visibility(::ListingVisibility::Public).build_query(); 146 | assert_eq!(query, expected_string); 147 | 148 | let expected_string = "projects/all?visibility=internal"; 149 | let query = gl.projects().all().visibility(::ListingVisibility::Internal).build_query(); 150 | assert_eq!(query, expected_string); 151 | 152 | let expected_string = "projects/all?visibility=private"; 153 | let query = gl.projects().all().visibility(::ListingVisibility::Private).build_query(); 154 | assert_eq!(query, expected_string); 155 | } 156 | 157 | 158 | #[test] 159 | fn build_query_order_by() { 160 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 161 | // let gl: ::GitLab = Default::default(); 162 | 163 | let expected_string = "projects/all?order_by=id"; 164 | let query = gl.projects().all().order_by(::projects::ListingOrderBy::Id).build_query(); 165 | assert_eq!(query, expected_string); 166 | 167 | let expected_string = "projects/all?order_by=name"; 168 | let query = gl.projects().all().order_by(::projects::ListingOrderBy::Name).build_query(); 169 | assert_eq!(query, expected_string); 170 | 171 | let expected_string = "projects/all?order_by=path"; 172 | let query = gl.projects().all().order_by(::projects::ListingOrderBy::Path).build_query(); 173 | assert_eq!(query, expected_string); 174 | 175 | let expected_string = "projects/all?order_by=created_at"; 176 | let query = 177 | gl.projects().all().order_by(::projects::ListingOrderBy::CreatedAt).build_query(); 178 | assert_eq!(query, expected_string); 179 | 180 | let expected_string = "projects/all?order_by=updated_at"; 181 | let query = 182 | gl.projects().all().order_by(::projects::ListingOrderBy::UpdatedAt).build_query(); 183 | assert_eq!(query, expected_string); 184 | 185 | let expected_string = "projects/all?order_by=last_activity_at"; 186 | let query = 187 | gl.projects().all().order_by(::projects::ListingOrderBy::LastActivityAt).build_query(); 188 | assert_eq!(query, expected_string); 189 | } 190 | 191 | 192 | #[test] 193 | fn build_query_sort() { 194 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 195 | // let gl: ::GitLab = Default::default(); 196 | 197 | let expected_string = "projects/all?sort=asc"; 198 | let query = gl.projects().all().sort(::ListingSort::Asc).build_query(); 199 | assert_eq!(query, expected_string); 200 | 201 | let expected_string = "projects/all?sort=desc"; 202 | let query = gl.projects().all().sort(::ListingSort::Desc).build_query(); 203 | assert_eq!(query, expected_string); 204 | } 205 | 206 | 207 | #[test] 208 | fn build_query_search() { 209 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 210 | // let gl: ::GitLab = Default::default(); 211 | 212 | let expected_string = "projects/all?search=SearchPattern"; 213 | let query = gl.projects().all().search(String::from("SearchPattern")).build_query(); 214 | assert_eq!(query, expected_string); 215 | } 216 | 217 | 218 | #[test] 219 | fn groups_build_query_multiple() { 220 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 221 | // let gl: ::GitLab = Default::default(); 222 | 223 | let expected_string = "projects/all?archived=false&sort=asc"; 224 | let query = gl.projects().all().archived(false).sort(::ListingSort::Asc).build_query(); 225 | assert_eq!(query, expected_string); 226 | } 227 | } 228 | -------------------------------------------------------------------------------- /src/projects/id.rs: -------------------------------------------------------------------------------- 1 | //! Get project by id. 2 | //! 3 | //! https://docs.gitlab.com/ce/api/projects.html#get-single-project 4 | //! 5 | //! # Get single project 6 | //! 7 | //! Get a specific project, identified by project ID or `NAMESPACE/PROJECT_NAME`, which is owned by 8 | //! the authenticated user. 9 | //! If using namespaced projects call make sure that the `NAMESPACE/PROJECT_NAME` is URL-encoded, 10 | //! eg. `/api/v3/projects/diaspora%2Fdiaspora` (where `/` is represented by `%2F`). 11 | //! 12 | //! ```text 13 | //! GET /projects/ID 14 | //! ``` 15 | //! 16 | //! Parameters: 17 | //! 18 | //! | Attribute | Type | Required | Description | 19 | //! | --------- | ---- | -------- | ----------- | 20 | //! | `id` | integer/string | yes | The ID or `NAMESPACE/PROJECT_NAME` of the project | 21 | 22 | 23 | use BuildQuery; 24 | 25 | use ::errors::*; 26 | 27 | 28 | #[derive(Debug, Clone)] 29 | pub struct ProjectsLister<'a> { 30 | gl: &'a ::GitLab, 31 | id: ::projects::ListingId, 32 | } 33 | 34 | 35 | impl<'a> ProjectsLister<'a> { 36 | pub fn new(gl: &'a ::GitLab, id: ::projects::ListingId) -> ProjectsLister { 37 | ProjectsLister { gl: gl, id: id } 38 | } 39 | 40 | 41 | /// Commit the lister: Query GitLab and return a list of projects. 42 | pub fn list(&self) -> Result<::projects::Project> { 43 | // let query = serde_urlencoded::to_string(&self); 44 | let query = self.build_query(); 45 | debug!("query: {:?}", query); 46 | 47 | self.gl.get(&query, None, None).chain_err(|| format!("cannot get query {}", query)) 48 | } 49 | 50 | 51 | /// Return a lister for the project's issues 52 | pub fn issues(self) -> Result<::issues::project::IssuesLister<'a>> { 53 | let project = self.list().chain_err(|| "failure to find project")?; 54 | 55 | Ok(::issues::project::IssuesLister::new(self.gl, project.id)) 56 | } 57 | 58 | /// Return a lister for the project's merge requests 59 | pub fn merge_requests(self) -> Result<::merge_requests::MergeRequestsLister<'a>> { 60 | let project = self.list().chain_err(|| "failure to find project")?; 61 | 62 | Ok(::merge_requests::MergeRequestsLister::new(self.gl, project.id)) 63 | } 64 | } 65 | 66 | impl<'a> BuildQuery for ProjectsLister<'a> { 67 | fn build_query(&self) -> String { 68 | let mut query = String::from("projects/"); 69 | 70 | query.push_str(&match self.id { 71 | ::projects::ListingId::Id(id) => id.to_string(), 72 | ::projects::ListingId::NamespaceProject(ref s) => s.replace("/", "%2F"), 73 | }); 74 | 75 | query 76 | } 77 | } 78 | 79 | 80 | #[cfg(test)] 81 | mod tests { 82 | use BuildQuery; 83 | 84 | const TEST_PROJECT_ID: i64 = 123; 85 | const TEST_PROJECT_NAME: &'static str = "group/project"; 86 | 87 | 88 | #[test] 89 | fn build_query_id() { 90 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 91 | // let gl: ::GitLab = Default::default(); 92 | 93 | let expected_string = format!("projects/{}", TEST_PROJECT_ID); 94 | let query = gl.projects() 95 | .id(::projects::ListingId::Id(TEST_PROJECT_ID)) 96 | .build_query(); 97 | assert_eq!(query, expected_string); 98 | 99 | let expected_string = format!("projects/{}", 100 | TEST_PROJECT_NAME.to_string().replace("/", "%2F")); 101 | let query = gl.projects() 102 | .id(::projects::ListingId::NamespaceProject(TEST_PROJECT_NAME.to_string())) 103 | .build_query(); 104 | assert_eq!(query, expected_string); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/projects/id_branch.rs: -------------------------------------------------------------------------------- 1 | //! Single branch 2 | //! 3 | //! https://docs.gitlab.com/ce/api/projects.html#single-branch 4 | //! 5 | //! # Single branch 6 | //! 7 | //! A specific branch of a project. 8 | //! 9 | //! ```text 10 | //! GET /projects/ID/repository/branches/BRANCH 11 | //! ``` 12 | //! 13 | //! Parameters: 14 | //! 15 | //! | Attribute | Type | Required | Description | 16 | //! | --------- | ---- | -------- | ----------- | 17 | //! | `id` | integer/string | yes | The ID of the project or `NAMESPACE/PROJECT_NAME` | 18 | //! | `branch` | string | yes | The name of the branch | 19 | //! | `developers_can_push` | boolean | no | Flag if developers can push to the branch | 20 | //! | `developers_can_merge` | boolean | no | Flag if developers can merge to the branch | 21 | -------------------------------------------------------------------------------- /src/projects/id_branches.rs: -------------------------------------------------------------------------------- 1 | //! List branches 2 | //! 3 | //! https://docs.gitlab.com/ce/api/projects.html#list-branches 4 | //! 5 | //! # List branches 6 | //! 7 | //! Lists all branches of a project. 8 | //! 9 | //! ```text 10 | //! GET /projects/ID/repository/branches 11 | //! ``` 12 | //! 13 | //! Parameters: 14 | //! 15 | //! | Attribute | Type | Required | Description | 16 | //! | --------- | ---- | -------- | ----------- | 17 | //! | `id` | integer/string | yes | The ID of the project or `NAMESPACE/PROJECT_NAME` | 18 | //! 19 | //! 20 | -------------------------------------------------------------------------------- /src/projects/id_events.rs: -------------------------------------------------------------------------------- 1 | //! Get project events 2 | //! 3 | //! https://docs.gitlab.com/ce/api/projects.html#get-project-events 4 | //! 5 | //! # Get project events 6 | //! 7 | //! Get the events for the specified project. 8 | //! Sorted from newest to oldest 9 | //! 10 | //! ```text 11 | //! GET /projects/ID/events 12 | //! ``` 13 | //! 14 | //! Parameters: 15 | //! 16 | //! | Attribute | Type | Required | Description | 17 | //! | --------- | ---- | -------- | ----------- | 18 | //! | `id` | integer/string | yes | The ID or `NAMESPACE/PROJECT_NAME` of the project | 19 | //! 20 | //! 21 | -------------------------------------------------------------------------------- /src/projects/id_hooks.rs: -------------------------------------------------------------------------------- 1 | //! List project hooks 2 | //! 3 | //! https://docs.gitlab.com/ce/api/projects.html#list-project-hooks 4 | //! 5 | //! # List project hooks 6 | //! 7 | //! Get a list of project hooks. 8 | //! 9 | //! ```text 10 | //! GET /projects/ID/hooks 11 | //! ``` 12 | //! 13 | //! Parameters: 14 | //! 15 | //! | Attribute | Type | Required | Description | 16 | //! | --------- | ---- | -------- | ----------- | 17 | //! | `id` | integer/string | yes | The ID of the project or `NAMESPACE/PROJECT_NAME` | 18 | -------------------------------------------------------------------------------- /src/projects/id_hooks_id.rs: -------------------------------------------------------------------------------- 1 | //! Get project hook 2 | //! 3 | //! https://docs.gitlab.com/ce/api/projects.html#get-project-hook 4 | //! 5 | //! # Get project hook 6 | //! 7 | //! Get a specific hook for a project. 8 | //! 9 | //! ```text 10 | //! GET /projects/ID/hooks/HOOK_ID 11 | //! ``` 12 | //! 13 | //! Parameters: 14 | //! 15 | //! | Attribute | Type | Required | Description | 16 | //! | --------- | ---- | -------- | ----------- | 17 | //! | `id` | integer/string | yes | The ID of the project or `NAMESPACE/PROJECT_NAME` | 18 | //! | `hook_id` | integer | yes | The ID of a project hook | 19 | //! 20 | //! 21 | -------------------------------------------------------------------------------- /src/projects/mod.rs: -------------------------------------------------------------------------------- 1 | //! List projects 2 | //! 3 | //! https://docs.gitlab.com/ce/api/projects.html 4 | //! 5 | //! # List projects 6 | //! 7 | //! Get a list of projects for which the authenticated user is a member. 8 | //! 9 | //! ```text 10 | //! GET /projects 11 | //! ``` 12 | //! 13 | //! Parameters: 14 | //! 15 | //! | Attribute | Type | Required | Description | 16 | //! | --------- | ---- | -------- | ----------- | 17 | //! | `archived` | boolean | no | Limit by archived status | 18 | //! | `visibility` | string | no | Limit by visibility `public`, `internal`, or `private` | 19 | //! | `order_by` | string | no | Return projects ordered by `id`, `name`, `path`, `created_at`, `updated_at`, or `last_activity_at` fields. Default is `created_at` | 20 | //! | `sort` | string | no | Return projects sorted in `asc` or `desc` order. Default is `desc` | 21 | //! | `search` | string | no | Return list of authorized projects matching the search criteria | 22 | //! | `simple` | boolean | no | Return only the ID, URL, name, and path of each project | 23 | //! 24 | 25 | 26 | use serde_urlencoded; 27 | 28 | use BuildQuery; 29 | use Lister; 30 | 31 | 32 | pub mod all; 33 | pub mod id_branches; 34 | pub mod id_branch; 35 | pub mod id_events; 36 | pub mod id_hooks_id; 37 | pub mod id_hooks; 38 | pub mod id; 39 | pub mod owned; 40 | pub mod search; 41 | pub mod starred; 42 | pub mod visible; 43 | 44 | use ::errors::*; 45 | 46 | 47 | #[derive(Debug, Copy, Clone, Serialize, Deserialize)] 48 | pub enum ListingOrderBy { 49 | #[serde(rename = "id")] 50 | Id, 51 | #[serde(rename = "name")] 52 | Name, 53 | #[serde(rename = "path")] 54 | Path, 55 | #[serde(rename = "created_at")] 56 | CreatedAt, 57 | #[serde(rename = "updated_at")] 58 | UpdatedAt, 59 | #[serde(rename = "last_activity_at")] 60 | LastActivityAt, 61 | } 62 | 63 | 64 | #[derive(Debug, Copy, Clone, Serialize, Deserialize)] 65 | pub enum SearchListingOrderBy { 66 | #[serde(rename = "id")] 67 | Id, 68 | #[serde(rename = "name")] 69 | Name, 70 | // #[serde(rename = "path")] 71 | // Path, 72 | #[serde(rename = "created_at")] 73 | CreatedAt, 74 | // #[serde(rename = "updated_at")] 75 | // UpdatedAt, 76 | #[serde(rename = "last_activity_at")] 77 | LastActivityAt, 78 | } 79 | 80 | 81 | #[derive(Debug, Clone, Serialize, Deserialize)] 82 | struct ProjectListerInternal { 83 | /// Limit by archived status 84 | archived: Option, 85 | /// Limit by visibility. 86 | visibility: Option<::ListingVisibility>, 87 | /// Return requests ordered by. Default is `ListingOrderBy::CreatedAt`. 88 | order_by: Option, 89 | /// Return requests sorted. Default is `::ListingSort::Desc`. 90 | sort: Option<::ListingSort>, 91 | /// Return list of authorized projects matching the search criteria. 92 | search: Option, 93 | /// Return only the ID, URL, name, and path of each project 94 | simple: Option, 95 | } 96 | 97 | 98 | #[derive(Debug, Clone, Serialize, Deserialize)] 99 | struct OwnedProjectListerInternal { 100 | /// Limit by archived status 101 | archived: Option, 102 | /// Limit by visibility. 103 | visibility: Option<::ListingVisibility>, 104 | /// Return requests ordered by. Default is `ListingOrderBy::CreatedAt`. 105 | order_by: Option, 106 | /// Return requests sorted. Default is `::ListingSort::Desc`. 107 | sort: Option<::ListingSort>, 108 | /// Return list of authorized projects matching the search criteria. 109 | search: Option, 110 | } 111 | 112 | #[derive(Debug, Clone, Serialize, Deserialize)] 113 | struct SearchProjectListerInternal { 114 | /// Return requests ordered by. Default is `ListingOrderBy::CreatedAt`. 115 | order_by: Option, 116 | /// Return requests sorted. Default is `::ListingSort::Desc`. 117 | sort: Option<::ListingSort>, 118 | } 119 | 120 | 121 | 122 | #[derive(Debug, Clone, Serialize, Deserialize)] 123 | pub enum ListingId { 124 | Id(i64), 125 | NamespaceProject(String), 126 | } 127 | 128 | 129 | type AllProjectListerInternal = OwnedProjectListerInternal; 130 | 131 | 132 | #[derive(Debug, Serialize, Deserialize)] 133 | pub struct ProjectOwner { 134 | pub name: String, 135 | pub username: String, 136 | pub id: i64, 137 | pub state: String, 138 | pub avatar_url: String, 139 | pub web_url: String, 140 | } 141 | 142 | 143 | #[derive(Debug, Serialize, Deserialize)] 144 | pub struct ProjectNamespaceAvatar { 145 | pub url: Option, 146 | } 147 | 148 | 149 | #[derive(Default, Debug, Serialize, Deserialize)] 150 | pub struct ProjectNamespace { 151 | pub id: i64, 152 | pub name: String, 153 | pub path: String, 154 | pub owner_id: Option, // FIXME: Why would a project not have this? 155 | pub created_at: Option, // FIXME: Date instead? 156 | pub updated_at: Option, // FIXME: Date instead? 157 | pub description: Option, 158 | pub avatar: Option, 159 | pub membership_lock: Option, 160 | pub share_with_group_lock: Option, 161 | pub visibility_level: Option, 162 | pub request_access_enabled: Option, 163 | pub ldap_sync_status: Option, 164 | pub ldap_sync_error: Option, // FIXME: Is String the proper type? 165 | pub ldap_sync_last_update_at: Option, // FIXME: Is String the proper type? 166 | pub ldap_sync_last_successful_update_at: Option, // FIXME: Is String the proper type? 167 | pub ldap_sync_last_sync_at: Option, // FIXME: Is String the proper type? 168 | pub deleted_at: Option, // FIXME: Is String the proper type? 169 | pub lfs_enabled: Option, // FIXME: Is String the proper type? 170 | pub repository_size_limit: Option // FIXME: Is String the proper type? 171 | } 172 | 173 | 174 | #[derive(Debug, Serialize, Deserialize)] 175 | pub struct ProjectForkedFrom { 176 | pub id: i64, 177 | pub http_url_to_repo: String, 178 | pub web_url: String, 179 | pub name: String, 180 | pub name_with_namespace: String, 181 | pub path: String, 182 | pub path_with_namespace: String, 183 | } 184 | 185 | 186 | #[derive(Debug, Serialize, Deserialize)] 187 | pub struct ProjectAccess { 188 | pub access_level: i64, 189 | pub notification_level: i64, 190 | } 191 | 192 | #[derive(Debug, Serialize, Deserialize)] 193 | pub struct ProjectPermissions { 194 | pub project_access: Option, 195 | pub group_access: Option, 196 | } 197 | 198 | #[derive(Debug, Serialize, Deserialize)] 199 | pub struct ProjectSharedWithGroup { 200 | pub group_id: i64, 201 | pub group_name: String, 202 | pub group_access_level: i64, 203 | } 204 | 205 | 206 | // https://users.rust-lang.org/t/what-am-i-doing-wrong-go-program-is-12x-faster-than-rust/5692/13 207 | // https://doc.rust-lang.org/rustc-serialize/rustc_serialize/json/index.html 208 | #[derive(Default, Debug, Serialize, Deserialize)] 209 | pub struct Project { 210 | pub id: i64, 211 | pub description: String, 212 | pub default_branch: Option, 213 | pub tag_list: Vec, 214 | pub public: bool, 215 | pub archived: bool, 216 | pub visibility_level: i64, 217 | pub ssh_url_to_repo: String, 218 | pub http_url_to_repo: String, 219 | pub web_url: String, 220 | // owner: Option, // FIXME: Why would a project not have an owner? 221 | pub name: String, 222 | pub name_with_namespace: String, 223 | pub path: String, 224 | pub path_with_namespace: String, 225 | pub container_registry_enabled: Option, 226 | pub issues_enabled: Option, 227 | pub merge_requests_enabled: Option, 228 | pub wiki_enabled: Option, 229 | pub builds_enabled: Option, 230 | pub snippets_enabled: Option, 231 | pub created_at: String, // FIXME: Date instead? 232 | pub last_activity_at: String, // FIXME: Date instead? 233 | pub shared_runners_enabled: bool, 234 | pub lfs_enabled: bool, 235 | pub creator_id: i64, 236 | pub namespace: ProjectNamespace, 237 | pub forked_from_project: Option, 238 | pub avatar_url: Option, 239 | pub star_count: i64, 240 | pub forks_count: i64, 241 | pub open_issues_count: Option, 242 | pub runners_token: Option, 243 | pub public_builds: bool, 244 | pub shared_with_groups: Vec, 245 | pub only_allow_merge_if_build_succeeds: bool, 246 | pub request_access_enabled: bool, 247 | pub only_allow_merge_if_all_discussions_are_resolved: Option, // FIXME: Is bool the proper type? 248 | pub approvals_before_merge: Option, 249 | pub permissions: Option, 250 | } 251 | 252 | pub type Projects = Vec; 253 | 254 | 255 | #[derive(Debug, Clone)] 256 | pub struct ProjectsLister<'a> { 257 | gl: &'a ::GitLab, 258 | internal: ::projects::ProjectListerInternal, 259 | } 260 | 261 | 262 | impl<'a> Lister<::projects::Projects> for ProjectsLister<'a> { 263 | /// Commit the lister: Query GitLab and return a list of projects. 264 | fn list(&self) -> Result<::projects::Projects> { 265 | let query = self.build_query(); 266 | debug!("query: {:?}", query); 267 | 268 | self.gl.get(&query, None, None).chain_err(|| format!("cannot get query {}", query)) 269 | } 270 | 271 | /// Commit the lister: Query GitLab and return a list of issues. 272 | fn list_paginated(&self, page: u16, per_page: u16) -> Result<::projects::Projects> { 273 | let query = self.build_query(); 274 | debug!("query: {:?}", query); 275 | 276 | self.gl.get(&query, page, per_page).chain_err(|| format!("cannot get query {}", query)) 277 | } 278 | } 279 | 280 | 281 | impl<'a> ProjectsLister<'a> { 282 | pub fn new(gl: &'a ::GitLab) -> ProjectsLister { 283 | ProjectsLister { 284 | gl: gl, 285 | internal: ::projects::ProjectListerInternal { 286 | archived: None, 287 | visibility: None, 288 | order_by: None, 289 | sort: None, 290 | search: None, 291 | simple: None, 292 | }, 293 | } 294 | } 295 | 296 | pub fn all(self) -> all::ProjectsLister<'a> { 297 | // assert_eq!(self, ProjectsLister::new(self.gl)); 298 | all::ProjectsLister::new(self.gl) 299 | } 300 | 301 | pub fn owned(self) -> owned::ProjectsLister<'a> { 302 | // assert_eq!(self, ProjectsLister::new(self.gl)); 303 | owned::ProjectsLister::new(self.gl) 304 | } 305 | 306 | pub fn search(self, query: String) -> search::ProjectsLister<'a> { 307 | // assert_eq!(self, ProjectsLister::new(self.gl)); 308 | search::ProjectsLister::new(self.gl, query) 309 | } 310 | 311 | pub fn id(self, id: ListingId) -> id::ProjectsLister<'a> { 312 | // assert_eq!(self, ProjectsLister::new(self.gl)); 313 | id::ProjectsLister::new(self.gl, id) 314 | } 315 | 316 | 317 | 318 | pub fn archived(&'a mut self, archived: bool) -> &'a mut ProjectsLister { 319 | self.internal.archived = Some(archived); 320 | self 321 | } 322 | 323 | pub fn visibility(&'a mut self, visibility: ::ListingVisibility) -> &'a mut ProjectsLister { 324 | self.internal.visibility = Some(visibility); 325 | self 326 | } 327 | 328 | pub fn order_by(&'a mut self, order_by: ListingOrderBy) -> &'a mut ProjectsLister { 329 | self.internal.order_by = Some(order_by); 330 | self 331 | } 332 | 333 | pub fn sort(&'a mut self, sort: ::ListingSort) -> &'a mut ProjectsLister { 334 | self.internal.sort = Some(sort); 335 | self 336 | } 337 | 338 | pub fn search_pattern(&'a mut self, search: String) -> &'a mut ProjectsLister { 339 | self.internal.search = Some(search); 340 | self 341 | } 342 | 343 | pub fn simple(&'a mut self, simple: bool) -> &'a mut ProjectsLister { 344 | self.internal.simple = Some(simple); 345 | self 346 | } 347 | } 348 | 349 | impl<'a> BuildQuery for ProjectsLister<'a> { 350 | fn build_query(&self) -> String { 351 | 352 | let encoded = serde_urlencoded::to_string(&self.internal).unwrap(); 353 | let mut query = String::from("projects"); 354 | if !encoded.is_empty() { 355 | query.push_str("?"); 356 | query.push_str(&encoded); 357 | } 358 | 359 | query 360 | } 361 | } 362 | 363 | 364 | impl<'a> Project { 365 | /// Return a lister for the project's issues 366 | pub fn issues(&'a self, gl: &'a ::GitLab) -> ::issues::project::IssuesLister { 367 | ::issues::project::IssuesLister::new(gl, self.id) 368 | } 369 | 370 | /// Return a lister for the project's merge requests 371 | pub fn merge_requests(&'a self, gl: &'a ::GitLab) -> ::merge_requests::MergeRequestsLister { 372 | ::merge_requests::MergeRequestsLister::new(gl, self.id) 373 | } 374 | } 375 | 376 | 377 | 378 | #[cfg(test)] 379 | mod tests { 380 | use BuildQuery; 381 | 382 | 383 | #[test] 384 | fn build_query_default() { 385 | let expected_string = "projects"; 386 | 387 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 388 | // let gl: ::GitLab = Default::default(); 389 | 390 | let projects_lister = gl.projects(); 391 | let query = projects_lister.build_query(); 392 | assert_eq!(query, expected_string); 393 | 394 | let query = gl.projects().build_query(); 395 | assert_eq!(query, expected_string); 396 | } 397 | 398 | 399 | #[test] 400 | fn build_query_archived() { 401 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 402 | // let gl: ::GitLab = Default::default(); 403 | 404 | let expected_string = "projects?archived=true"; 405 | 406 | let mut projects_lister = gl.projects(); 407 | let query = projects_lister.archived(true).build_query(); 408 | assert_eq!(query, expected_string); 409 | let query = gl.projects().archived(true).build_query(); 410 | assert_eq!(query, expected_string); 411 | 412 | let expected_string = "projects?archived=false"; 413 | 414 | let mut projects_lister = gl.projects(); 415 | let query = projects_lister.archived(false).build_query(); 416 | assert_eq!(query, expected_string); 417 | let query = gl.projects().archived(false).build_query(); 418 | assert_eq!(query, expected_string); 419 | } 420 | 421 | 422 | #[test] 423 | fn build_query_visibility() { 424 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 425 | // let gl: ::GitLab = Default::default(); 426 | 427 | let expected_string = "projects?visibility=public"; 428 | let mut projects_lister = gl.projects(); 429 | let query = projects_lister.visibility(::ListingVisibility::Public).build_query(); 430 | assert_eq!(query, expected_string); 431 | let query = gl.projects().visibility(::ListingVisibility::Public).build_query(); 432 | assert_eq!(query, expected_string); 433 | 434 | let expected_string = "projects?visibility=internal"; 435 | let mut projects_lister = gl.projects(); 436 | let query = projects_lister.visibility(::ListingVisibility::Internal).build_query(); 437 | assert_eq!(query, expected_string); 438 | let query = gl.projects().visibility(::ListingVisibility::Internal).build_query(); 439 | assert_eq!(query, expected_string); 440 | 441 | let expected_string = "projects?visibility=private"; 442 | let mut projects_lister = gl.projects(); 443 | let query = projects_lister.visibility(::ListingVisibility::Private).build_query(); 444 | assert_eq!(query, expected_string); 445 | let query = gl.projects().visibility(::ListingVisibility::Private).build_query(); 446 | assert_eq!(query, expected_string); 447 | } 448 | 449 | 450 | #[test] 451 | fn build_query_order_by() { 452 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 453 | // let gl: ::GitLab = Default::default(); 454 | 455 | let expected_string = "projects?order_by=id"; 456 | let mut projects_lister = gl.projects(); 457 | let query = projects_lister.order_by(::projects::ListingOrderBy::Id).build_query(); 458 | assert_eq!(query, expected_string); 459 | let query = gl.projects().order_by(::projects::ListingOrderBy::Id).build_query(); 460 | assert_eq!(query, expected_string); 461 | 462 | let expected_string = "projects?order_by=name"; 463 | let mut projects_lister = gl.projects(); 464 | let query = projects_lister.order_by(::projects::ListingOrderBy::Name).build_query(); 465 | assert_eq!(query, expected_string); 466 | let query = gl.projects().order_by(::projects::ListingOrderBy::Name).build_query(); 467 | assert_eq!(query, expected_string); 468 | 469 | let expected_string = "projects?order_by=path"; 470 | let mut projects_lister = gl.projects(); 471 | let query = projects_lister.order_by(::projects::ListingOrderBy::Path).build_query(); 472 | assert_eq!(query, expected_string); 473 | let query = gl.projects().order_by(::projects::ListingOrderBy::Path).build_query(); 474 | assert_eq!(query, expected_string); 475 | 476 | let expected_string = "projects?order_by=created_at"; 477 | let mut projects_lister = gl.projects(); 478 | let query = projects_lister.order_by(::projects::ListingOrderBy::CreatedAt).build_query(); 479 | assert_eq!(query, expected_string); 480 | let query = gl.projects().order_by(::projects::ListingOrderBy::CreatedAt).build_query(); 481 | assert_eq!(query, expected_string); 482 | 483 | let expected_string = "projects?order_by=updated_at"; 484 | let mut projects_lister = gl.projects(); 485 | let query = projects_lister.order_by(::projects::ListingOrderBy::UpdatedAt).build_query(); 486 | assert_eq!(query, expected_string); 487 | let query = gl.projects().order_by(::projects::ListingOrderBy::UpdatedAt).build_query(); 488 | assert_eq!(query, expected_string); 489 | 490 | let expected_string = "projects?order_by=last_activity_at"; 491 | let mut projects_lister = gl.projects(); 492 | let query = projects_lister.order_by(::projects::ListingOrderBy::LastActivityAt) 493 | .build_query(); 494 | assert_eq!(query, expected_string); 495 | let query = 496 | gl.projects().order_by(::projects::ListingOrderBy::LastActivityAt).build_query(); 497 | assert_eq!(query, expected_string); 498 | } 499 | 500 | 501 | #[test] 502 | fn build_query_sort() { 503 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 504 | // let gl: ::GitLab = Default::default(); 505 | 506 | let expected_string = "projects?sort=asc"; 507 | let mut projects_lister = gl.projects(); 508 | let query = projects_lister.sort(::ListingSort::Asc).build_query(); 509 | assert_eq!(query, expected_string); 510 | let query = gl.projects().sort(::ListingSort::Asc).build_query(); 511 | assert_eq!(query, expected_string); 512 | 513 | let expected_string = "projects?sort=desc"; 514 | let mut projects_lister = gl.projects(); 515 | let query = projects_lister.sort(::ListingSort::Desc).build_query(); 516 | assert_eq!(query, expected_string); 517 | let query = gl.projects().sort(::ListingSort::Desc).build_query(); 518 | assert_eq!(query, expected_string); 519 | } 520 | 521 | 522 | #[test] 523 | fn build_query_search() { 524 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 525 | // let gl: ::GitLab = Default::default(); 526 | 527 | let expected_string = "projects?search=SearchPattern"; 528 | let mut projects_lister = gl.projects(); 529 | let query = projects_lister.search_pattern(String::from("SearchPattern")).build_query(); 530 | assert_eq!(query, expected_string); 531 | let query = gl.projects().search_pattern(String::from("SearchPattern")).build_query(); 532 | assert_eq!(query, expected_string); 533 | } 534 | 535 | 536 | #[test] 537 | fn build_query_simple() { 538 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 539 | // let gl: ::GitLab = Default::default(); 540 | 541 | let expected_string = "projects?simple=true"; 542 | let mut projects_lister = gl.projects(); 543 | let query = projects_lister.simple(true).build_query(); 544 | assert_eq!(query, expected_string); 545 | let query = gl.projects().simple(true).build_query(); 546 | assert_eq!(query, expected_string); 547 | 548 | let expected_string = "projects?simple=false"; 549 | let mut projects_lister = gl.projects(); 550 | let query = projects_lister.simple(false).build_query(); 551 | assert_eq!(query, expected_string); 552 | let query = gl.projects().simple(false).build_query(); 553 | assert_eq!(query, expected_string); 554 | } 555 | 556 | 557 | #[test] 558 | fn groups_build_query_multiple() { 559 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 560 | // let gl: ::GitLab = Default::default(); 561 | 562 | let expected_string = "projects?archived=true&simple=true"; 563 | let mut projects_lister = gl.projects(); 564 | let query = projects_lister.archived(true).simple(true).build_query(); 565 | assert_eq!(query, expected_string); 566 | let query = gl.projects().archived(true).simple(true).build_query(); 567 | assert_eq!(query, expected_string); 568 | } 569 | 570 | 571 | #[test] 572 | fn project_to_issues() { 573 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 574 | let project_id = 123; 575 | let project = ::projects::Project { id: project_id, ..Default::default() }; 576 | let issues_lister = format!("{:?}", project.issues(&gl)); 577 | let default_issues_lister = format!("{:?}", 578 | ::issues::project::IssuesLister::new(&gl, project_id)); 579 | assert_eq!(issues_lister, default_issues_lister); 580 | } 581 | 582 | 583 | #[test] 584 | fn project_to_merge_requests() { 585 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 586 | let project_id = 123; 587 | let project = ::projects::Project { id: project_id, ..Default::default() }; 588 | let merge_requests_lister = format!("{:?}", project.merge_requests(&gl)); 589 | let default_merge_requests_lister = format!("{:?}", 590 | ::merge_requests::MergeRequestsLister::new(&gl, project_id)); 591 | assert_eq!(merge_requests_lister, default_merge_requests_lister); 592 | } 593 | } 594 | -------------------------------------------------------------------------------- /src/projects/owned.rs: -------------------------------------------------------------------------------- 1 | //! List owned projects 2 | //! 3 | //! https://docs.gitlab.com/ce/api/projects.html#list-owned-projects 4 | //! 5 | //! # List owned projects 6 | //! 7 | //! Get a list of projects which are owned by the authenticated user. 8 | //! 9 | //! ```text 10 | //! GET /projects/owned 11 | //! ``` 12 | //! 13 | //! Parameters: 14 | //! 15 | //! | Attribute | Type | Required | Description | 16 | //! | --------- | ---- | -------- | ----------- | 17 | //! | `archived` | boolean | no | Limit by archived status | 18 | //! | `visibility` | string | no | Limit by visibility `public`, `internal`, or `private` | 19 | //! | `order_by` | string | no | Return projects ordered by `id`, `name`, `path`, `created_at`, `updated_at`, or `last_activity_at` fields. Default is `created_at` | 20 | //! | `sort` | string | no | Return projects sorted in `asc` or `desc` order. Default is `desc` | 21 | //! | `search` | string | no | Return list of authorized projects matching the search criteria | 22 | 23 | 24 | use serde_urlencoded; 25 | 26 | use BuildQuery; 27 | 28 | use ::errors::*; 29 | 30 | 31 | #[derive(Debug, Clone)] 32 | pub struct ProjectsLister<'a> { 33 | gl: &'a ::GitLab, 34 | internal: ::projects::OwnedProjectListerInternal, 35 | } 36 | 37 | impl<'a> ProjectsLister<'a> { 38 | pub fn new(gl: &'a ::GitLab) -> ProjectsLister { 39 | ProjectsLister { 40 | gl: gl, 41 | internal: ::projects::OwnedProjectListerInternal { 42 | archived: None, 43 | visibility: None, 44 | order_by: None, 45 | sort: None, 46 | search: None, 47 | }, 48 | } 49 | } 50 | 51 | pub fn archived(&'a mut self, archived: bool) -> &'a mut ProjectsLister { 52 | self.internal.archived = Some(archived); 53 | self 54 | } 55 | 56 | pub fn visibility(&'a mut self, visibility: ::ListingVisibility) -> &'a mut ProjectsLister { 57 | self.internal.visibility = Some(visibility); 58 | self 59 | } 60 | 61 | pub fn order_by(&'a mut self, order_by: ::projects::ListingOrderBy) -> &'a mut ProjectsLister { 62 | self.internal.order_by = Some(order_by); 63 | self 64 | } 65 | 66 | pub fn sort(&'a mut self, sort: ::ListingSort) -> &'a mut ProjectsLister { 67 | self.internal.sort = Some(sort); 68 | self 69 | } 70 | 71 | pub fn search(&'a mut self, search: String) -> &'a mut ProjectsLister { 72 | self.internal.search = Some(search); 73 | self 74 | } 75 | 76 | /// Commit the lister: Query GitLab and return a list of projects. 77 | pub fn list(&self) -> Result<::projects::Projects> { 78 | // let query = serde_urlencoded::to_string(&self); 79 | let query = self.build_query(); 80 | debug!("query: {:?}", query); 81 | 82 | self.gl.get(&query, None, None).chain_err(|| format!("cannot get query {}", query)) 83 | } 84 | } 85 | 86 | impl<'a> BuildQuery for ProjectsLister<'a> { 87 | fn build_query(&self) -> String { 88 | 89 | let encoded = serde_urlencoded::to_string(&self.internal).unwrap(); 90 | let mut query = String::from("projects/owned"); 91 | if !encoded.is_empty() { 92 | query.push_str("?"); 93 | query.push_str(&encoded); 94 | } 95 | debug!("query: {}", query); 96 | 97 | query 98 | } 99 | } 100 | 101 | 102 | #[cfg(test)] 103 | mod tests { 104 | use BuildQuery; 105 | 106 | 107 | #[test] 108 | fn build_query_default() { 109 | let expected_string = "projects/owned"; 110 | 111 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 112 | // let gl: ::GitLab = Default::default(); 113 | 114 | let projects_lister = gl.projects().owned(); 115 | let query = projects_lister.build_query(); 116 | assert_eq!(query, expected_string); 117 | 118 | let query = gl.projects().owned().build_query(); 119 | assert_eq!(query, expected_string); 120 | } 121 | 122 | 123 | #[test] 124 | fn build_query_archived() { 125 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 126 | // let gl: ::GitLab = Default::default(); 127 | 128 | let expected_string = "projects/owned?archived=true"; 129 | 130 | let mut projects_lister = gl.projects().owned(); 131 | let query = projects_lister.archived(true).build_query(); 132 | assert_eq!(query, expected_string); 133 | let query = gl.projects().owned().archived(true).build_query(); 134 | assert_eq!(query, expected_string); 135 | 136 | let expected_string = "projects/owned?archived=false"; 137 | 138 | let mut projects_lister = gl.projects().owned(); 139 | let query = projects_lister.archived(false).build_query(); 140 | assert_eq!(query, expected_string); 141 | let query = gl.projects().owned().archived(false).build_query(); 142 | assert_eq!(query, expected_string); 143 | } 144 | 145 | 146 | #[test] 147 | fn build_query_visibility() { 148 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 149 | // let gl: ::GitLab = Default::default(); 150 | 151 | let expected_string = "projects/owned?visibility=public"; 152 | let mut projects_lister = gl.projects().owned(); 153 | let query = projects_lister.visibility(::ListingVisibility::Public).build_query(); 154 | assert_eq!(query, expected_string); 155 | let query = gl.projects().owned().visibility(::ListingVisibility::Public).build_query(); 156 | assert_eq!(query, expected_string); 157 | 158 | let expected_string = "projects/owned?visibility=internal"; 159 | let mut projects_lister = gl.projects().owned(); 160 | let query = projects_lister.visibility(::ListingVisibility::Internal).build_query(); 161 | assert_eq!(query, expected_string); 162 | let query = gl.projects().owned().visibility(::ListingVisibility::Internal).build_query(); 163 | assert_eq!(query, expected_string); 164 | 165 | let expected_string = "projects/owned?visibility=private"; 166 | let mut projects_lister = gl.projects().owned(); 167 | let query = projects_lister.visibility(::ListingVisibility::Private).build_query(); 168 | assert_eq!(query, expected_string); 169 | let query = gl.projects().owned().visibility(::ListingVisibility::Private).build_query(); 170 | assert_eq!(query, expected_string); 171 | } 172 | 173 | 174 | #[test] 175 | fn build_query_order_by() { 176 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 177 | // let gl: ::GitLab = Default::default(); 178 | 179 | let expected_string = "projects/owned?order_by=id"; 180 | let mut projects_lister = gl.projects().owned(); 181 | let query = projects_lister.order_by(::projects::ListingOrderBy::Id).build_query(); 182 | assert_eq!(query, expected_string); 183 | let query = gl.projects().owned().order_by(::projects::ListingOrderBy::Id).build_query(); 184 | assert_eq!(query, expected_string); 185 | 186 | let expected_string = "projects/owned?order_by=name"; 187 | let mut projects_lister = gl.projects().owned(); 188 | let query = projects_lister.order_by(::projects::ListingOrderBy::Name).build_query(); 189 | assert_eq!(query, expected_string); 190 | let query = gl.projects().owned().order_by(::projects::ListingOrderBy::Name).build_query(); 191 | assert_eq!(query, expected_string); 192 | 193 | let expected_string = "projects/owned?order_by=path"; 194 | let mut projects_lister = gl.projects().owned(); 195 | let query = projects_lister.order_by(::projects::ListingOrderBy::Path).build_query(); 196 | assert_eq!(query, expected_string); 197 | let query = gl.projects().owned().order_by(::projects::ListingOrderBy::Path).build_query(); 198 | assert_eq!(query, expected_string); 199 | 200 | let expected_string = "projects/owned?order_by=created_at"; 201 | let mut projects_lister = gl.projects().owned(); 202 | let query = projects_lister.order_by(::projects::ListingOrderBy::CreatedAt).build_query(); 203 | assert_eq!(query, expected_string); 204 | let query = 205 | gl.projects().owned().order_by(::projects::ListingOrderBy::CreatedAt).build_query(); 206 | assert_eq!(query, expected_string); 207 | 208 | let expected_string = "projects/owned?order_by=updated_at"; 209 | let mut projects_lister = gl.projects().owned(); 210 | let query = projects_lister.order_by(::projects::ListingOrderBy::UpdatedAt).build_query(); 211 | assert_eq!(query, expected_string); 212 | let query = 213 | gl.projects().owned().order_by(::projects::ListingOrderBy::UpdatedAt).build_query(); 214 | assert_eq!(query, expected_string); 215 | 216 | let expected_string = "projects/owned?order_by=last_activity_at"; 217 | let mut projects_lister = gl.projects().owned(); 218 | let query = projects_lister.order_by(::projects::ListingOrderBy::LastActivityAt) 219 | .build_query(); 220 | assert_eq!(query, expected_string); 221 | let query = gl.projects() 222 | .owned() 223 | .order_by(::projects::ListingOrderBy::LastActivityAt) 224 | .build_query(); 225 | assert_eq!(query, expected_string); 226 | } 227 | 228 | 229 | #[test] 230 | fn build_query_sort() { 231 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 232 | // let gl: ::GitLab = Default::default(); 233 | 234 | let expected_string = "projects/owned?sort=asc"; 235 | let mut projects_lister = gl.projects().owned(); 236 | let query = projects_lister.sort(::ListingSort::Asc).build_query(); 237 | assert_eq!(query, expected_string); 238 | let query = gl.projects().owned().sort(::ListingSort::Asc).build_query(); 239 | assert_eq!(query, expected_string); 240 | 241 | let expected_string = "projects/owned?sort=desc"; 242 | let mut projects_lister = gl.projects().owned(); 243 | let query = projects_lister.sort(::ListingSort::Desc).build_query(); 244 | assert_eq!(query, expected_string); 245 | let query = gl.projects().owned().sort(::ListingSort::Desc).build_query(); 246 | assert_eq!(query, expected_string); 247 | } 248 | 249 | 250 | #[test] 251 | fn build_query_search() { 252 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 253 | // let gl: ::GitLab = Default::default(); 254 | 255 | let expected_string = "projects/owned?search=SearchPattern"; 256 | let mut projects_lister = gl.projects().owned(); 257 | let query = projects_lister.search(String::from("SearchPattern")).build_query(); 258 | assert_eq!(query, expected_string); 259 | let query = gl.projects().owned().search(String::from("SearchPattern")).build_query(); 260 | assert_eq!(query, expected_string); 261 | } 262 | 263 | 264 | #[test] 265 | fn groups_build_query_multiple() { 266 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 267 | // let gl: ::GitLab = Default::default(); 268 | 269 | let expected_string = "projects/owned?archived=true&sort=desc"; 270 | let mut projects_lister = gl.projects().owned(); 271 | let query = projects_lister.archived(true).sort(::ListingSort::Desc).build_query(); 272 | assert_eq!(query, expected_string); 273 | let query = gl.projects().owned().archived(true).sort(::ListingSort::Desc).build_query(); 274 | assert_eq!(query, expected_string); 275 | } 276 | } 277 | -------------------------------------------------------------------------------- /src/projects/search.rs: -------------------------------------------------------------------------------- 1 | //! Search for projects by name 2 | //! 3 | //! https://docs.gitlab.com/ce/api/projects.html#search-for-projects-by-name 4 | //! 5 | //! # Search for projects by name 6 | //! 7 | //! Search for projects by name which are accessible to the authenticated user. 8 | //! 9 | //! ```text 10 | //! GET /projects/search/QUERY 11 | //! ``` 12 | //! 13 | //! Parameters: 14 | //! 15 | //! | Attribute | Type | Required | Description | 16 | //! | --------- | ---- | -------- | ----------- | 17 | //! | `query` | string | yes | A string contained in the project name | 18 | //! | `order_by` | string | no | Return requests ordered by `id`, `name`, `created_at` or `last_activity_at` fields | 19 | //! | `sort` | string | no | Return requests sorted in `asc` or `desc` order | 20 | 21 | 22 | use serde_urlencoded; 23 | 24 | use BuildQuery; 25 | use Lister; 26 | 27 | use ::errors::*; 28 | 29 | 30 | #[derive(Debug, Clone)] 31 | pub struct ProjectsLister<'a> { 32 | gl: &'a ::GitLab, 33 | /// A string contained in the project name. 34 | query: String, 35 | internal: ::projects::SearchProjectListerInternal, 36 | } 37 | 38 | 39 | impl<'a> Lister<::projects::Projects> for ProjectsLister<'a> { 40 | /// Commit the lister: Query GitLab and return a list of projects. 41 | fn list(&self) -> Result<::projects::Projects> { 42 | let query = self.build_query(); 43 | debug!("query: {:?}", query); 44 | 45 | self.gl.get(&query, None, None).chain_err(|| format!("cannot get query {}", query)) 46 | } 47 | 48 | /// Commit the lister: Query GitLab and return a list of issues. 49 | fn list_paginated(&self, page: u16, per_page: u16) -> Result<::projects::Projects> { 50 | let query = self.build_query(); 51 | debug!("query: {:?}", query); 52 | 53 | self.gl.get(&query, page, per_page).chain_err(|| format!("cannot get query {}", query)) 54 | } 55 | } 56 | 57 | 58 | impl<'a> ProjectsLister<'a> { 59 | pub fn new(gl: &'a ::GitLab, query: String) -> ProjectsLister { 60 | ProjectsLister { 61 | gl: gl, 62 | query: query, 63 | internal: ::projects::SearchProjectListerInternal { 64 | order_by: None, 65 | sort: None, 66 | }, 67 | } 68 | } 69 | 70 | pub fn order_by(&'a mut self, order_by: ::projects::ListingOrderBy) -> &'a mut ProjectsLister { 71 | self.internal.order_by = Some(order_by); 72 | self 73 | } 74 | 75 | pub fn sort(&'a mut self, sort: ::ListingSort) -> &'a mut ProjectsLister { 76 | self.internal.sort = Some(sort); 77 | self 78 | } 79 | } 80 | 81 | impl<'a> BuildQuery for ProjectsLister<'a> { 82 | fn build_query(&self) -> String { 83 | 84 | let encoded = serde_urlencoded::to_string(&self.internal).unwrap(); 85 | let mut query = format!("projects/search/{}", self.query); 86 | if !encoded.is_empty() { 87 | query.push_str("?"); 88 | query.push_str(&encoded); 89 | } 90 | debug!("query: {}", query); 91 | 92 | query 93 | } 94 | } 95 | 96 | 97 | #[cfg(test)] 98 | mod tests { 99 | use BuildQuery; 100 | 101 | const TEST_SEARCH_QUERY: &'static str = "SearchPattern"; 102 | 103 | 104 | #[test] 105 | fn build_query_default() { 106 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 107 | // let gl: ::GitLab = Default::default(); 108 | 109 | let expected_string = format!("projects/search/{}", TEST_SEARCH_QUERY); 110 | 111 | let lister = gl.projects().search(TEST_SEARCH_QUERY.to_string()); 112 | let query = lister.build_query(); 113 | assert_eq!(query, expected_string); 114 | } 115 | 116 | 117 | #[test] 118 | fn build_query_order_by() { 119 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 120 | // let gl: ::GitLab = Default::default(); 121 | 122 | let expected_string = format!("projects/search/{}?order_by=id", TEST_SEARCH_QUERY); 123 | let query = gl.projects() 124 | .search(TEST_SEARCH_QUERY.to_string()) 125 | .order_by(::projects::ListingOrderBy::Id) 126 | .build_query(); 127 | assert_eq!(query, expected_string); 128 | 129 | let expected_string = format!("projects/search/{}?order_by=name", TEST_SEARCH_QUERY); 130 | let query = gl.projects() 131 | .search(TEST_SEARCH_QUERY.to_string()) 132 | .order_by(::projects::ListingOrderBy::Name) 133 | .build_query(); 134 | assert_eq!(query, expected_string); 135 | 136 | let expected_string = format!("projects/search/{}?order_by=created_at", TEST_SEARCH_QUERY); 137 | let query = gl.projects() 138 | .search(TEST_SEARCH_QUERY.to_string()) 139 | .order_by(::projects::ListingOrderBy::CreatedAt) 140 | .build_query(); 141 | assert_eq!(query, expected_string); 142 | 143 | let expected_string = format!("projects/search/{}?order_by=last_activity_at", 144 | TEST_SEARCH_QUERY); 145 | let query = gl.projects() 146 | .search(TEST_SEARCH_QUERY.to_string()) 147 | .order_by(::projects::ListingOrderBy::LastActivityAt) 148 | .build_query(); 149 | assert_eq!(query, expected_string); 150 | } 151 | 152 | 153 | #[test] 154 | fn build_query_sort() { 155 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 156 | // let gl: ::GitLab = Default::default(); 157 | 158 | let expected_string = format!("projects/search/{}?sort=asc", TEST_SEARCH_QUERY); 159 | let query = gl.projects() 160 | .search(TEST_SEARCH_QUERY.to_string()) 161 | .sort(::ListingSort::Asc) 162 | .build_query(); 163 | assert_eq!(query, expected_string); 164 | 165 | let expected_string = format!("projects/search/{}?sort=desc", TEST_SEARCH_QUERY); 166 | let query = gl.projects() 167 | .search(TEST_SEARCH_QUERY.to_string()) 168 | .sort(::ListingSort::Desc) 169 | .build_query(); 170 | assert_eq!(query, expected_string); 171 | } 172 | 173 | 174 | #[test] 175 | fn groups_build_query_multiple() { 176 | let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX").unwrap(); 177 | // let gl: ::GitLab = Default::default(); 178 | 179 | let expected_string = format!("projects/search/{}?order_by=created_at&sort=desc", 180 | TEST_SEARCH_QUERY); 181 | let query = gl.projects() 182 | .search(TEST_SEARCH_QUERY.to_string()) 183 | .order_by(::projects::ListingOrderBy::CreatedAt) 184 | .sort(::ListingSort::Desc) 185 | .build_query(); 186 | assert_eq!(query, expected_string); 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /src/projects/starred.rs: -------------------------------------------------------------------------------- 1 | //! List starred projects 2 | //! 3 | //! https://docs.gitlab.com/ce/api/projects.html#list-starred-projects 4 | //! 5 | //! # List starred projects 6 | //! 7 | //! Get a list of projects which are starred by the authenticated user. 8 | //! 9 | //! ```text 10 | //! GET /projects/starred 11 | //! ``` 12 | //! 13 | //! Parameters: 14 | //! 15 | //! | Attribute | Type | Required | Description | 16 | //! | --------- | ---- | -------- | ----------- | 17 | //! | `archived` | boolean | no | Limit by archived status | 18 | //! | `visibility` | string | no | Limit by visibility `public`, `internal`, or `private` | 19 | //! | `order_by` | string | no | Return projects ordered by `id`, `name`, `path`, `created_at`, `updated_at`, or `last_activity_at` fields. Default is `created_at` | 20 | //! | `sort` | string | no | Return projects sorted in `asc` or `desc` order. Default is `desc` | 21 | //! | `search` | string | no | Return list of authorized projects matching the search criteria | 22 | -------------------------------------------------------------------------------- /src/projects/visible.rs: -------------------------------------------------------------------------------- 1 | //! Get a list of projects which the authenticated user can see. 2 | //! 3 | //! https://docs.gitlab.com/ce/api/projects.html#list-projects 4 | //! 5 | //! # Get a list of projects which the authenticated user can see. 6 | //! 7 | //! ```text 8 | //! GET /projects/visible 9 | //! ``` 10 | //! 11 | //! Parameters: 12 | //! 13 | //! | Attribute | Type | Required | Description | 14 | //! | --------- | ---- | -------- | ----------- | 15 | //! | `archived` | boolean | no | Limit by archived status | 16 | //! | `visibility` | string | no | Limit by visibility `public`, `internal`, or `private` | 17 | //! | `order_by` | string | no | Return projects ordered by `id`, `name`, `path`, `created_at`, `updated_at`, or `last_activity_at` fields. Default is `created_at` | 18 | //! | `sort` | string | no | Return projects sorted in `asc` or `desc` order. Default is `desc` | 19 | //! | `search` | string | no | Return list of authorized projects matching the search criteria | 20 | //! | `simple` | boolean | no | Return only the ID, URL, name, and path of each project | 21 | //! 22 | //! 23 | -------------------------------------------------------------------------------- /src/serde_types.in.rs: -------------------------------------------------------------------------------- 1 | 2 | // https://users.rust-lang.org/t/what-am-i-doing-wrong-go-program-is-12x-faster-than-rust/5692/13 3 | // https://doc.rust-lang.org/rustc-serialize/rustc_serialize/json/index.html 4 | // https://serde.rs/codegen-hybrid.html 5 | 6 | 7 | // FIXME: Use https://github.com/nox/serde_urlencoded 8 | // FIXME: Use a type for sha1 9 | // FIXME: Use chrono crate for dates 10 | // FIXME: Use unsigned integers where it makes sense (id, iid, etc.) 11 | // FIXME: Verify all `match` in push_str() in build_query(): They should contain all members. 12 | // FIXME: Get rid of build_query(), use serde's Serialize instead. 13 | -------------------------------------------------------------------------------- /tools/create_doc.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import os 4 | import re 5 | import sys 6 | import urllib.request 7 | 8 | 9 | # api_filename = "projects.md" 10 | api_filename = "groups.md" 11 | url = "https://gitlab.com/gitlab-org/gitlab-ce/raw/master/doc/api/" + api_filename 12 | 13 | 14 | doc_dir = "doc_tmp" 15 | if not os.path.exists(doc_dir): 16 | os.makedirs(doc_dir) 17 | 18 | 19 | filename, headers = urllib.request.urlretrieve(url) 20 | 21 | with open(filename, 'r') as f: 22 | markdown = f.read() 23 | # print("markdown:", markdown) 24 | urllib.request.urlcleanup() 25 | 26 | # Strip out all `json` code blocks included in the file. 27 | p = re.compile("```json.*?```", re.MULTILINE | re.DOTALL) 28 | markdown_wo_json = re.sub(p, "", markdown) 29 | 30 | GET_block = "GET /" 31 | 32 | p_GET_block = re.compile("```\n(%s.*?)\n```" % GET_block, re.MULTILINE | re.DOTALL) 33 | p_GET_variable = re.compile("(:[^/]*)") 34 | 35 | 36 | sectionsList = re.sub("[^#]#", "TOSPLIT#", markdown_wo_json).split("TOSPLIT") 37 | 38 | for section in sectionsList: 39 | if GET_block in section: 40 | lines = section.splitlines() 41 | title = lines[0].replace("#", "").strip() 42 | # print("title:", title) 43 | 44 | # section = re.sub(p_GET_block, "```\n```") 45 | m = p_GET_block.search(section) 46 | GET_command = m.group(1) 47 | GET_variables = p_GET_variable.findall(GET_command) 48 | # Sort the variables in decreasing order of _length_. The reason is that a replace of a shorter 49 | # variable might catch a longer one and corrupt the final result. 50 | GET_variables.sort(key = lambda s: -len(s)) 51 | 52 | # Replace occurrences of the found variables with upper case, removing the ":" 53 | new_GET_command = GET_command 54 | for GET_variable in GET_variables: 55 | new_GET_command = new_GET_command.replace(GET_variable, GET_variable.replace(":", "").upper()) 56 | 57 | # section = section.replace(GET_command, new_GET_command) 58 | lines = [line.replace(GET_command, new_GET_command) for line in lines] 59 | 60 | # print("title:", title) 61 | filename = api_filename.replace(".md", "") + "-GET-" + title.replace(" ", "-").lower() + ".md" 62 | print("filename:", filename) 63 | full_filename = os.path.join(doc_dir, filename) 64 | with open(full_filename, "w") as f: 65 | f.write("//! %s\n" % title) 66 | f.write("//!\n") 67 | f.write("//! # %s\n" % title) 68 | for line in lines[1:]: 69 | f.write("//! %s\n" % line) 70 | --------------------------------------------------------------------------------