JIRA Hipchat integration plugin's REST call fails when called from some other plugin - jira-plugin

The JIRA HipChat integration plugin has this REST call which I am unable to invoke successfully from my plugin:
AJS.params.baseURL + "/rest/hipchat/compatibility/1.0/issuepanel/data/"+issueId+".json
This call can give me the parts of the HipChat Room URL I am looking for , and if I make it from REST Browser, or even if I paste this in the address bar of my browser, it still gives me the JSON. But I need to make this call from my plugin , and in that case it always errors out i.e. goes to the error: section of the AJAX call. Please provide your input on this - is there any restrcition in making that REST call from any other plugin?
Code wise, I am basically calling this function I created:
function getHipChatRoomURL(issueId) {
console.log("Invoking get hipchat room URL");
var hipChatRoomURL = "";
var urlToGetHipchatRoom = AJS.params.baseURL + "/rest/hipchat/compatibility/1.0/issuepanel/data/"+issueId+".json";
console.log("Attempting to get the hipchat room URL thru this REST Call " + urlToGetHipchatRoom);
AJS.$.ajax({
url: AJS.params.baseURL + "/rest/hipchat/compatibility/1.0/issuepanel/data/"+issueId+".json",
dataType: "application/json",
async:false,
success: function(response) {
var apiUrl = response.dedicatedRoom.apiUrl;
var roomId = response.dedicatedRoom.roomId;
hipChatRoomURL = apiUrl+"/chat/room/"+roomId;
hipChatRoomURL = "Join Hipchat Room";
console.log("Hipchat room URL In AJAX Call is " + hipChatRoomURL);
return hipChatRoomURL;
},
error: function() {
hipChatRoomURL = "No Hipchat room configured for this issue";
return hipChatRoomURL;
}
});
console.log("Get hipchat room URL invoked");
return hipChatRoomURL;
}
And invoking this method like this under AJS.toInit() :
setTimeout(function() {
var issueKey = AJS.Meta.get("issue-key");
AJS.log("ISSUE KEY SENT to find the room URL is " + issueKey)
hipChatRoomURL = getHipChatRoomURL(issueKey);
console.log("Hipchat room URL is " + hipChatRoomURL);
},1000);
But I am seeing that it always executes the error: section of the AJAX call above and returns "
No Hipchat room configured for this issue"
Please advise as soon as possible.

Related

Testing Coinbase API with Postman : pagination gives me error

I am testing the Coinbase API endpoints with Postman and the challenge is when I need to paginate
In order to setup Postman, I have followed the guide available here and in summary:
added variables
coinbase-api-base
coinbase-api-key
coinbase-api-secret
coinbase-api-timestamp
coinbase-api-signature
Added pre-request script in order to generate the request signature
// 1. Import crypto-js library
var CryptoJS = require("crypto-js");
// 2. Create the JSON request object var req = { timestamp: Math.floor(Date.now() / 1000), // seconds since Unix epoch method:
pm.request.method, path: pm.request.url.getPath(), body: '', // empty
for GET requests message: undefined, secret:
pm.collectionVariables.get("coinbase-api-secret"), // read value from
collection variable hmac: undefined, signature: undefined, };
// 3. Create the message to be signed req.message = req.timestamp + req.method + req.path + req.body;
// 4. Create HMAC using message and API secret req.hmac = CryptoJS.HmacSHA256(req.message, req.secret);
// 5. Obtain signature by converting HMAC to hexadecimal String req.signature = req.hmac.toString(CryptoJS.enc.Hex);
// 6. Log the request console.info("request: ", req);
// 7. Set Postman request's authentication headers for Coinbase REST API call pm.collectionVariables.set("coinbase-api-timestamp",
req.timestamp); pm.collectionVariables.set("coinbase-api-signature",
req.signature);
all worked well for a simple request such as:
GET {{coinbase-api-base}}/v2/accounts
then, if I add in the body request parameter (as explained here):
limit=50
to change the default pagination, I get an authentication error....
"errors": [
{ "id": "authentication_error",
"message": "invalid signature"
}
questions:
how can I fix it?
how the body of the request can play with the request signature...
any help suggestion is much appreciated
Thank you
Edit: the below being said, I'm not sure the base accounts API supports paging I could be wrong though, the CB docs are inconsistent to say the least. It does seem that the account history (ledger) and holds do though.
https://docs.cloud.coinbase.com/exchange/reference/exchangerestapi_getaccounts
get accounts function in Node.js API doesn't give an args param where the ledger does (see below):
getAccounts(callback) {
return this.get(['accounts'], callback);
}
Documentation for an api that does support paging, notice it gives you a query param section not available in the accounts documentation:
https://docs.cloud.coinbase.com/exchange/reference/exchangerestapi_getaccountledger
Looking at the node api, you still need to add the query string params to the body in order to sign:
calling function:
return this.get(
['accounts', accountID, 'ledger'],
{ qs: args },
callback
);
signing function:
let body = '';
if (options.body) {
body = JSON.stringify(options.body);
} else if (options.qs && Object.keys(options.qs).length !== 0) {
body = '?' + querystring.stringify(options.qs);
}
const what = timestamp + method.toUpperCase() + path + body;
const key = Buffer.from(auth.secret, 'base64');
const hmac = crypto.createHmac('sha256', key);
const signature = hmac.update(what).digest('base64');
return {
key: auth.key,
signature: signature,
timestamp: timestamp,
passphrase: auth.passphrase,
};
You can't add the limit to the body of the request, GET requests never includes any body.
You should add it as a query string parameter like (this is just an example):
GET {{coinbase-api-base}}/v2/accounts?limit=50

Google Apps Script/URLFetchApp and using returned data

I am very new to this, so please bear with me-- I have currently have an operational google apps script on the backend of a google sheet that is generated from Google Form answers. I am essentially setting up a ticket form in google forms that will trigger the data in the corresponding sheet to be sent via api call to our ticketing system. It works great, but I am trying to optimize it currently. The goal is to take the json response I get using:
Logger.log(response.getContentText());
which provides me the following info:
Aug 9, 2020, 11:44:40 AM Info {"_url":"https://testticketingsystem.com/REST/2.0/ticket/123456","type":"ticket","id":"123456"}
and send another API call to send data to that new ticket.
Here's a code snippet:
var payload = {
"Subject": String(su),
"Content": String(as),
"Requestor": String(em),
"Queue": String(qu),
"CustomFields": {"CustomField1": String(vn), "CustomField2": String(vb), "CustomField3":
String(vg), "CustomField4": String(av), "CustomField5": String(ov), "CustomField6":
String(sd)}
}
var options = {
'method': 'post',
"contentType" : "application/json",
'payload': JSON.stringify(payload),
'muteHttpExceptions': true
}
var url = "https://testticketingsystem.com/REST/2.0/ticket?token=****************";
var response = UrlFetchApp.fetch(url,options);
Logger.log(response.getContentText());
} catch (error) {
Logger.log(error.toString());
}
}
After the ticket is created, how do I script the use of that ID number as a variable into my next api call?
Thank you!
UrlFetchApp.fetch returns a HTTPResponse, and if you expect JSON then you should be able to just use JSON.parse() to create an object from the text. (The JSON object is a standard JavaScript global object like Math; it is not Google Apps Script specific.)
If all goes well, you should just be able to use
var response = UrlFetchApp.fetch(url,options);
var data = JSON.parse(response.getContentText());
var id = data.id;
and then use that id for your next fetch().
Notes
If your literal response is indeed
Aug 9, 2020, 11:44:40 AM Info {"_url":"https://testticketingsystem.com/REST/2.0/ticket/123456","type":"ticket","id":"123456"}
you will run into trouble as everything until the { is invalid JSON (use a linter if you need to check yourself). But I'm assuming that was added by the console when you logged JSON, and not in the actual response itself.
JSON.parse() throws an error with invalid JSON, so you can use try/catch if needed.
You can also check the headers before you try to JSON.parse().
Here's an example that checks and handles issues, should they arise.
var type = response.getHeaders()["Content-Type"];
var text = response.getContentText();
if (type === "application/json") {
try {
var data = JSON.parse(text);
} catch (error) {
return Logger.log("Invalid JSON: " + response.getContentText(text));
}
} else {
return Logger.log("expected JSON, but got response of type: " + type);
}
// if we get to this line, data is an object we can use

How to correctly handle the case where a stream is empty

How do I perform operations that have side effects in response to a stream being empty? For example, to handle the case where a client sends an empty body, and we read it with .bodyToMono(...).
See for example:
public Mono<ServerResponse> createEventDetails(ServerRequest request) {
return request.principal().flatMap(principal -> {
UserProfile userProfile = (UserProfile) ((UsernamePasswordAuthenticationToken) principal).getCredentials();
final String id = request.pathVariable("id");
final UUID eventId = UUID.fromString(id);
return request.bodyToMono(CreateEventDetailsRequest.class)
.map(body -> new CreateEventDetailsCommand(eventId, userProfile, body.getObjectId(), body.getDocumentUrls()))
.flatMap(command -> {
createEventDetailsCommandHandler.handle(command);
return ServerResponse
.created(URI.create(eventId.toString()))
.body(BodyInserters.fromObject(new CreateEventDetailsResponse(eventId)));
})
.switchIfEmpty(ServerResponse.badRequest().syncBody(new Error("missing request body for event id: " + id)))
.map(response -> {
LOG.error("POST createEventDetails: missing request body for event id: " + id);
return response;
});
});
}
This returns the desired 400 response in the case that the client omits the request body, but the log message is always printed, even for successful requests.
My best guess as to why this occurs, is that .switchIfEmpty(...).map(...) is executed as one unit by the framework, and then the result ignored.
How do I handle this case?
There are a few ways to do this. In the case above you can just add the logging to the switch
e.g.
.switchIfEmpty(ServerResponse.badRequest().syncBody(new Error("missing request body for event id: " + id))
.doOnNext(errorResponse -> LOG.error("POST createEventDetails: missing request body for event id: " + id)
)
another approach could be sending an Error signal back and in your handler catch and log it.
You can have a look here https://www.baeldung.com/spring-webflux-errors
Some light reading can be found here https://docs.spring.io/spring-framework/docs/5.0.0.BUILD-SNAPSHOT/javadoc-api/org/springframework/web/server/WebHandler.html
Use HttpWebHandlerAdapter to adapt a WebHandler to an HttpHandler. The
WebHttpHandlerBuilder provides a convenient way to do that while also
optionally configuring one or more filters and/or exception handlers.
You can view a sample handler in ResponseStatusExceptionHandler

Angular5 + HttpClient + Observable + HTTP PUT + 204 NO CONTENT

I have a webapi , which has HTTP PUT method with return result "204 No content" (if success). I try to use Angular5 + HttpClient + Observable to update data.
My code is :
updateUser(user): Observable<object> {
var putUrl = this.userURL + "/" + user.id;
var result = this.http.put(putUrl, user).pipe(
catchError(this.handleError)
);
return result;}
private handleError(error: HttpErrorResponse) {
if (error.error instanceof ErrorEvent) {
// A client-side or network error occurred. Handle it accordingly.
console.error('An error occurred:', error.error.message);
} else {
// The backend returned an unsuccessful response code.
// The response body may contain clues as to what went wrong,
console.error(
`Backend returned code ${error.status}, ` +
`body was: ${error.error}`);
}
// return an observable with a user-facing error message
return _throw(
'Something bad happened; please try again later.');
};
I clicked update button and there are no request to server in network tab, there are no console error. Where is am I wrong ?
UPDATE I so stupid, I'm sorry. I should subscibe() to the result
Stupid question, I was hurry.
Should : this.userService.updateUser(user).subscribe();
and all will work correctly.

File Upload Control in Domino

I need to post a file to Domino Server from a PhoneGap Application.
Here is the PhoneGap File Transfer example
// !! Assumes variable fileURI contains a valid URI to a text file on the device
var win = function(r) {
console.log("Code = " + r.responseCode);
console.log("Response = " + r.response);
console.log("Sent = " + r.bytesSent);
}
var fail = function(error) {
alert("An error has occurred: Code = " = error.code);
}
var options = new FileUploadOptions();
options.fileKey="file";
options.fileName=fileURI.substr(fileURI.lastIndexOf('/')+1);
options.mimeType="text/plain";
var params = new Object();
params.value1 = "test";
params.value2 = "param";
options.params = params;
var ft = new FileTransfer();
ft.upload(fileURI, "http://some.server.com/upload.php", win, fail, options);
//This is a PHP example - Domino would be like
// ft.upload(fileURI, "http://some.server.com/database.nsf/attachmentForm? createDocument", win, fail, options);
Does anyone know what needs to be done in Domino to get the file attachment that is being posted?
The easiest thing to do would be to create a form in Domino containing a File Upload control. You should be able to open the Domino form with a browser and see the generated html form that would normally be used. In there you will find all the info you need. This is of course dependent on the ft.upload method acting like an http multipart/form-data POST.