Using Artifactory's REST API to deploy jar file - groovy

Given this api documentation, how would I use HTTPBuilder and Groovy to construct my query? I've tried multiple things but I'm not getting it right.
def http = new HTTPBuilder()
http.request('http://artifactory:8888/libs-snapshot-local/my/jar/1.0/test-jar-1.0.jar', PUT, JSON ) { req ->
body = [
uri: "http://artifactory:8888/libs-snapshot-local/my/jar/1.0/test-jar-1.0.jar",
downloadUri: "http://artifactory:8888/libs-snapshot-local/my/jar/1.0/test-jar-1.0.jar",
repo: "libs-snapshot-local",
path: "c:\\pathtojarfile\\test.jar",
created: "2012-02-03T08:37:12.599-0800",
createdBy: "someuser",
size: "1024",
mimeType: "application/java-archive"
]
response.success = { resp, json ->
}
}
This seems to get me part way there, but it uploads an empty jar file. Seems like the body is completely ignored. Removing it produces the same result. I can't seem to find a good reference on how this is done.

The JSON in the mentioned documentation is actually Artifactory's response to the deployment request.
For deployment, Artifactroy requires only a simple PUT request, for example:
def restClient = new RESTClient('http://localhost:8080/artifactory/libs-release-local/')
restClient.auth.basic 'username', 'password'
restClient.encoder.'application/zip' = this.&encodeZipFile
def encodeZipFile(Object data) throws UnsupportedEncodingException {
def entity = new FileEntity((File) data, 'application/zip');
entity.setContentType('application/zip');
return entity
}
def response = restClient.put(path: 'org/artifact/1.0/artifact-1.0.jar',
body: new File('/path/to/local/artifact.jar'),
requestContentType: 'application/zip'
)

Related

fetch customerId for myApiClient

I am trying to fetch the customerId for myAPIClient using swift firebase and node.js. When I run my application it crashes and tells me that the customerId is nil. The reason that I am needing this customerId is in order to create the ephemeralKey.
The code that I use in node.js is,
exports.createEphemeralKey = functions.https.onRequest((req, res) => {
const stripe_version = req.body.api_version;
const customerId = req.body.customerId
if (!stripe_version) {
console.log('I did not see any api version')
res.status(400).end()
return;
}
stripe.ephemeralKeys.create(
{customer: customerId},
{stripe_version: apiVersion}
).then((key) => {
console.log("Ephemeral key: " + key)
res.status(200).json(key)
}).catch((err) => {
console.log('stripe version is ' + stripe_version + " and customer id is " + customerId + " for key: " + stripe_key + " and err is " + err.message )
res.status(500).json(err)
});
});
and the code that I am using inside of myApiClient is.
func createCustomerKey(withAPIVersion apiVersion: String, completion: #escaping STPJSONResponseCompletionBlock) {
let url = self.baseURL.appendingPathComponent("ephemeral_keys")
let defaults = UserDefaults.standard
let customerId = defaults.string(forKey: "customerId")
AF.request(url, method: .post, parameters: [
"api_version": apiVersion, "customer_id": customerId!
])
.validate(statusCode: 200..<300)
.responseJSON { responseJSON in
switch responseJSON.result {
case .success(let json):
completion(json as? [String: AnyObject], nil)
case .failure(let error):
completion(nil, error)
}
}
}
I have been stuck on creating this ephemeralKey for a while now and have asked other questions but have not gotten a real answer. Is there anything that I am missing? How can I actually access the customersId from stripe inside of my ios project.
You should first check that let customerId = defaults.string(forKey: "customerId") provides a valid customerId. You can do so by debugging your app and inspecting the value of customerId. Assuming that it is valid, I see a couple of things that may be causing issues for you:
The Alamofire library that you are using has 2 Parameter Encoders with different properties and options. You can see them here.
My hypothesis is that you are nor passing the customerId correclty to the body of the request. I suggest you start by trying the JSONParameterEncoder. Your request would be somehting like this:
AF.request(url, method: .post, parameters: [
"api_version": apiVersion, "customer_id": customerId!
],
encoder: JSONParameterEncoder.default)
It is possible that you also have to tweak the way your retrieve the customerID from the Cloud Function according to how you pass the info from the swift app. This docs may provide some insights on how to parse HTTP request info

uploaded files to Azure are corrupted when using dio

I'm trying to upload a file from my phone to azure blob storage as a BlockBlob with a SAS. I can get the file to upload, but it can't be opened once downloaded. The file gets corrupted somehow. I thought this was a content-type problem, but I have tried several different approaches to changing to content-type. Nothing has worked so far.
My code:
FileInfo _fileInfo = await filePicker(); // get the file path and file name
// my getUploadInfo fires a call to my backend to get a SAS.
// I know for a fact that this works because my website uses this SAS to upload files perfectly fine
UploadInfo uploadInfo = await getUploadInfo(_fileInfo.fileName, _fileInfo.filePath);
final bytes = File(_fileInfo.filePath).readAsBytesSync();
try {
final response = await myDio.put(
uploadInfo.url,
data: bytes,
onSendProgress:
(int sent, int total) {
if (total != -1) {
print((sent / total * 100).toStringAsFixed(0) + "%");
}
},
options:
dioPrefix.Options(headers: {
'x-ms-blob-type': 'BlockBlob',
'Content-Type': mime(_fileInfo.filePath),
})
);
} catch (e) {
print(e);
}
This code uploads a file just fine. But I can't open the file since it becomes corrupted. At first, I thought this was a Content-Type problem, so I've tried changing the content type header to: application/octet-stream and multipart/form-data as well. That doesn't work.
I've also tried to do
dioPrefix.FormData formData =
new dioPrefix.FormData.fromMap({
'file': await MultipartFile.fromFile(
_fileInfo.filePath,
filename: _fileInfo.fileName,
)
});
...
final response = await myDio.put(
uploadInfo.url,
data: formData, // This approach is recommended on the dio documentation
onSendProgress:
...
but this also corrupts the file. It gets uploaded, but I can't open it.
I have been able to successfully upload a file with this code, but with this approach I cannot get any type of response so I have no idea whether it uploaded successfully or not (Also, I can't get the progress of the upload):
try {
final data = imageFile.readAsBytesSync();
final response = await http.put( // here, response is empty no matter what i try to print
url,
body: data,
headers: {
'x-ms-blob-type': 'BlockBlob',
'Content-Type': mime(filePath),
});
...
Any help would be greatly appreciated. Thanks
I tried to upload a file using dio in Dart to Azure Blob Storage, and then download and print the content of the file, as the code below.
import 'package:dio/dio.dart';
import 'dart:io';
main() async {
var accountName = '<account name>';
var containerName = '<container name>';
var blobName = '<blob name>';
var sasTokenContainerLevel = '<container level sas token copied from Azure Storage Explorer, such as `st=2019-12-31T07%3A17%3A31Z&se=2020-01-01T07%3A17%3A31Z&sp=racwdl&sv=2018-03-28&sr=c&sig=xxxxxxxxxxxxxxxxxxxxxxxxxx`';
var url = 'https://$accountName.blob.core.windows.net/$containerName/$blobName?$sasTokenContainerLevel';
var data = File(blobName).readAsBytesSync();
var dio = Dio();
try {
final response = await dio.put(
url,
data: data,
onSendProgress:
(int sent, int total) {
if (total != -1) {
print((sent / total * 100).toStringAsFixed(0) + "%");
}
},
options: Options(
headers: {
'x-ms-blob-type': 'BlockBlob',
'Content-Type': 'text/plain',
})
);
print(response.data);
} catch (e) {
print(e);
}
Response response = await dio.get(url);
print(response.data);
}
Then, I ran it and got the result as the figure below.
The content of the uploaded file as blob is the json string encoded from a Uint8List bytes from the funtion readAsBytesSync.
I researched the description and the source code of dio, actually I found dio is only suitable for sending the request body of json format, not for raw content as request body.
Fig 1. The default transformer apply for POST method
Fig 2. https://github.com/flutterchina/dio/blob/master/dio/lib/src/transformer.dart
So to fix it is to write a custom transformer class PutTransformerForRawData instead of the default one to override the function transformRequest, as the code below.
import 'dart:typed_data';
class PutTransformerForRawData extends DefaultTransformer {
#override
Future<String> transformRequest(RequestOptions options) async {
if(options.data is Uint8List) {
return new String.fromCharCodes(options.data);
} else if(options.data is String) {
return options.data;
}
}
}
And to replace the default transformer via the code below.
var dio = Dio();
dio.transformer = PutTransformerForRawData();
Then, you can get the data via the code below.
var data = File(blobName).readAsBytesSync();
Or
var data = File(blobName).readAsStringSync();
Note: the custom transfer PutTransformerForRawData is only for uploading, please remove the download & print code Response response = await dio.get(url); print(response.data);, the default transformer seems to check the response body whether be json format, I got the exception as below when my uploaded file is my sample code.
Unhandled exception:
DioError [DioErrorType.DEFAULT]: FormatException: Unexpected character (at character 1)
import 'dart:typed_data';

HTTPBuilder & Session ID

I have the following code to connect to a REST API service, authenticate, retrieve a session ID then make further requests passing the session ID to authenticate. The initial request works and I get a HTTP 200 OK plus the session ID in the response, however when I try to make a second request passing the session ID in the header, I get
Caught: groovyx.net.http.HttpResponseException: Bad Request
I know the script can be written much better with the use of classes and try / catch etc. I am still learning both java and groovy so I start by just trying to do everything within the same class.
Any help much appreciated.
import groovyx.net.http.HTTPBuilder
import groovyx.net.http.URIBuilder
import static groovyx.net.http.Method.POST
import static groovyx.net.http.ContentType.*
def url = 'https://1.1.1.1/web_api/'
def uri = new URIBuilder(url)
String CHKPsid
uri.path = 'login'
def http = new HTTPBuilder(uri)
http.ignoreSSLIssues()
http.request(POST,JSON ) { req ->
headers.'Content-Type' = 'application/json'
body = [
"user":"username",
"password":"password"
]
response.success = { resp, json ->
println (json)
CHKPsid = (json.sid)
println "POST Success: ${resp.statusLine}"
}
}
uri.path = 'show-changes'
http.request(POST,JSON ) { req ->
headers.'Content-Type' = 'application/json'
headers.'X-chkp-sid' = '${CHKPsid}'
body = [
"from-date" : "2017-02-01T08:20:50",
"to-date" : "2017-10-21"
]
response.success = { resp, json ->
println (json)
println "POST Success: ${resp.statusLine}"
}
}
String interpolation does not work with single (or triple single quotes). When groovy will evaluate '${CHKPsid}' (single quotes), its value will be ${CHKPsid} (this string). In order to use the value of the variable, you should use double quotes: "${CHKPsid}" or simply just the variable: headers.'X-chkp-sid' = CHKPsid.
So the output of this:
String CHKPsid = "abc123"
println '${CHKPsid}'
println "${CHKPsid}"
will be:
${CHKPsid}
abc123
In order to quickly test what the server receives, you can use httpbin.org or requestb.in
So as well as the correct assignment of the value of the session ID, I found that calling the same HTTPbuilder - http.request the second time even with a change of uri, header and body was the problem. The listening server still saw this as part of the same login API call. My workaround / resolution was to define a 2nd HTTPbuilder with a different name and this now works. I'm interested to know if this is normal behaviour and how others approach this. Thanks.

SoapUI. Add multipart body part using groovy

I need to add json as multipart body part to "multipart/form-data" request using groovy.
I can do this using file attachments:
testRunner.testCase
.testSteps["/job/result"]
.getHttpRequest()
.attachBinaryData(json.toString().getBytes(), "application/json").contentID = "info"
My problem is "attachBinaryData" creates a temp file per request. It is not good for load tests :)
Is there other possibility to add body parts, without file attachments?
something like :
testRunner.testCase
.testSteps["/job/result"]
.getHttpRequest()
.addBodyPart("application/json", json.toString())
P.S. it must be the "add", because request has also one static attachment.
If you want to add a json to your request as a multipart/form-data using groovy script testStep you can use the follow code:
def jsonStr = "{'id':'test','someValue':'3'}"
def testStep = context.testCase.testSteps["/job/result"]
// set the content for the request
testStep.getHttpRequest().setRequestContent(jsonStr)
// and set the media type
testStep.testRequest.setMediaType('application/json')
// if you want to send as a multipart/form-data then use follow line instead
// testStep.testRequest.setMediaType('multipart/form-data')
This results in a request configured as follows:
Hope it helps,
Have found a solution using https://github.com/jgritman/httpbuilder
def http = new HTTPBuilder(serviceEndPoint)
def scanResultFile = new File(testRunner.testCase.getPropertyValue("ScanResultFile"))
http.request( POST ){ req ->
headers.'Connection' = 'Keep-Alive'
headers.'User-Agent' = 'SoapUI 4.5.1'
requestContentType = 'multipart/form-data'
ByteArrayBody bin = new ByteArrayBody(scanResultFile.readBytes(), "application/octet-stream", "jobResult");
StringBody info = new StringBody(testRunner.testCase.getPropertyValue("JsonScanResult"), "application/json", java.nio.charset.StandardCharsets.UTF_8);
MultipartEntity entity = new MultipartEntity()
entity.addPart("info", info);
entity.addPart("jobResult", bin)
req.entity = entity
}

Why is HTTPBuilder basic auth not working?

The following code doesn't authenticate the user (no authentication failure happens, but the call fails due to lack of permissions):
def remote = new HTTPBuilder("http://example.com")
remote.auth.basic('username', 'password')
remote.request(POST) { req ->
uri.path = "/do-something"
uri.query = ['with': "data"]
response.success = { resp, json ->
json ?: [:]
}
}
But the following works fine:
def remote = new HTTPBuilder("http://example.com")
remote.request(POST) { req ->
uri.path = "/do-something"
uri.query = ['with': "data"]
headers.'Authorization' =
"Basic ${"username:password".bytes.encodeBase64().toString()}"
response.success = { resp, json ->
json ?: [:]
}
}
Why isn't the first one working?
Two things that I can think of off the top of my head.
The .setHeaders method requires a map. Have you tried 'Authorization' : "Basic ${"username:password".bytes.encodeBase64().toString()}" ?
If not, It's a bit more work and code, but you could user the URIBuilder as well. Generally I encapsulate to a different class
protected final runGetRequest(String endpointPassedIn, RESTClient Client){
URIBuilder myEndpoint = new URIBuilder(new URI(Client.uri.toString() + endpointPassedIn))
HttpResponseDecorator unprocessedResponse = Client.get(uri: myEndpoint) as HttpResponseDecorator
def Response = unprocessedResponse.getData() as LazyMap
return Response
}
Hope this helps
Looks like your server isn't fully HTTPBuilder-compilant. It should return 401 code (which is standart behaviour for REST servers, but non-standart for others) for HTTPBuilder to catch this status and resend authentication request. Here is written about it.

Resources