io.signpath.signpathclient.api.http.SignPathApiHttpClient Maven / Gradle / Ivy
package io.signpath.signpathclient.api.http;
import com.squareup.moshi.JsonAdapter;
import com.squareup.moshi.Moshi;
import io.signpath.signpathclient.SignPathClient;
import io.signpath.signpathclient.SignPathClientException;
import io.signpath.signpathclient.api.model.SigningRequest;
import io.signpath.signpathclient.api.model.SigningRequestSubmitResponse;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import org.apache.commons.lang.Validate;
/**
*
* @author rober
*/
public class SignPathApiHttpClient {
public static final String BASE_URL = "https://app.signpath.io/API/";
private static final String ARTIFACT_PARAM_NAME = "Artifact";
private static final int NUMBER_OF_RETRIES = 4;
private OkHttpClient okHttpClient = new OkHttpClient();
private final String signPathApiBaseUrl;
public SignPathApiHttpClient(String signPathApiBaseUrl) {
this.signPathApiBaseUrl = signPathApiBaseUrl;
}
public SigningRequest getSigningRequestHttp(String ciUserToken,
String tbsToken,
String organizationId,
String signingRequestId) {
String url = "v1/{OrganizationId}/SigningRequests/{SigningRequestId}";
url = url.replace("{OrganizationId}", organizationId);
url = url.replace("{SigningRequestId}", signingRequestId);
Request request = new Request.Builder()
.url(this.signPathApiBaseUrl + url)
.addHeader("Authorization", this.buildAuthorizationHeaderValue(ciUserToken, tbsToken))
.build();
okhttp3.Call call = okHttpClient.newCall(request);
okhttp3.Response response = SignPathApiHttpClient.executeWithRetryOkHttp(call);
SigningRequest signingRequest = new SigningRequest();
if (response.isSuccessful() && response.body() != null) {
try {
Moshi moshi = new Moshi.Builder().build();
JsonAdapter jsonAdapter = moshi.adapter(SigningRequest.class).lenient();
signingRequest = jsonAdapter.fromJson(response.body().string());
} catch (IOException ex) {
Logger.getLogger(SignPathClient.class.getName()).log(Level.SEVERE, null, ex);
}
}
return signingRequest;
}
public String submitSigningRequestHttp(
String ciUserToken,
String tbsToken,
String organizationId,
File artifact,
String projectSlug,
String signingPolicySlug,
String artifactConfigurationSlug,
String description,
Map origin) {
String url = "v1/{OrganizationId}/SigningRequests";
url = url.replace("{OrganizationId}", organizationId);
MultipartBody.Builder requestBuilder = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart(ARTIFACT_PARAM_NAME, artifact.getName(),
RequestBody.create(MediaType.parse("application/octet-stream"), artifact))
.addFormDataPart("ProjectSlug", projectSlug)
.addFormDataPart("SigningPolicySlug", signingPolicySlug);
if (artifactConfigurationSlug != null && !artifactConfigurationSlug.isEmpty()) {
requestBuilder.addFormDataPart("ArtifactConfigurationSlug", artifactConfigurationSlug);
}
if (description != null && !description.isEmpty()) {
requestBuilder.addFormDataPart("Description", description);
}
if (origin != null) {
String prefix = "Origin.";
for (String key : origin.keySet()) {
String value = origin.get(key);
RequestBody originRequestBody;
if (value.startsWith("@")) {
String filePath = value.substring(1);
requestBuilder.addFormDataPart(prefix + key, ARTIFACT_PARAM_NAME,
RequestBody.create(MediaType.parse("application/octet-stream"), new File(filePath)));
} else {
requestBuilder.addFormDataPart(prefix + key, value);
}
}
}
RequestBody requestBody = requestBuilder.build();
Request request = new Request.Builder()
.url(this.signPathApiBaseUrl + url)
.addHeader("Authorization", this.buildAuthorizationHeaderValue(ciUserToken, tbsToken))
.post(requestBody)
.build();
okhttp3.Call call = okHttpClient.newCall(request);
okhttp3.Response response = SignPathApiHttpClient.executeWithRetryOkHttp(call);
SigningRequestSubmitResponse signingRequestSubmitResponse = new SigningRequestSubmitResponse();
if (response.isSuccessful() && response.body() != null) {
try {
Moshi moshi = new Moshi.Builder().build();
JsonAdapter jsonAdapter = moshi.adapter(SigningRequestSubmitResponse.class).lenient();
signingRequestSubmitResponse = jsonAdapter.fromJson(response.body().string());
} catch (IOException ex) {
Logger.getLogger(SignPathClient.class.getName()).log(Level.SEVERE, null, ex);
}
}
return signingRequestSubmitResponse.getSigningRequestId();
}
public void downloadSignedArtifactHttp(
String ciUserToken,
String tbsToken,
String organizationId,
String signingRequestId,
File artifactTargetFile) {
String url = "v1/{OrganizationId}/SigningRequests/{SigningRequestId}/SignedArtifact";
url = url.replace("{OrganizationId}", organizationId);
url = url.replace("{SigningRequestId}", signingRequestId);
Request request = new Request.Builder()
.url(this.signPathApiBaseUrl + url)
.addHeader("Authorization", this.buildAuthorizationHeaderValue(ciUserToken, tbsToken))
.build();
okhttp3.Call call = okHttpClient.newCall(request);
okhttp3.Response response = SignPathApiHttpClient.executeWithRetryOkHttp(call);
try ( InputStream stream = response.body().byteStream(); FileOutputStream outStream = new FileOutputStream(artifactTargetFile);) {
copyStream(stream, outStream);
outStream.close();
} catch (IOException ex) {
Logger.getLogger(SignPathClient.class.getName()).log(Level.SEVERE, null, ex);
throw new SignPathClientException("Artifact download error", ex);
}
}
private static okhttp3.Response executeWithRetryOkHttp(okhttp3.Call call) throws SignPathClientException {
int numberOfTries = 0;
int delayInSeconds = 1;
okhttp3.Response lastResponse;
Exception lastException = null;
while (numberOfTries <= NUMBER_OF_RETRIES) {
try {
// make sure we don't call execute more than once per call object
lastResponse = call.clone().execute();
if (!IsTransientHttpError(lastResponse.code())) {
// the response is either successful or not transient
// that's why there is no sense in trying again
// we return whatever we have
if (!lastResponse.isSuccessful()) {
throw new SignPathClientException(
"The SignPath API call cannot be completed due to the following error: "
+ "Response Code: " + lastResponse.code() + " - " + lastResponse.body().string());
}
return lastResponse;
}
} catch (IOException ex) {
lastException = ex;
Logger.getLogger(SignPathClient.class.getName()).log(Level.SEVERE, "encountered io exception -> retry triggered", ex);
}
try {
Thread.sleep(delayInSeconds * 1000);
} catch (InterruptedException ex) {
Logger.getLogger(SignPathClient.class.getName()).log(Level.SEVERE, null, ex);
}
numberOfTries++;
delayInSeconds *= 3; // each try increases the delay three times
}
throw new SignPathClientException("SignPath API communication error.", lastException);
}
private String buildAuthorizationHeaderValue(String ciUserToken, String tbsToken) {
Validate.notEmpty(ciUserToken);
//Validate.notEmpty(tbsToken);
if (tbsToken == null || tbsToken.isEmpty()) {
return String.format("Bearer %s", ciUserToken);
}
return String.format("Bearer %s:%s", ciUserToken, tbsToken);
}
private static boolean IsTransientHttpError(int httpResponseCode) {
return httpResponseCode == 408 // Request Timeout
|| (httpResponseCode >= 500 && httpResponseCode < 600);
}
private void copyStream(InputStream input, OutputStream output) throws IOException {
byte[] buf = new byte[8192];
int length;
while ((length = input.read(buf)) > 0) {
output.write(buf, 0, length);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy