Skip to content

Commit

Permalink
feat: Add support for downloading resolved definitions (#27)
Browse files Browse the repository at this point in the history
  • Loading branch information
Anders Jaensson authored May 16, 2023
1 parent 0143172 commit 9ab3427
Show file tree
Hide file tree
Showing 6 changed files with 117 additions and 17 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ Parameter | Description | Required | Default
**`protocol`** | Protocol for SwaggerHub API,`http` or `https` | false | `https`
**`port`** | Port to access SwaggerHub API| false | `443`
**`oas`** | Version of the OpenApi Specification the definition adheres to | false | `2.0`

**`resolved`** | Download a resolved version of the API definition | false | `false`
***

### swaggerhubUpload
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ private Request buildPostRequest(HttpUrl httpUrl, MediaType mediaType, String co
private HttpUrl getDownloadUrl(SwaggerHubRequest swaggerHubRequest) {
return getBaseUrl(swaggerHubRequest.getOwner(), swaggerHubRequest.getApi())
.addEncodedPathSegment(swaggerHubRequest.getVersion())
.addQueryParameter("resolved", String.valueOf(swaggerHubRequest.resolved()))
.build();
}

Expand Down
12 changes: 12 additions & 0 deletions src/main/java/io/swagger/swaggerhub/client/SwaggerHubRequest.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ public class SwaggerHubRequest {
private final String swagger;
private final String oas;
private final boolean isPrivate;
private final boolean resolved;

public String getApi() {
return api;
Expand Down Expand Up @@ -36,6 +37,10 @@ public boolean isPrivate() {
return isPrivate;
}

public boolean resolved() {
return resolved;
}

private SwaggerHubRequest(Builder builder) {
this.api = builder.api;
this.owner = builder.owner;
Expand All @@ -44,6 +49,7 @@ private SwaggerHubRequest(Builder builder) {
this.swagger = builder.swagger;
this.isPrivate = builder.isPrivate;
this.oas = builder.oas;
this.resolved = builder.resolved;
}

public static class Builder {
Expand All @@ -54,6 +60,7 @@ public static class Builder {
private String swagger;
private String oas;
private boolean isPrivate;
private boolean resolved;

public Builder(String api, String owner, String version) {
this.api = api;
Expand Down Expand Up @@ -81,6 +88,11 @@ public Builder oas(String oas) {
return this;
}

public Builder resolved(Boolean resolved) {
this.resolved = resolved;
return this;
}

public SwaggerHubRequest build() {
return new SwaggerHubRequest(this);
}
Expand Down
17 changes: 15 additions & 2 deletions src/main/java/io/swagger/swaggerhub/tasks/DownloadTask.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ public class DownloadTask extends DefaultTask {
private String host = "api.swaggerhub.com";
private Integer port = 443;
private String protocol = "https";
private Boolean resolved = false;

@Input
public String getOwner() {
Expand Down Expand Up @@ -119,15 +120,27 @@ public void setFormat(String format) {
this.format = format;
}

@Input
@Optional
public Boolean getResolved() {
return resolved;
}

public void setResolved(Boolean resolved) {
this.resolved = resolved;
}


@TaskAction
public void downloadDefinition() throws GradleException {
SwaggerHubClient swaggerHubClient = new SwaggerHubClient(host, port, protocol, token);

LOGGER.info("Downloading from {}: api: {}, owner: {}, version: {}, format: {}, outputFile: {}",
host, api, owner, version, format, outputFile);
LOGGER.info("Downloading from {}: api: {}, owner: {}, version: {}, format: {}, resolved: {}, outputFile: {}",
host, api, owner, version, format, resolved, outputFile);

SwaggerHubRequest swaggerHubRequest = new SwaggerHubRequest.Builder(api, owner, version)
.format(format)
.resolved(resolved)
.build();

try {
Expand Down
101 changes: 87 additions & 14 deletions src/test/java/io/swagger/swaggerhub/gradle/SwaggerHubDownloadTest.java
Original file line number Diff line number Diff line change
@@ -1,56 +1,129 @@
package io.swagger.swaggerhub.gradle;

import com.github.tomakehurst.wiremock.client.WireMock;
import com.github.tomakehurst.wiremock.junit.WireMockRule;
import org.apache.commons.io.FileUtils;
import org.gradle.testkit.runner.BuildResult;
import org.gradle.testkit.runner.GradleRunner;
import org.hamcrest.MatcherAssert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;

import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
import static com.github.tomakehurst.wiremock.client.WireMock.anyUrl;
import static com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor;
import static com.github.tomakehurst.wiremock.client.WireMock.stubFor;
import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;
import static java.nio.charset.StandardCharsets.UTF_8;
import static junit.framework.TestCase.assertTrue;
import static org.gradle.testkit.runner.TaskOutcome.SUCCESS;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;

public class SwaggerHubDownloadTest {
private static final String DOWNLOAD_TASK = "swaggerhubDownload";
@Rule
public WireMockRule wireMockRule = new WireMockRule(wireMockConfig().dynamicPort());

@Rule
public final TemporaryFolder testProjectDir = new TemporaryFolder();
private File buildFile;
private String filePath;
private Path outputFile;

@Before
public void setup() throws IOException {
buildFile = testProjectDir.newFile("build.gradle");
outputFile = Paths.get(testProjectDir.getRoot().toString(), "testAPI.json");
filePath = outputFile.toString().replace("\\", "/");
}

@Test
public void testSwaggerHubDownloadTask() throws IOException {
Path outputFile = Paths.get(testProjectDir.getRoot().toString(), "testAPI.json");
String filePath = outputFile.toString().replace("\\", "/");
stubFor(WireMock.get(urlPathEqualTo("/apis/swagger-hub/test-api/1.0.0"))
.willReturn(aResponse().withBodyFile("TestAPI.json")));
String buildFileContent = "plugins { id 'io.swagger.swaggerhub' }\n" +
DOWNLOAD_TASK + " {\n" +
" protocol 'http'\n" +
" host 'localhost'\n" +
" port " + wireMockRule.port() + "\n" +
" api 'test-api'\n" +
" owner 'swagger-hub'\n" +
" version '1.0.0'\n" +
" outputFile '" + filePath + "'\n" +
"}";

Files.write(buildFile.toPath(), buildFileContent.getBytes());

BuildResult result = executeTask();

assertEquals(SUCCESS, result.task(":" + DOWNLOAD_TASK).getOutcome());
assertTrue(Files.exists(outputFile));
assertThat(FileUtils.readFileToString(outputFile.toFile(), UTF_8), containsString("This is a simple API"));
}

@Test
public void downloadsUnresolvedByDefault() throws IOException {
stubFor(WireMock.get(anyUrl()).willReturn(WireMock.ok()));

String buildFileContent = "plugins { id 'io.swagger.swaggerhub' }\n" +
DOWNLOAD_TASK + " {\n" +
" protocol 'http'\n" +
" host 'localhost'\n" +
" port " + wireMockRule.port() + "\n" +
" api 'test-api'\n" +
" owner 'swagger-hub'\n" +
" version '1.0.0'\n" +
" outputFile '" + filePath + "'\n" +
"}\n";

Files.write(buildFile.toPath(), buildFileContent.getBytes());

executeTask();

WireMock.verify(getRequestedFor(urlEqualTo("/apis/swagger-hub/test-api/1.0.0?resolved=false")));
}

@Test
public void supportsResolvedFlag() throws IOException {
stubFor(WireMock.get(anyUrl()).willReturn(WireMock.ok()));

String downloadTask = "swaggerhubDownload";
String buildFileContent = "plugins { id 'io.swagger.swaggerhub' }\n" +
downloadTask + " {\n" +
" api 'PetStoreAPI'\n" +
" owner 'jsfrench'\n" +
" version '1.0.0'\n" +
" outputFile '" + filePath + "'\n" +
"}";
DOWNLOAD_TASK + " {\n" +
" protocol 'http'\n" +
" host 'localhost'\n" +
" port " + wireMockRule.port() + "\n" +
" api 'test-api'\n" +
" owner 'swagger-hub'\n" +
" version '1.0.0'\n" +
" outputFile '" + filePath + "'\n" +
" resolved true\n" +
"}\n";

Files.write(buildFile.toPath(), buildFileContent.getBytes());

BuildResult result = GradleRunner.create()
executeTask();

WireMock.verify(getRequestedFor(urlEqualTo("/apis/swagger-hub/test-api/1.0.0?resolved=true")));
}

private BuildResult executeTask() {
return GradleRunner.create()
.withPluginClasspath()
.withProjectDir(testProjectDir.getRoot())
.withArguments(downloadTask, "--stacktrace")
.withArguments(DOWNLOAD_TASK, "--stacktrace")
.build();

assertEquals(SUCCESS, result.task(":" + downloadTask).getOutcome());
assertTrue(Files.exists(outputFile));
}
}
1 change: 1 addition & 0 deletions src/test/resources/__files/TestAPI.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"swagger":"2.0","info":{"description":"This is a simple API","version":"1.0.0","title":"Test API","contact":{"email":"you@your-company.com"},"license":{"name":"Apache 2.0","url":"http://www.apache.org/licenses/LICENSE-2.0.html"}},"tags":[{"name":"admins","description":"Secured Admin-only calls"},{"name":"developers","description":"Operations available to regular developers"}],"paths":{"/inventory":{"get":{"tags":["developers"],"summary":"searches inventory","operationId":"searchInventory","description":"By passing in the appropriate options, you can search for\navailable inventory in the system\n","produces":["application/json"],"parameters":[{"in":"query","name":"searchString","description":"pass an optional search string for looking up inventory","required":false,"type":"string"},{"in":"query","name":"skip","description":"number of records to skip for pagination","type":"integer","format":"int32","minimum":0},{"in":"query","name":"limit","description":"maximum number of records to return","type":"integer","format":"int32","minimum":0,"maximum":50}],"responses":{"200":{"description":"search results matching criteria","schema":{"type":"array","items":{"$ref":"#/definitions/InventoryItem"}}},"400":{"description":"bad input parameter"}}},"post":{"tags":["admins"],"summary":"adds an inventory item","operationId":"addInventory","description":"Adds an item to the system","consumes":["application/json"],"produces":["application/json"],"parameters":[{"in":"body","name":"inventoryItem","description":"Inventory item to add","schema":{"$ref":"#/definitions/InventoryItem"}}],"responses":{"201":{"description":"item created"},"400":{"description":"invalid input, object invalid"},"409":{"description":"an existing item already exists"}}}}},"definitions":{"InventoryItem":{"type":"object","required":["id","name","manufacturer","releaseDate"],"properties":{"id":{"type":"string","format":"uuid","example":"d290f1ee-6c54-4b01-90e6-d701748f0851"},"name":{"type":"string","example":"Widget Adapter"},"releaseDate":{"type":"string","format":"int32","example":"2016-08-29T09:12:33.001Z"},"manufacturer":{"$ref":"#/definitions/Manufacturer"}}},"Manufacturer":{"required":["name"],"properties":{"name":{"type":"string","example":"ACME Corporation"},"homePage":{"type":"string","format":"url","example":"https://www.acme-corp.com"},"phone":{"type":"string","example":"408-867-5309"}}}}}

0 comments on commit 9ab3427

Please sign in to comment.