├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── README.md
├── pom.xml
└── src
├── lombok.config
├── main
└── java
│ └── com
│ └── codepine
│ └── api
│ └── testrail
│ ├── Request.java
│ ├── TestRail.java
│ ├── TestRailConfig.java
│ ├── TestRailException.java
│ ├── internal
│ ├── BooleanToIntSerializer.java
│ ├── CaseModule.java
│ ├── CsvToListDeserializer.java
│ ├── FieldModule.java
│ ├── IntToBooleanDeserializer.java
│ ├── ListToCsvSerializer.java
│ ├── PageDeserializer.java
│ ├── PlanModule.java
│ ├── QueryParameterString.java
│ ├── ResultModule.java
│ ├── StringToMapDeserializer.java
│ ├── UnixTimestampModule.java
│ └── UrlConnectionFactory.java
│ └── model
│ ├── Case.java
│ ├── CaseField.java
│ ├── CaseType.java
│ ├── Configuration.java
│ ├── Field.java
│ ├── Links.java
│ ├── Milestone.java
│ ├── Page.java
│ ├── Plan.java
│ ├── Priority.java
│ ├── Project.java
│ ├── Result.java
│ ├── ResultField.java
│ ├── Run.java
│ ├── Section.java
│ ├── Status.java
│ ├── Suite.java
│ ├── Test.java
│ └── User.java
└── test
├── java
└── com
│ └── codepine
│ └── api
│ └── testrail
│ ├── RequestTest.java
│ └── internal
│ ├── BooleanToIntSerializerTest.java
│ ├── CaseModuleTest.java
│ ├── CsvToListDeserializerTest.java
│ ├── FieldModuleTest.java
│ ├── IntToBooleanDeserializerTest.java
│ ├── ListToCsvSerializerTest.java
│ ├── PlanModuleTest.java
│ ├── ResultModuleTest.java
│ ├── StringToMapDeserializerTest.java
│ └── UnixTimestampModuleTest.java
└── resources
├── add_model.json
├── auth_error.json
├── case_with_no_custom_fields.json
├── case_with_step_field_set.json
├── get_cases.json
├── get_model.json
├── get_model_error.json
├── get_models.json
├── get_modelsA.json
├── get_modelsB.json
├── get_modelsC.json
├── log4j.properties
├── plan_with_entries.json
├── plan_with_no_entries.json
├── result_with_no_custom_fields.json
├── result_with_step_result_field_set.json
├── step_field.json
├── step_result_field.json
└── update_model.json
/.gitignore:
--------------------------------------------------------------------------------
1 | .classpath
2 | .project
3 | *.log
4 | *.apk
5 | *.zip
6 | .settings/*
7 | delombok/*
8 | target/*
9 | test-output/*
10 | reports/*
11 | /reports
12 | /.metadata/
13 | /.classpath
14 | /.project
15 | .idea
16 | *.iml
17 | /target/
18 | /bin/
19 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 |
3 | ## [v2.0.2](https://github.com/codepine/testrail-api-java-client/tree/v2.0.2) (2021-10-07)
4 |
5 | [Full Changelog](https://github.com/codepine/testrail-api-java-client/compare/v2.0.1...v2.0.2)
6 |
7 | **Close issues:**
8 |
9 | - Fix for pagination [\#68](https://github.com/codepine/testrail-api-java-client/pull/68) ([maksimsarychau](https://github.com/maksimsarychau))
10 | - Add entry's description in plans. [\#42](https://github.com/codepine/testrail-api-java-client/pull/42) ([ChenChiaHung](https://github.com/ChenChiaHung))
11 |
12 | ## [v2.0.1](https://github.com/codepine/testrail-api-java-client/tree/v2.0.1) (2018-12-02)
13 |
14 | [Full Changelog](https://github.com/codepine/testrail-api-java-client/compare/v2.0.0...v2.0.1)
15 |
16 | **Closed issues:**
17 |
18 | - Unable to delete Entry from testplan using EntryId [\#29](https://github.com/codepine/testrail-api-java-client/issues/29)
19 | - Cannot deserialize instance of java.util.ArrayList out of VALUE_STRING token for "config" property [\#15](https://github.com/codepine/testrail-api-java-client/issues/15)
20 |
21 | ## [v2.0.0](https://github.com/codepine/testrail-api-java-client/tree/v2.0.0) (2017-08-19)
22 |
23 | [Full Changelog](https://github.com/codepine/testrail-api-java-client/compare/v1.0.2...v2.0.0)
24 |
25 | **Closed issues:**
26 |
27 | - Upgrade to JDK8 [\#23](https://github.com/codepine/testrail-api-java-client/issues/23)
28 | - Upgrade Guava version from 16.x to 21.x ([apolaskey](https://github.com/apolaskey))
29 | * [\#21](https://github.com/codepine/testrail-api-java-client/issues/21)
30 | * [\#20](https://github.com/codepine/testrail-api-java-client/issues/20)
31 | * [\#18](https://github.com/codepine/testrail-api-java-client/issues/18)
32 | * [\#14](https://github.com/codepine/testrail-api-java-client/issues/14)
33 | - Add section description [\#16](https://github.com/codepine/testrail-api-java-client/pull/16) ([sofiaguyang](https://github.com/sofiaguyang))
34 |
35 | ## [v1.0.2](https://github.com/codepine/testrail-api-java-client/tree/v1.0.2) (2016-12-17)
36 | [Full Changelog](https://github.com/codepine/testrail-api-java-client/compare/v1.0.1...v1.0.2)
37 |
38 | **Closed issues:**
39 |
40 | - 411 [\#11](https://github.com/codepine/testrail-api-java-client/issues/11)
41 |
42 | ## [v1.0.1](https://github.com/codepine/testrail-api-java-client/tree/v1.0.1) (2016-02-27)
43 | [Full Changelog](https://github.com/codepine/testrail-api-java-client/compare/v1.0.0...v1.0.1)
44 |
45 | **Closed issues:**
46 |
47 | - Unable to query for cases with TestRail 5.2 or later. [\#8](https://github.com/codepine/testrail-api-java-client/issues/8)
48 |
49 | ## [v1.0.0](https://github.com/codepine/testrail-api-java-client/tree/v1.0.0) (2015-03-24)
50 |
51 |
52 | \* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)*
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 codepine
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 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # TestRail API Java Client
2 | --------------------------
3 |
4 | A Java client library for [TestRail API](http://docs.gurock.com/testrail-api2/start).
5 |
6 | [](https://maven-badges.herokuapp.com/maven-central/com.codepine.api/testrail-api-java-client)
7 |
8 | ## Quick Start
9 | --------------
10 |
11 | ### Maven Dependency
12 | ```xml
13 |
14 | com.codepine.api
15 | testrail-api-java-client
16 | ${stable.version.shown.above}
17 |
18 | ```
19 |
20 | ### Example Usage
21 | ```java
22 | // create a TestRail instance
23 | TestRail testRail = TestRail.builder("https://some.testrail.net/", "username", "password").applicationName("playground").build();
24 |
25 | // create a new project
26 | Project project = testRail.projects().add(new Project().setName("Playground Project")).execute();
27 |
28 | // add a new test suite
29 | Suite suite = testRail.suites().add(project.getId(), new Suite().setName("Functional Tests")).execute();
30 |
31 | // add a new section
32 | Section section = testRail.sections().add(project.getId(), new Section().setSuiteId(suite.getId()).setName("Boundary Cases")).execute();
33 |
34 | // add a test case
35 | List customCaseFields = testRail.caseFields().list().execute();
36 | Case testCase = testRail.cases().add(section.getId(), new Case().setTitle("Be able to play in playground"), customCaseFields).execute();
37 |
38 | // add a new test run
39 | Run run = testRail.runs().add(project.getId(), new Run().setSuiteId(suite.getId()).setName("Weekly Regression")).execute();
40 |
41 | // add test result
42 | List customResultFields = testRail.resultFields().list().execute();
43 | testRail.results().addForCase(run.getId(), testCase.getId(), new Result().setStatusId(1), customResultFields).execute();
44 |
45 | // close the run
46 | testRail.runs().close(run.getId()).execute();
47 |
48 | // complete the project - supports partial updates
49 | testRail.projects().update(project.setCompleted(true)).execute();
50 | ```
51 |
52 | ## Supported TestRail Version
53 | -----------------------------
54 | 
55 | [](http://docs.gurock.com/testrail-api2/start)
56 |
57 | [Old API (aka Mini API)](http://docs.gurock.com/testrail-api/start) is not supported. Please note that you may not be able to use some API features supported by this library depending on the TestRail version you use. Similarly, since this is not an official library, API updates in future versions of TestRail may not be supported immediately with the release of new version or may need an incompatible major version change.
58 |
59 | ## Notables
60 | ------------
61 |
62 | ### Thin Client Library
63 | Except the initial configration (refer to [example](#example-usage)), this client library does not maintain any state from your TestRail service. You can maintain/cache state on your end if you like.
64 |
65 | ### Custom Case And Result Fields
66 | TestRail supports adding custom case and result fields. The request interfaces in ```TestRail.Cases``` and ```TestRail.Results``` requires a list of these fields in order to allow this library to map them to the correct Java types. Here's an example where we want to to know the separated test steps of a particular test case:
67 | ```java
68 | // fetch list of custom case field configured in TestRail
69 | List customCaseFields = testRail.caseFields().list().execute();
70 |
71 | // get test case
72 | Case testCase = testRail.cases().get(1, customCaseFields).execute();
73 |
74 | // assuming separated_steps is a custom TestRail Steps type case field
75 | List customSteps = testCase.getCustomField("separated_steps");
76 |
77 | // work with typed customSteps
78 | ......
79 | ```
80 | Find the map of supported TestRail field types to Java types in the javadoc of ```Field.Type``` enum.
81 | As mentioned [above](#thin-client-library), since this is a thin library, it does not store the list of fields. You can cache them on your end if you like.
82 |
83 | ## License
84 | ----------
85 | This project is licensed under [MIT license](http://opensource.org/licenses/MIT).
86 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 | com.codepine.api
5 | testrail-api-java-client
6 | 2.0.3-SNAPSHOT
7 | jar
8 |
9 | TestRail API Java Client
10 | Java client for TestRail API
11 | https://github.com/codepine/testrail-api-java-client
12 |
13 |
14 |
15 | MIT License
16 | http://www.opensource.org/licenses/mit-license.php
17 | repo
18 |
19 |
20 |
21 |
22 | scm:git:git@github.com:codepine/testrail-api-java-client.git
23 | scm:git:git@github.com:codepine/testrail-api-java-client.git
24 | scm:git:git@github.com:codepine/testrail-api-java-client.git
25 | HEAD
26 |
27 |
28 |
29 |
30 | Kunal Shah
31 | kunal546@gmail.com
32 | codepine.com
33 | https://github.com/codepine
34 |
35 |
36 |
37 |
38 |
39 | ossrh
40 | https://oss.sonatype.org/content/repositories/snapshots
41 |
42 |
43 |
44 |
45 |
46 | 1.8
47 | 1.16.2
48 | 2.3.1
49 | 21.0
50 | 1.2.17
51 | 4.11
52 | 1.9.5
53 |
54 |
55 | 1.16.2.0
56 | 3.1
57 | 2.4
58 | 0.7.3.201502191951
59 | 3.0.0-M1
60 | 1.6
61 | 2.5.1
62 | 1.6.5
63 |
64 |
65 | target/generated-sources/delombok
66 | target/generated-test-sources/delombok
67 |
68 |
69 |
70 |
71 | org.projectlombok
72 | lombok
73 | ${lombok.version}
74 | provided
75 |
76 |
77 | com.fasterxml.jackson.core
78 | jackson-databind
79 | ${jackson.version}
80 |
81 |
82 | com.google.guava
83 | guava
84 | ${guava.version}
85 |
86 |
87 | log4j
88 | log4j
89 | ${log4j.version}
90 |
91 |
92 | junit
93 | junit
94 | ${junit.version}
95 | test
96 |
97 |
98 | org.mockito
99 | mockito-core
100 | ${mockito.version}
101 | test
102 |
103 |
104 |
105 |
106 |
107 | lombok-needs-tools-jar
108 |
109 |
110 | ${java.home}/../lib/tools.jar
111 |
112 |
113 |
114 |
115 |
116 | org.projectlombok
117 | lombok-maven-plugin
118 | ${lombok.plugin.version}
119 |
120 |
121 | sun.jdk
122 | tools
123 | ${jdk.version}
124 | system
125 | ${java.home}/../lib/tools.jar
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 | release
134 |
135 |
136 |
137 | org.apache.maven.plugins
138 | maven-source-plugin
139 | ${maven.source.plugin.version}
140 |
141 |
142 | attach-sources
143 |
144 | jar
145 |
146 |
147 |
148 |
149 |
150 | org.apache.maven.plugins
151 | maven-javadoc-plugin
152 | ${maven.javadoc.plugin.version}
153 |
154 | ${project.version}
155 | ${src.dir}
156 | com.codepine.api.testrail.internal
157 | -Xdoclint:none
158 |
159 |
160 |
161 | attach-javadocs
162 |
163 | jar
164 |
165 |
166 |
167 |
168 |
169 | org.apache.maven.plugins
170 | maven-gpg-plugin
171 | ${maven.gpg.plugin.version}
172 |
173 |
174 | sign-artifacts
175 | verify
176 |
177 | sign
178 |
179 |
180 |
181 |
182 |
183 | org.sonatype.plugins
184 | nexus-staging-maven-plugin
185 | ${nexus.staging.maven.plugin.version}
186 | true
187 |
188 | ossrh
189 | https://oss.sonatype.org/
190 | false
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 | ${src.dir}
200 | ${tst.dir}
201 |
202 |
203 | org.projectlombok
204 | lombok-maven-plugin
205 | ${lombok.plugin.version}
206 |
207 |
208 | delombok
209 | generate-sources
210 |
211 | delombok
212 |
213 |
214 | UTF-8
215 |
216 | skip
217 |
218 | false
219 | src/main/java
220 | true
221 |
222 |
223 |
224 | test-delombok
225 | generate-test-sources
226 |
227 | testDelombok
228 |
229 |
230 | UTF-8
231 | true
232 | false
233 | src/test/java
234 |
235 |
236 |
237 |
238 |
239 | org.apache.maven.plugins
240 | maven-compiler-plugin
241 | ${maven.compiler.plugin.version}
242 |
243 | ${jdk.version}
244 | ${jdk.version}
245 | ${jdk.version}
246 |
247 |
248 |
249 | org.jacoco
250 | jacoco-maven-plugin
251 | ${jacoco.plugin.version}
252 |
253 |
254 | default-prepare-agent
255 |
256 | prepare-agent
257 |
258 |
259 |
260 | coverage-report
261 | prepare-package
262 |
263 | report
264 |
265 |
266 |
267 |
268 |
269 | org.apache.maven.plugins
270 | maven-release-plugin
271 | ${maven.release.plugin.version}
272 |
273 | v@{project.version}
274 | release
275 |
276 |
277 |
278 |
279 |
280 |
--------------------------------------------------------------------------------
/src/lombok.config:
--------------------------------------------------------------------------------
1 | lombok.accessors.fluent = false
2 | lombok.accessors.chain = true
3 |
4 | config.stopBubbling = true
--------------------------------------------------------------------------------
/src/main/java/com/codepine/api/testrail/Request.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2015 Kunal Shah
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE.
23 | */
24 |
25 | package com.codepine.api.testrail;
26 |
27 | import com.codepine.api.testrail.internal.*;
28 | import com.codepine.api.testrail.model.Page;
29 | import com.fasterxml.jackson.annotation.JsonInclude;
30 | import com.fasterxml.jackson.core.type.TypeReference;
31 | import com.fasterxml.jackson.databind.*;
32 | import com.google.common.base.Charsets;
33 | import com.google.common.io.ByteStreams;
34 | import lombok.NonNull;
35 | import lombok.extern.log4j.Log4j;
36 |
37 | import javax.xml.bind.DatatypeConverter;
38 | import java.io.*;
39 | import java.lang.reflect.ParameterizedType;
40 | import java.lang.reflect.Type;
41 | import java.net.HttpURLConnection;
42 | import java.net.MalformedURLException;
43 | import java.nio.charset.Charset;
44 | import java.util.ArrayList;
45 | import java.util.List;
46 | import java.util.regex.Matcher;
47 | import java.util.regex.Pattern;
48 |
49 | /**
50 | * TestRail request.
51 | */
52 | @Log4j
53 | public abstract class Request {
54 |
55 | private static final UrlConnectionFactory DEFAULT_URL_CONNECTION_FACTORY = new UrlConnectionFactory();
56 |
57 | private static final ObjectMapper JSON = new ObjectMapper()
58 | .setPropertyNamingStrategy(PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES)
59 | .configure(MapperFeature.DEFAULT_VIEW_INCLUSION, false)
60 | .setSerializationInclusion(JsonInclude.Include.NON_NULL)
61 | .disable(SerializationFeature.FAIL_ON_EMPTY_BEANS)
62 | .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
63 | .registerModules(new CaseModule(), new FieldModule(), new PlanModule(), new ResultModule(), new UnixTimestampModule());
64 |
65 | @NonNull
66 | private final TestRailConfig config;
67 | @NonNull
68 | private final Method method;
69 | @NonNull
70 | private String restPath;
71 | private String apiSegment;
72 | private final Class extends T> responseClass;
73 | private final TypeReference extends T> responseType;
74 | private final TypeReference> pageType;
75 | private UrlConnectionFactory urlConnectionFactory = DEFAULT_URL_CONNECTION_FACTORY;
76 |
77 | Request(TestRailConfig config, Method method, String restPath, Class extends T> responseClass, TypeReference extends T>
78 | responseType, TypeReference> pageType) {
79 | this.config = config;
80 | this.method = method;
81 |
82 | this.responseClass = responseClass;
83 | this.responseType = responseType;
84 | this.pageType = pageType;
85 | this.apiSegment = config.getBaseApiUrl().split("\\?")[1];
86 | this.restPath = restPath.replace(this.apiSegment, "");
87 | }
88 |
89 | /**
90 | * @param config TestRail configuration
91 | * @param method the HTTP method for request
92 | * @param restPath the path of the request URL
93 | * @param responseClass the type of the response entity
94 | */
95 | Request(TestRailConfig config, Method method, String restPath, @NonNull Class extends T> responseClass) {
96 | this(config, method, restPath, responseClass, null, null);
97 | }
98 |
99 | /**
100 | * @param config TestRail configuration
101 | * @param method the HTTP method for request
102 | * @param restPath the path of the request URL
103 | * @param responseType the type of the response entity
104 | */
105 | Request(TestRailConfig config, Method method, String restPath, @NonNull TypeReference extends T> responseType) {
106 | this(config, method, restPath, null, responseType, null);
107 | }
108 |
109 | Request(TestRailConfig config, Method method, String restPath, @NonNull TypeReference extends T> responseType,
110 | @NonNull TypeReference> pageType) {
111 | this(config, method, restPath, null, responseType, pageType);
112 | }
113 |
114 | /**
115 | * Get URL string for this request.
116 | *
117 | * @return the string URL
118 | * @throws IOException if there is an error creating query parameter string
119 | */
120 | private String getUrl() throws IOException {
121 | StringBuilder urlBuilder = new StringBuilder(config.getBaseApiUrl()).append(restPath);
122 |
123 | String queryParamJson = JSON.writerWithView(getClass()).writeValueAsString(this);
124 | String queryParamString = JSON.readValue(queryParamJson, QueryParameterString.class).toString();
125 | if (!queryParamString.isEmpty()) {
126 | urlBuilder.append("&").append(queryParamString);
127 | }
128 |
129 | return urlBuilder.toString();
130 | }
131 |
132 | /**
133 | * Override this method to provide content to be send with {@code Method#POST} requests.
134 | *
135 | * @return content
136 | */
137 | Object getContent() {
138 | return null;
139 | }
140 |
141 | /**
142 | * Override this method to provide supplementary information to deserializer.
143 | *
144 | * @return any object acting as supplement for deserialization
145 | */
146 | Object getSupplementForDeserialization() {
147 | return null;
148 | }
149 |
150 | /**
151 | * Execute this request.
152 | *
153 | * @return response from TestRail
154 | */
155 | public T execute() {
156 | try {
157 |
158 | String url = getUrl();
159 | HttpURLConnection con = (HttpURLConnection) urlConnectionFactory.getUrlConnection(url);
160 | con.setRequestMethod(method.name());
161 | if (config.getApplicationName().isPresent()) {
162 | con.setRequestProperty("User-Agent", config.getApplicationName().get());
163 | }
164 | con.setRequestProperty("Content-Type", "application/json");
165 | String basicAuth = "Basic "
166 | + DatatypeConverter.printBase64Binary((config.getUsername()
167 | + ":" + config.getPassword()).getBytes(Charset.forName("UTF-8")));
168 | con.setRequestProperty("Authorization", basicAuth);
169 | if (method == Method.POST) {
170 | con.setDoOutput(true);
171 | Object content = getContent();
172 | if (content != null) {
173 | try (OutputStream outputStream = new BufferedOutputStream(con.getOutputStream())) {
174 | JSON.writerWithView(this.getClass()).writeValue(outputStream, content);
175 | }
176 | } else {
177 | con.setFixedLengthStreamingMode(0);
178 | }
179 | }
180 | log.debug("Sending " + method + " request to URL : " + url);
181 | int responseCode = 0;
182 | try {
183 | responseCode = con.getResponseCode();
184 | } catch (IOException e) {
185 | // swallow it since for 401 getResponseCode throws an IOException
186 | responseCode = con.getResponseCode();
187 | }
188 | log.debug("Response Code : " + responseCode);
189 |
190 | if (responseCode != HttpURLConnection.HTTP_OK) {
191 | try (InputStream errorStream = con.getErrorStream()) {
192 | TestRailException.Builder exceptionBuilder = new TestRailException.Builder().setResponseCode(responseCode);
193 | if (errorStream == null) {
194 | throw exceptionBuilder.setError("").build();
195 | }
196 | throw JSON.readerForUpdating(exceptionBuilder).readValue(new BufferedInputStream(errorStream)).build();
197 | }
198 | }
199 |
200 | try (InputStream responseStream = new BufferedInputStream(con.getInputStream())) {
201 | Object supplementForDeserialization = getSupplementForDeserialization();
202 | if (responseClass != null) {
203 | if (responseClass == Void.class) {
204 | return null;
205 | }
206 | if (supplementForDeserialization != null) {
207 | return JSON.reader(responseClass).with(new InjectableValues.Std().addValue(responseClass.toString(), supplementForDeserialization)).readValue(responseStream);
208 | }
209 | return JSON.readValue(responseStream, responseClass);
210 | } else {
211 | String payload = new String(ByteStreams.toByteArray(responseStream), Charsets.UTF_8).replace("\"_links\":", "\"links\":");
212 | if (((ParameterizedType) responseType.getType()).getRawType().getTypeName().equals("java.util.List")
213 | && payload.contains("\"offset\":") && payload.contains("\"limit\":") && payload.contains("\"offset\":")) {
214 | Matcher matcher = Pattern.compile("get_([^\\_/]+)").matcher(restPath);
215 | if (matcher.find())
216 | PageDeserializer.field = matcher.group(1);
217 | try {
218 | PageDeserializer.type = Class.forName(((ParameterizedType) responseType.getType()).getActualTypeArguments()[0].getTypeName());
219 | }
220 | catch(Exception e) {
221 | return ((T)new ArrayList());
222 | }
223 | if (supplementForDeserialization != null) {
224 | PageDeserializer.supplement = supplementForDeserialization;
225 | }
226 | Page page = JSON.readValue(payload, pageType);
227 | if (page._links.next != null) {
228 | restPath = page._links.next.replace(this.apiSegment, "");
229 | T concat = execute();
230 | T models = page.objects;
231 | ((List)models).addAll(((List)concat));
232 | return models;
233 | }
234 | else
235 | return page.objects;
236 | }
237 | else if (supplementForDeserialization != null) {
238 | String supplementKey = responseType.getType().toString();
239 | if (responseType.getType() instanceof ParameterizedType) {
240 | Type[] actualTypes = ((ParameterizedType) responseType.getType()).getActualTypeArguments();
241 | if (actualTypes.length == 1 && actualTypes[0] instanceof Class>) {
242 | supplementKey = actualTypes[0].toString();
243 | }
244 | }
245 | return JSON.reader(responseType).with(new InjectableValues.Std().addValue(supplementKey, supplementForDeserialization)).readValue(payload);
246 | }
247 | return JSON.readValue(payload, responseType);
248 | }
249 | }
250 |
251 | } catch (MalformedURLException e) {
252 | throw new RuntimeException(e);
253 | } catch (IOException e) {
254 | throw new RuntimeException(e);
255 | }
256 | }
257 |
258 | /**
259 | * Set URL connection factory. Only used for testing.
260 | *
261 | * @param urlConnectionFactory the URL connection factory
262 | */
263 | void setUrlConnectionFactory(UrlConnectionFactory urlConnectionFactory) {
264 | this.urlConnectionFactory = urlConnectionFactory;
265 | }
266 |
267 | /**
268 | * Allowed HTTP methods.
269 | */
270 | static enum Method {
271 | GET, POST;
272 | }
273 |
274 | }
275 |
--------------------------------------------------------------------------------
/src/main/java/com/codepine/api/testrail/TestRailConfig.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2015 Kunal Shah
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE.
23 | */
24 |
25 | package com.codepine.api.testrail;
26 |
27 | import com.google.common.base.Optional;
28 | import lombok.*;
29 |
30 | /**
31 | * Configuration for using this client library.
32 | */
33 | @Value
34 | @ToString(exclude = {"password"})
35 | class TestRailConfig {
36 |
37 | private final String baseApiUrl;
38 | private final String username;
39 | private final String password;
40 | private final Optional applicationName;
41 |
42 | TestRailConfig(final String baseApiUrl, final String username, final String password, final String applicationName) {
43 | this.baseApiUrl = baseApiUrl;
44 | this.username = username;
45 | this.password = password;
46 | this.applicationName = Optional.fromNullable(applicationName);
47 | }
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/src/main/java/com/codepine/api/testrail/TestRailException.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2015 Kunal Shah
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE.
23 | */
24 |
25 | package com.codepine.api.testrail;
26 |
27 | import com.google.common.base.Preconditions;
28 | import lombok.Getter;
29 | import lombok.Setter;
30 | import lombok.experimental.Accessors;
31 |
32 | /**
33 | * Exception representing error returned by TestRail API.
34 | */
35 | public class TestRailException extends RuntimeException {
36 |
37 | private static final long serialVersionUID = -2131644110724458502L;
38 |
39 | @Getter
40 | private final int responseCode;
41 |
42 | /**
43 | * @param responseCode the HTTP response code from the TestRail server
44 | * @param error the error message from TestRail service
45 | */
46 | TestRailException(int responseCode, String error) {
47 | super(responseCode + " - " + error);
48 | this.responseCode = responseCode;
49 | }
50 |
51 | /**
52 | * Builder for {@code TestRailException}.
53 | */
54 | @Setter
55 | static class Builder {
56 | private int responseCode;
57 | private String error;
58 |
59 | public TestRailException build() {
60 | Preconditions.checkNotNull(responseCode);
61 | Preconditions.checkNotNull(error);
62 | return new TestRailException(responseCode, error);
63 | }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/main/java/com/codepine/api/testrail/internal/BooleanToIntSerializer.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2015 Kunal Shah
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE.
23 | */
24 |
25 | package com.codepine.api.testrail.internal;
26 |
27 | import com.fasterxml.jackson.core.JsonGenerator;
28 | import com.fasterxml.jackson.core.JsonProcessingException;
29 | import com.fasterxml.jackson.databind.JsonSerializer;
30 | import com.fasterxml.jackson.databind.SerializerProvider;
31 |
32 | import java.io.IOException;
33 |
34 | /**
35 | * Serializer to convert (boolean) false/true to (int) 0/1.
36 | */
37 | public class BooleanToIntSerializer extends JsonSerializer {
38 |
39 | @Override
40 | public void serialize(final Boolean value, final JsonGenerator jgen, final SerializerProvider provider) throws IOException, JsonProcessingException {
41 | jgen.writeNumber(value == null ? 0 : value ? 1 : 0);
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/main/java/com/codepine/api/testrail/internal/CaseModule.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2015 Kunal Shah
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE.
23 | */
24 |
25 | package com.codepine.api.testrail.internal;
26 |
27 | import com.codepine.api.testrail.model.Case;
28 | import com.codepine.api.testrail.model.CaseField;
29 | import com.codepine.api.testrail.model.Field;
30 | import com.fasterxml.jackson.core.JsonParser;
31 | import com.fasterxml.jackson.core.JsonProcessingException;
32 | import com.fasterxml.jackson.databind.BeanDescription;
33 | import com.fasterxml.jackson.databind.DeserializationConfig;
34 | import com.fasterxml.jackson.databind.DeserializationContext;
35 | import com.fasterxml.jackson.databind.JsonDeserializer;
36 | import com.fasterxml.jackson.databind.JsonMappingException;
37 | import com.fasterxml.jackson.databind.ObjectMapper;
38 | import com.fasterxml.jackson.databind.deser.BeanDeserializerModifier;
39 | import com.fasterxml.jackson.databind.deser.ResolvableDeserializer;
40 | import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
41 | import com.fasterxml.jackson.databind.module.SimpleModule;
42 | import com.google.common.base.Function;
43 | import com.google.common.collect.Maps;
44 |
45 | import java.io.IOException;
46 | import java.util.HashMap;
47 | import java.util.List;
48 | import java.util.Map;
49 |
50 | import static com.google.common.base.Preconditions.checkArgument;
51 |
52 | /**
53 | * Jackson module for {@link com.codepine.api.testrail.model.Case}.
54 | *
55 | * INTERNAL ONLY
56 | */
57 | public class CaseModule extends SimpleModule {
58 |
59 | @Override
60 | public void setupModule(SetupContext setupContext) {
61 | setupContext.addBeanDeserializerModifier(new CaseDeserializerModifier());
62 | super.setupModule(setupContext);
63 | }
64 |
65 | private static class CaseDeserializer extends StdDeserializer implements ResolvableDeserializer {
66 | private final JsonDeserializer> defaultDeserializer;
67 |
68 | CaseDeserializer(JsonDeserializer> defaultDeserializer) {
69 | super(Case.class);
70 | this.defaultDeserializer = defaultDeserializer;
71 | }
72 |
73 | @Override
74 | public Case deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
75 | Case testCase = (Case) defaultDeserializer.deserialize(jsonParser, deserializationContext);
76 |
77 | ObjectMapper mapper = (ObjectMapper) jsonParser.getCodec();
78 | List caseFieldList = (List) deserializationContext.findInjectableValue(Case.class.toString(), null, null);
79 | Map caseFields = Maps.uniqueIndex(caseFieldList, new Function() {
80 | @Override
81 | public String apply(final CaseField caseField) {
82 | return caseField.getName();
83 | }
84 | });
85 | Map customFields = new HashMap<>(testCase.getCustomFields().size());
86 | for (Map.Entry customField : testCase.getCustomFields().entrySet()) {
87 | checkArgument(caseFields.containsKey(customField.getKey()), "Case field list configuration is possibly outdated since it does not contain custom field: " + customField.getKey());
88 | customFields.put(customField.getKey(), mapper.convertValue(customField.getValue(), Field.Type.getType(caseFields.get(customField.getKey()).getTypeId()).getTypeReference()));
89 | }
90 | testCase.setCustomFields(customFields);
91 | return testCase;
92 | }
93 |
94 | @Override
95 | public void resolve(DeserializationContext deserializationContext) throws JsonMappingException {
96 | ((ResolvableDeserializer) defaultDeserializer).resolve(deserializationContext);
97 | }
98 | }
99 |
100 | private static class CaseDeserializerModifier extends BeanDeserializerModifier {
101 |
102 | @Override
103 | public JsonDeserializer> modifyDeserializer(DeserializationConfig deserializationConfig, BeanDescription beanDescription, JsonDeserializer> jsonDeserializer) {
104 | if (Case.class.isAssignableFrom(beanDescription.getBeanClass())) {
105 | return new CaseDeserializer(jsonDeserializer);
106 | }
107 | return jsonDeserializer;
108 | }
109 |
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/src/main/java/com/codepine/api/testrail/internal/CsvToListDeserializer.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2015 Kunal Shah
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE.
23 | */
24 |
25 | package com.codepine.api.testrail.internal;
26 |
27 | import com.fasterxml.jackson.core.JsonParser;
28 | import com.fasterxml.jackson.core.JsonProcessingException;
29 | import com.fasterxml.jackson.databind.DeserializationContext;
30 | import com.fasterxml.jackson.databind.JsonDeserializer;
31 | import com.google.common.base.Splitter;
32 |
33 | import java.io.IOException;
34 | import java.util.List;
35 |
36 | /**
37 | * Deserializer to convert csv string to {@code List}.
38 | */
39 | public class CsvToListDeserializer extends JsonDeserializer> {
40 |
41 | @Override
42 | public List deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
43 | if (jp.getValueAsString() == null) {
44 | return null;
45 | }
46 | return Splitter.on(',').trimResults().omitEmptyStrings().splitToList(jp.getValueAsString());
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/main/java/com/codepine/api/testrail/internal/FieldModule.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2015 Kunal Shah
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE.
23 | */
24 |
25 | package com.codepine.api.testrail.internal;
26 |
27 | import com.codepine.api.testrail.model.Field;
28 | import com.fasterxml.jackson.core.JsonParser;
29 | import com.fasterxml.jackson.core.JsonProcessingException;
30 | import com.fasterxml.jackson.databind.*;
31 | import com.fasterxml.jackson.databind.deser.BeanDeserializerModifier;
32 | import com.fasterxml.jackson.databind.deser.ResolvableDeserializer;
33 | import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
34 | import com.fasterxml.jackson.databind.module.SimpleModule;
35 |
36 | import java.io.IOException;
37 |
38 | /**
39 | * Jackson module for {@link com.codepine.api.testrail.model.Field}.
40 | *
41 | * INTERNAL ONLY
42 | */
43 | public class FieldModule extends SimpleModule {
44 |
45 | @Override
46 | public void setupModule(SetupContext setupContext) {
47 | setupContext.addBeanDeserializerModifier(new FieldDeserializerModifier());
48 | super.setupModule(setupContext);
49 | }
50 |
51 | static class FieldDeserializer extends StdDeserializer implements ResolvableDeserializer {
52 | private final JsonDeserializer> defaultDeserializer;
53 |
54 | FieldDeserializer(JsonDeserializer> defaultDeserializer) {
55 | super(Field.class);
56 | this.defaultDeserializer = defaultDeserializer;
57 | }
58 |
59 | @Override
60 | public Field deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
61 | Field field = (Field) defaultDeserializer.deserialize(jsonParser, deserializationContext);
62 | ObjectMapper mapper = (ObjectMapper) jsonParser.getCodec();
63 | // set type
64 | field.setType(Field.Type.getType(field.getTypeId()));
65 | for (Field.Config config : field.getConfigs()) {
66 | // update options to correct type implementations
67 | config.setOptions(mapper.convertValue(config.getOptions(), field.getType().getOptionsClass()));
68 | }
69 | return field;
70 | }
71 |
72 | @Override
73 | public void resolve(DeserializationContext deserializationContext) throws JsonMappingException {
74 | ((ResolvableDeserializer) defaultDeserializer).resolve(deserializationContext);
75 | }
76 | }
77 |
78 | private static class FieldDeserializerModifier extends BeanDeserializerModifier {
79 |
80 | @Override
81 | public JsonDeserializer> modifyDeserializer(DeserializationConfig deserializationConfig, BeanDescription beanDescription, JsonDeserializer> jsonDeserializer) {
82 | if (Field.class.isAssignableFrom(beanDescription.getBeanClass())) {
83 | return new FieldDeserializer(jsonDeserializer);
84 | }
85 | return jsonDeserializer;
86 | }
87 |
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/src/main/java/com/codepine/api/testrail/internal/IntToBooleanDeserializer.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2015 Kunal Shah
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE.
23 | */
24 |
25 | package com.codepine.api.testrail.internal;
26 |
27 | import com.fasterxml.jackson.core.JsonParser;
28 | import com.fasterxml.jackson.core.JsonProcessingException;
29 | import com.fasterxml.jackson.databind.DeserializationContext;
30 | import com.fasterxml.jackson.databind.JsonDeserializer;
31 |
32 | import java.io.IOException;
33 |
34 | /**
35 | * Deserializer to convert (int) 0/1 to (boolean) false/true.
36 | */
37 | public class IntToBooleanDeserializer extends JsonDeserializer {
38 |
39 | @Override
40 | public Boolean deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
41 | return jp.getValueAsInt(0) <= 0 ? Boolean.FALSE : Boolean.TRUE;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/main/java/com/codepine/api/testrail/internal/ListToCsvSerializer.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2015 Kunal Shah
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE.
23 | */
24 |
25 | package com.codepine.api.testrail.internal;
26 |
27 | import com.fasterxml.jackson.core.JsonGenerator;
28 | import com.fasterxml.jackson.core.JsonProcessingException;
29 | import com.fasterxml.jackson.databind.JsonSerializer;
30 | import com.fasterxml.jackson.databind.SerializerProvider;
31 | import com.google.common.base.Joiner;
32 |
33 | import java.io.IOException;
34 | import java.util.List;
35 |
36 | /**
37 | * Serializer to convert {@code List>} to csv string.
38 | */
39 | public class ListToCsvSerializer extends JsonSerializer> {
40 |
41 | @Override
42 | public void serialize(List> value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
43 | if (value != null) {
44 | jgen.writeString(Joiner.on(',').join(value));
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/main/java/com/codepine/api/testrail/internal/PageDeserializer.java:
--------------------------------------------------------------------------------
1 | package com.codepine.api.testrail.internal;
2 |
3 | import com.codepine.api.testrail.model.Links;
4 | import com.codepine.api.testrail.model.Page;
5 | import com.fasterxml.jackson.annotation.JsonInclude;
6 | import com.fasterxml.jackson.core.JsonParser;
7 | import com.fasterxml.jackson.core.JsonProcessingException;
8 | import com.fasterxml.jackson.databind.*;
9 | import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
10 | import com.fasterxml.jackson.databind.node.ArrayNode;
11 |
12 | import java.io.IOException;
13 | import java.util.ArrayList;
14 | import java.util.Collections;
15 | import java.util.List;
16 |
17 | public class PageDeserializer extends StdDeserializer {
18 |
19 | public static String field = "objects";
20 | public static Class type = Object.class;
21 | public static Object supplement = Collections.emptyList();
22 |
23 | public PageDeserializer() {
24 | this(null);
25 | }
26 |
27 | public PageDeserializer(Class> vc) {
28 | super(vc);
29 | }
30 |
31 | @Override
32 | public Page deserialize(JsonParser jp, DeserializationContext ctxt)
33 | throws IOException, JsonProcessingException {
34 | JsonNode node = jp.getCodec().readTree(jp);
35 | int offset = node.get("offset").asInt();
36 | int limit = node.get("limit").asInt();
37 | int size = node.get("size").asInt();
38 | JsonNode links = node.get("links");
39 | String next = links.get("next").isNull() ? null : links.get("next").asText();
40 | String prev = links.get("prev").isNull() ? null : links.get("prev").asText();
41 | ArrayNode objects = (ArrayNode) node.get(field);
42 | List list = new ArrayList<>();
43 | ObjectMapper mapper = new ObjectMapper()
44 | .setPropertyNamingStrategy(PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES)
45 | .configure(MapperFeature.DEFAULT_VIEW_INCLUSION, false)
46 | .setSerializationInclusion(JsonInclude.Include.NON_NULL)
47 | .disable(SerializationFeature.FAIL_ON_EMPTY_BEANS)
48 | .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
49 | .registerModules(new CaseModule(), new FieldModule(), new PlanModule(), new ResultModule(), new UnixTimestampModule());
50 | for(int i = 0; i < objects.size(); i++) {
51 | JsonNode element = objects.get(i);
52 | list.add(mapper.reader(type).with(new InjectableValues.Std().addValue(type.toString(), supplement)).readValue(element.toString()));
53 | }
54 | Page page = new Page();
55 | page.limit = limit;
56 | page.offset = offset;
57 | page.size = size;
58 | page._links = new Links();
59 | page._links.next = next;
60 | page._links.prev = prev;
61 | page.objects = list;
62 | return page;
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/main/java/com/codepine/api/testrail/internal/PlanModule.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2015 Kunal Shah
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE.
23 | */
24 |
25 | package com.codepine.api.testrail.internal;
26 |
27 | import com.codepine.api.testrail.model.Plan;
28 | import com.codepine.api.testrail.model.Run;
29 | import com.fasterxml.jackson.core.JsonParser;
30 | import com.fasterxml.jackson.core.JsonProcessingException;
31 | import com.fasterxml.jackson.databind.*;
32 | import com.fasterxml.jackson.databind.deser.BeanDeserializerModifier;
33 | import com.fasterxml.jackson.databind.deser.ResolvableDeserializer;
34 | import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
35 | import com.fasterxml.jackson.databind.module.SimpleModule;
36 |
37 | import java.io.IOException;
38 |
39 | /**
40 | * Jackson module for {@link com.codepine.api.testrail.model.Plan} to set some additional properties on {@link com.codepine.api.testrail.model.Plan.Entry.Run}.
41 | *
42 | * INTERNAL ONLY
43 | */
44 | public class PlanModule extends SimpleModule {
45 |
46 | @Override
47 | public void setupModule(SetupContext setupContext) {
48 | setupContext.addBeanDeserializerModifier(new PlanDeserializerModifier());
49 | super.setupModule(setupContext);
50 | }
51 |
52 | private static class PlanDeserializer extends StdDeserializer implements ResolvableDeserializer {
53 |
54 | private final JsonDeserializer> defaultDeserializer;
55 |
56 | PlanDeserializer(JsonDeserializer> defaultDeserializer) {
57 | super(Plan.class);
58 | this.defaultDeserializer = defaultDeserializer;
59 | }
60 |
61 | @Override
62 | public Plan deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
63 | Plan plan = (Plan) defaultDeserializer.deserialize(jsonParser, deserializationContext);
64 | ObjectMapper mapper = (ObjectMapper) jsonParser.getCodec();
65 |
66 | if (plan.getEntries() != null) {
67 | for (Plan.Entry entry : plan.getEntries()) {
68 | if (entry.getRuns() != null) {
69 | for (Run run : entry.getRuns()) {
70 | run.setCreatedOn(plan.getCreatedOn());
71 | run.setCreatedBy(plan.getCreatedBy());
72 | }
73 | }
74 | }
75 | }
76 |
77 | return plan;
78 | }
79 |
80 | @Override
81 | public void resolve(DeserializationContext deserializationContext) throws JsonMappingException {
82 | ((ResolvableDeserializer) defaultDeserializer).resolve(deserializationContext);
83 | }
84 | }
85 |
86 | private static class PlanDeserializerModifier extends BeanDeserializerModifier {
87 |
88 | @Override
89 | public JsonDeserializer> modifyDeserializer(DeserializationConfig deserializationConfig, BeanDescription beanDescription, JsonDeserializer> jsonDeserializer) {
90 | if (Plan.class.isAssignableFrom(beanDescription.getBeanClass())) {
91 | return new PlanDeserializer(jsonDeserializer);
92 | }
93 | return jsonDeserializer;
94 | }
95 |
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/src/main/java/com/codepine/api/testrail/internal/QueryParameterString.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2015 Kunal Shah
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE.
23 | */
24 |
25 | package com.codepine.api.testrail.internal;
26 |
27 | import com.fasterxml.jackson.annotation.JsonAnySetter;
28 |
29 | import java.io.UnsupportedEncodingException;
30 | import java.net.URLEncoder;
31 |
32 | /**
33 | * String representing query parameters of a URL.
34 | */
35 | public final class QueryParameterString {
36 |
37 | private final StringBuilder queryParamStringBuilder = new StringBuilder();
38 |
39 | @JsonAnySetter
40 | public void addQueryParameter(String key, String value) throws UnsupportedEncodingException {
41 | if (queryParamStringBuilder.length() > 0) {
42 | queryParamStringBuilder.append('&');
43 | }
44 | queryParamStringBuilder.append(URLEncoder.encode(key, "UTF-8")).append('=').append(URLEncoder.encode(value, "UTF-8"));
45 | }
46 |
47 | @Override
48 | public String toString() {
49 | return queryParamStringBuilder.toString();
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/main/java/com/codepine/api/testrail/internal/ResultModule.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2015 Kunal Shah
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE.
23 | */
24 |
25 | package com.codepine.api.testrail.internal;
26 |
27 | import com.codepine.api.testrail.model.Field;
28 | import com.codepine.api.testrail.model.Result;
29 | import com.codepine.api.testrail.model.ResultField;
30 | import com.fasterxml.jackson.core.JsonParser;
31 | import com.fasterxml.jackson.core.JsonProcessingException;
32 | import com.fasterxml.jackson.databind.BeanDescription;
33 | import com.fasterxml.jackson.databind.DeserializationConfig;
34 | import com.fasterxml.jackson.databind.DeserializationContext;
35 | import com.fasterxml.jackson.databind.JsonDeserializer;
36 | import com.fasterxml.jackson.databind.JsonMappingException;
37 | import com.fasterxml.jackson.databind.ObjectMapper;
38 | import com.fasterxml.jackson.databind.deser.BeanDeserializerModifier;
39 | import com.fasterxml.jackson.databind.deser.ResolvableDeserializer;
40 | import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
41 | import com.fasterxml.jackson.databind.module.SimpleModule;
42 | import com.google.common.base.Function;
43 | import com.google.common.collect.Maps;
44 |
45 | import java.io.IOException;
46 | import java.util.HashMap;
47 | import java.util.List;
48 | import java.util.Map;
49 |
50 | import static com.google.common.base.Preconditions.checkArgument;
51 |
52 | /**
53 | * Jackson module for {@link com.codepine.api.testrail.model.Result}.
54 | *
55 | * INTERNAL ONLY
56 | */
57 | public class ResultModule extends SimpleModule {
58 |
59 | @Override
60 | public void setupModule(SetupContext setupContext) {
61 | setupContext.addBeanDeserializerModifier(new ResultDeserializerModifier());
62 | super.setupModule(setupContext);
63 | }
64 |
65 | private static class ResultDeserializer extends StdDeserializer implements ResolvableDeserializer {
66 | private final JsonDeserializer> defaultDeserializer;
67 |
68 | ResultDeserializer(JsonDeserializer> defaultDeserializer) {
69 | super(Result.class);
70 | this.defaultDeserializer = defaultDeserializer;
71 | }
72 |
73 | @Override
74 | public Result deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
75 | Result result = (Result) defaultDeserializer.deserialize(jsonParser, deserializationContext);
76 |
77 | ObjectMapper mapper = (ObjectMapper) jsonParser.getCodec();
78 | List resultFieldList = (List) deserializationContext.findInjectableValue(Result.class.toString(), null, null);
79 | Map resultFields = Maps.uniqueIndex(resultFieldList, new Function() {
80 | @Override
81 | public String apply(final ResultField resultField) {
82 | return resultField.getName();
83 | }
84 | });
85 | Map customFields = new HashMap<>(result.getCustomFields().size());
86 | for (Map.Entry customField : result.getCustomFields().entrySet()) {
87 | checkArgument(resultFields.containsKey(customField.getKey()), "Result field list configuration is possibly outdated since it does not contain custom field: " + customField.getKey());
88 | customFields.put(customField.getKey(), mapper.convertValue(customField.getValue(), Field.Type.getType(resultFields.get(customField.getKey()).getTypeId()).getTypeReference()));
89 | }
90 | result.setCustomFields(customFields);
91 | return result;
92 | }
93 |
94 | @Override
95 | public void resolve(DeserializationContext deserializationContext) throws JsonMappingException {
96 | ((ResolvableDeserializer) defaultDeserializer).resolve(deserializationContext);
97 | }
98 | }
99 |
100 | private static class ResultDeserializerModifier extends BeanDeserializerModifier {
101 |
102 | @Override
103 | public JsonDeserializer> modifyDeserializer(DeserializationConfig deserializationConfig, BeanDescription beanDescription, JsonDeserializer> jsonDeserializer) {
104 | if (Result.class.isAssignableFrom(beanDescription.getBeanClass())) {
105 | return new ResultDeserializer(jsonDeserializer);
106 | }
107 | return jsonDeserializer;
108 | }
109 |
110 | }
111 | }
--------------------------------------------------------------------------------
/src/main/java/com/codepine/api/testrail/internal/StringToMapDeserializer.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2015 Kunal Shah
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE.
23 | */
24 |
25 | package com.codepine.api.testrail.internal;
26 |
27 | import com.fasterxml.jackson.core.JsonParser;
28 | import com.fasterxml.jackson.core.JsonProcessingException;
29 | import com.fasterxml.jackson.databind.DeserializationContext;
30 | import com.fasterxml.jackson.databind.JsonDeserializer;
31 | import com.google.common.base.Function;
32 | import com.google.common.base.Splitter;
33 | import com.google.common.collect.Maps;
34 |
35 | import java.io.IOException;
36 | import java.util.Map;
37 |
38 | /**
39 | * Deserializer string of form a,b\nc,d\ne,f to a map of form {a->b, c->d, e->f}.
40 | */
41 | public class StringToMapDeserializer extends JsonDeserializer
85 | */
86 | @RequiredArgsConstructor
87 | public static enum Type {
88 | UNKNOWN(Config.Options.class, new TypeReference