├── src └── main │ ├── resources │ ├── com │ │ └── apifortress │ │ │ └── jenkins │ │ │ └── plugin │ │ │ └── ApiFortressBuilder │ │ │ ├── help-blocking.html │ │ │ ├── help-silent.html │ │ │ ├── help-dryrun.html │ │ │ ├── help-mode.html │ │ │ ├── help-hook.html │ │ │ ├── help-id.html │ │ │ └── config.jelly │ └── index.jelly │ └── java │ └── com │ └── apifortress │ └── jenkins │ └── plugin │ └── ApiFortressBuilder.java ├── LICENSE ├── pom.xml └── README.md /src/main/resources/com/apifortress/jenkins/plugin/ApiFortressBuilder/help-blocking.html: -------------------------------------------------------------------------------- 1 | Set this flag is a test failure must determine a build failure. -------------------------------------------------------------------------------- /src/main/resources/com/apifortress/jenkins/plugin/ApiFortressBuilder/help-silent.html: -------------------------------------------------------------------------------- 1 | Set this flag if a failure should not generate API Fortress alerts. -------------------------------------------------------------------------------- /src/main/resources/com/apifortress/jenkins/plugin/ApiFortressBuilder/help-dryrun.html: -------------------------------------------------------------------------------- 1 | Set this flag if you don't want the event to be stored in API Fortress. -------------------------------------------------------------------------------- /src/main/resources/com/apifortress/jenkins/plugin/ApiFortressBuilder/help-mode.html: -------------------------------------------------------------------------------- 1 | When running in single test mode, once specific test will run. When running in automatch 2 | mode, a collection of tests will run. -------------------------------------------------------------------------------- /src/main/resources/index.jelly: -------------------------------------------------------------------------------- 1 | 2 |
3 | This plugin allows you to integrate API Fortress API testing 4 | in your Jenkins CI projects. 5 |
6 | -------------------------------------------------------------------------------- /src/main/resources/com/apifortress/jenkins/plugin/ApiFortressBuilder/help-hook.html: -------------------------------------------------------------------------------- 1 | The API Hook URL.
2 | API Hooks are unique urls connected to projects. They provide access to API Fortress automation 3 | API and will allow you to run tests.
4 | You can create one in the API Fortress Company dashboard. -------------------------------------------------------------------------------- /src/main/resources/com/apifortress/jenkins/plugin/ApiFortressBuilder/help-id.html: -------------------------------------------------------------------------------- 1 |

Test ID

2 | When running in Single Test mode, you have to provide the ID of the test you want to run. 3 | You can find this information in the interstitial page of the test. 4 |

Automatch URL

5 | When running in Automatch mode, introduce here the URL of the resource that you are willing 6 | to test. 7 |

Tag

8 | When running in tag mode, introduce the tag of the tests you want to run. 9 |

Project

10 | When running in project mode, no value is required. -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Thomas Park 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 13 | all 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 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | 6 | org.jenkins-ci.plugins 7 | plugin 8 | 2.11 9 | 10 | 11 | 12 | com.apifortress.jenkins 13 | apifortress 14 | 1.1-SNAPSHOT 15 | hpi 16 | 17 | 18 | 1.625.3 19 | 7 20 | 2.13 21 | 22 | 23 | API Fortress 24 | API Fortress API testing platform integration 25 | https://wiki.jenkins-ci.org/display/JENKINS/API+Fortress 26 | 27 | 28 | 29 | MIT License 30 | http://opensource.org/licenses/MIT 31 | 32 | 33 | 34 | 35 | scm:git:https://github.com/jenkinsci/apifortress-plugin.git 36 | scm:git:https://github.com/jenkinsci/apifortress-plugin.git 37 | http://github.com/jenkinsci/apifortress-plugin 38 | HEAD 39 | 40 | 41 | 42 | 43 | repo.jenkins-ci.org 44 | https://repo.jenkins-ci.org/public/ 45 | 46 | 47 | 48 | 49 | repo.jenkins-ci.org 50 | https://repo.jenkins-ci.org/public/ 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DEPRECATED 2 | ## For integration with Jenkins, please see the integration documentation located https://apifortress.com/doc/jenkins-integrate-cicd/. APIF-Auto, a Python-based command line tool can also be used, learn how here https://apifortress.com/doc/apif-auto-and-jenkins/. 3 | 4 | # API Fortress Jenkins plugin 5 | This plugin allows you to add an API testing build step in your Jenkins CI. 6 | There are multiple testing modes you can choose from, depending on your needs. 7 | 8 | The key parameter that you need to retrieve from API Fortress for any mode, is the Hook URL, 9 | a unique generated URL representing a project. You can create as many as you want in the company 10 | settings of your API Fortress trial/subscription account. 11 | 12 | ## Modes 13 | There are 4 testing modes available. "Run single test" will run a single test, while the others will 14 | run suites of tests. 15 | 16 | ### Run single test 17 | You can run a single test by providing a test ID. Test IDs can be found in test interstitial pages. 18 | 19 | ### Run automatch 20 | You can run our "automatch" mode by providing a URL representing a certain endpoint. You can configure 21 | automatch patterns in the "Automatch" section of each test. 22 | Read more about automatch [Here](http://apifortress.com/doc/automatch/) . 23 | 24 | ### Run by tag 25 | By running "by tag" you can run multiple tests, marked with a certain tag. Tags can be added and edited 26 | in the test details. 27 | 28 | ### Run project 29 | By running a full project, you'll be running all tests contained in the project. 30 | 31 | ## Options 32 | The following options can apply to any mode. 33 | 34 | ### Blocking 35 | The plugin can silently run (blocking = false), let the build continue with a success and inform you if the 36 | test failed using the various methods available. Or it can be actively determining the build success (blocking=true) 37 | so that the build will wait for the tests result and stop the build if the tests fail. 38 | 39 | ### Dry-run 40 | If checked, no events will be stored within API Fortress. To be used in conjuection with "blocking". 41 | 42 | ### Silent 43 | IF checked, no alerts will be sent if the tests fail. 44 | 45 | ### Parameters override 46 | You can override up to 3 parameters in the test scope. These variables will appear within the test scope just like 47 | any other variable. Pretty useful if you're willing to override the domain of the service being tested (Ie. staging vs production). 48 | -------------------------------------------------------------------------------- /src/main/resources/com/apifortress/jenkins/plugin/ApiFortressBuilder/config.jelly: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /src/main/java/com/apifortress/jenkins/plugin/ApiFortressBuilder.java: -------------------------------------------------------------------------------- 1 | package com.apifortress.jenkins.plugin; 2 | import groovy.json.JsonOutput; 3 | import groovy.json.JsonSlurper; 4 | import hudson.Extension; 5 | import hudson.Launcher; 6 | import hudson.model.AbstractBuild; 7 | import hudson.model.AbstractProject; 8 | import hudson.model.BuildListener; 9 | import hudson.tasks.BuildStepDescriptor; 10 | import hudson.tasks.Builder; 11 | import hudson.util.FormValidation; 12 | import net.sf.json.JSONObject; 13 | import org.kohsuke.stapler.DataBoundConstructor; 14 | import org.kohsuke.stapler.QueryParameter; 15 | import org.kohsuke.stapler.StaplerRequest; 16 | 17 | import javax.servlet.ServletException; 18 | import java.io.DataOutputStream; 19 | import java.io.IOException; 20 | import java.io.InputStreamReader; 21 | import java.net.HttpURLConnection; 22 | import java.net.URL; 23 | import java.util.ArrayList; 24 | import java.util.HashMap; 25 | import java.util.Iterator; 26 | import java.util.Map; 27 | import java.util.logging.Level; 28 | import java.util.logging.Logger; 29 | 30 | /** 31 | * The API Fortress Jenkins Plugin 32 | */ 33 | public class ApiFortressBuilder extends Builder{ 34 | 35 | /** 36 | * The Hook URL 37 | */ 38 | private final String hook; 39 | /** 40 | * singleTest, automatch, tag, project 41 | */ 42 | private final String mode; 43 | /** 44 | * can either be the test ID, the automatch URL or the tag 45 | */ 46 | private final String id; 47 | /** 48 | * true if you want to return a build step failure on test failure 49 | */ 50 | private final boolean blocking; 51 | /** 52 | * true if you don't want to store the event in the API Fortress dashboard 53 | */ 54 | private final boolean dryrun; 55 | /** 56 | * true if you don't want notifications to be sent 57 | */ 58 | private final boolean silent; 59 | /** 60 | * first couple of override parameters 61 | */ 62 | private final String param1name,param1value; 63 | /** 64 | * second couple of override parameters 65 | */ 66 | private final String param2name,param2value; 67 | /** 68 | * third couple of override parameters 69 | */ 70 | private final String param3name,param3value; 71 | 72 | static Logger log = Logger.getLogger("com.apifortress.jenkins.plugin.ApiFortressBuilder"); 73 | 74 | /** 75 | * Constructor 76 | * @param mode the mode 77 | * @param hook the Hook URL 78 | * @param id test id, automatch url or tag 79 | * @param blocking true if this is a blocking build step 80 | * @param dryrun true if you don't want to store the even on API Fortress 81 | * @param silent true if you don't want to trigger notifications 82 | * @param param1name override param 1 name 83 | * @param param1value override param 1 value 84 | * @param param2name override param 2 name 85 | * @param param2value override param 2 value 86 | * @param param3name override param 3 name 87 | * @param param3value override param 3 value 88 | */ 89 | @DataBoundConstructor 90 | public ApiFortressBuilder(String mode, String hook, String id, boolean blocking, boolean dryrun, 91 | boolean silent, 92 | String param1name, String param1value, 93 | String param2name, String param2value, 94 | String param3name, String param3value) { 95 | this.mode = mode; 96 | this.hook = hook; 97 | this.id = id; 98 | this.blocking = blocking; 99 | this.dryrun = dryrun; 100 | this.silent = silent; 101 | this.param1name = param1name; 102 | this.param1value = param1value; 103 | this.param2name = param2name; 104 | this.param2value = param2value; 105 | this.param3name = param3name; 106 | this.param3value = param3value; 107 | 108 | } 109 | 110 | 111 | public String getHook() { 112 | return hook; 113 | } 114 | public String getMode() { 115 | return mode; 116 | } 117 | 118 | public String getId(){ 119 | return id; 120 | } 121 | public boolean isBlocking(){ 122 | return blocking; 123 | } 124 | public boolean isDryrun(){ 125 | return dryrun; 126 | } 127 | public boolean isSilent(){ 128 | return silent; 129 | } 130 | 131 | public String getParam1name(){ 132 | return param1name; 133 | } 134 | public String getParam1value(){ 135 | return param1value; 136 | } 137 | 138 | public String getParam2name(){ 139 | return param2name; 140 | } 141 | public String getParam2value() { 142 | return param2value; 143 | } 144 | public String getParam3name(){ 145 | return param3name; 146 | } 147 | public String getParam3value(){ 148 | return param3value; 149 | } 150 | 151 | public boolean perform(AbstractBuild build, Launcher launcher, BuildListener listener) { 152 | StringBuilder sb = new StringBuilder(); 153 | sb.append("{ \"payload\":\"{}\",\"Content-Type\":\"application/json\","); 154 | if(getMode().equals("automatch")) 155 | sb.append("\"url\":\""+getId()+"\","); 156 | sb.append("\"params\":"+buildParams()); 157 | sb.append("}"); 158 | 159 | StringBuilder theUrl = new StringBuilder(); 160 | theUrl.append(getHook()+"/tests/"); 161 | if(getMode().equals("singleTest")) 162 | theUrl.append(id+"/run"); 163 | if(getMode().equals("automatch")) 164 | theUrl.append("automatch"); 165 | if(getMode().equals("tag")) 166 | theUrl.append("tag/"+id+"/run"); 167 | if(getMode().equals("project")) 168 | theUrl.append("run-all"); 169 | theUrl.append("?"); 170 | theUrl.append("sync="+isBlocking()); 171 | theUrl.append("&dryrun="+isDryrun()); 172 | theUrl.append("&silent="+isSilent()); 173 | String url = theUrl.toString(); 174 | boolean failed = true; 175 | InputStreamReader is = null; 176 | try { 177 | HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection(); 178 | connection.setRequestMethod("POST"); 179 | connection.setRequestProperty("Content-Type","application/json"); 180 | connection.setDoInput(true); 181 | connection.setDoOutput(true); 182 | DataOutputStream wr = new DataOutputStream(connection.getOutputStream()); 183 | wr.writeBytes(sb.toString()); 184 | wr.flush(); 185 | wr.close(); 186 | int responseCode = connection.getResponseCode(); 187 | if(responseCode==200) { 188 | is = new InputStreamReader(connection.getInputStream(),"UTF-8"); 189 | Object response = new JsonSlurper().parse(is); 190 | if (response instanceof ArrayList) { 191 | failed = hasFailures((ArrayList) response); 192 | } else { 193 | failed = hasFailures((Map) response); 194 | } 195 | is.close(); 196 | } else { 197 | listener.fatalError("API Fortress server responded with a "+responseCode+" status code"); 198 | log.warning("API Fortress server responded with a "+responseCode+" status code. No test executed"); 199 | } 200 | 201 | }catch(Exception e) { 202 | listener.fatalError("Error during API testing. Reason "+e.getMessage()); 203 | log.log(Level.SEVERE,"Error during API testing",e); 204 | } 205 | finally{ 206 | try { 207 | is.close(); 208 | } catch(Exception e){} 209 | } 210 | 211 | return (blocking && !failed) || (!blocking); 212 | } 213 | 214 | /** 215 | * Returns true when at least one of the tests has a failure 216 | * @param items an array of tests results 217 | * @return true if at least one test failed 218 | */ 219 | private boolean hasFailures(ArrayList items){ 220 | Iterator iterator = items.iterator(); 221 | while(iterator.hasNext()){ 222 | if(hasFailures(iterator.next())) 223 | return true; 224 | } 225 | return false; 226 | } 227 | 228 | /** 229 | * Returns true when the test result contains a failure 230 | * @param item a test result 231 | * @return true if the test failued 232 | */ 233 | private boolean hasFailures(Map item){ 234 | if(item.containsKey("code") && item.get("code").equals("ACCEPTED")) 235 | return false; 236 | return ((Integer)item.get("failuresCount")) > 0; 237 | } 238 | 239 | /** 240 | * Build a map of override parameters 241 | * @return a map of override parameters 242 | */ 243 | private String buildParams(){ 244 | HashMap items = new HashMap<>(); 245 | if(paramValid(param1name,param1value)) 246 | items.put(param1name,param1value); 247 | if(paramValid(param2name,param2value)) 248 | items.put(param2name,param2value); 249 | if(paramValid(param3name,param3value)) 250 | items.put(param3name,param3value); 251 | return JsonOutput.toJson(items); 252 | } 253 | 254 | /** 255 | * Validates a parameter name+value 256 | * @param paramName the parameter name 257 | * @param paramValue the parameter value 258 | * @return true if the parameter has been validated 259 | */ 260 | private static boolean paramValid(String paramName,String paramValue){ 261 | return paramName!=null&¶mName.length()>0&¶mValue!=null&¶mValue.length()>0; 262 | } 263 | 264 | @Override 265 | public DescriptorImpl getDescriptor() { 266 | return (DescriptorImpl)super.getDescriptor(); 267 | } 268 | 269 | @Extension 270 | public static final class DescriptorImpl extends BuildStepDescriptor { 271 | 272 | public DescriptorImpl() { 273 | load(); 274 | } 275 | 276 | public FormValidation doCheckHook(@QueryParameter String value) 277 | throws IOException, ServletException { 278 | if (value.length() == 0) 279 | return FormValidation.error("Please set a Hook URL"); 280 | if(value.length()>0) try{ 281 | new URL(value); 282 | }catch(Exception e){ 283 | FormValidation.error("Please enter a valid URL"); 284 | } 285 | return FormValidation.ok(); 286 | } 287 | public FormValidation doCheckId(@QueryParameter("id")String value, @QueryParameter("mode") String mode) 288 | throws IOException, ServletException { 289 | if(mode.equals("singleTest") || mode.equals("automatch") || mode.equals("tag")) 290 | if (value.length() == 0) 291 | return FormValidation.error("Please enter a valid test ID or automatch pattern"); 292 | return FormValidation.ok(); 293 | } 294 | 295 | public boolean isApplicable(Class aClass) { 296 | return true; 297 | } 298 | 299 | public String getDisplayName() { 300 | return "API Fortress API Testing"; 301 | } 302 | 303 | @Override 304 | public boolean configure(StaplerRequest req, JSONObject formData) throws FormException { 305 | return super.configure(req,formData); 306 | } 307 | } 308 | } 309 | 310 | --------------------------------------------------------------------------------