Accessing Sharepoint Project web app REST from separate app - javascript

I need to connect to our corporate PWA. This is the code I'm using:
// var endpointUrl = 'https://<companySite>.sharepoint.com/sites/pwa/_api/web/lists';
var endpointUrl = 'https://<companySite>.sharepoint.com/sites/pwa/_api/ProjectData/Projects?$select=ProjectName';
var xhr = new XMLHttpRequest();
xhr.open("GET", endpointUrl);
// The APIs require an OAuth access token in the Authorization header, formatted like this: 'Authorization: Bearer <token>'.
xhr.setRequestHeader("Authorization", "Bearer " + token);
xhr.setRequestHeader("Accept", "application/json");
$("#header").html("Requesting: " + endpointUrl);
// Process the response from the API.
xhr.onload = function () {
if (xhr.status == 200) {
var formattedResponse = JSON.stringify(JSON.parse(xhr.response), undefined, 2);
$("#results").html("<pre>" + formattedResponse + "</pre>");
} else {
$("#results").html("HTTP " + xhr.status + "<br>" + xhr.response);
}
}
// Make request.
xhr.send();
I've tried also a few different ways, all using Bearer token.
The problem is that this code works for accessing https://<companySite>.sharepoint.com/sites/pwa/_api/web/lists but doesn't for https://<companySite>.sharepoint.com/sites/pwa/_api/ProjectData/Projects?$select=ProjectName
For the latter it returns:
{"odata.error":{"code":"20010, Microsoft.ProjectServer.PJClientCallableException","message":{"lang":"en-US","value":"GeneralSecurityAccessDenied"}}}
What could be the possible problem?
I know that my token is correct, as it works for accessing */web/lists. I also know that the url is correct, as I can open it in my browser (providing that I'm logged in into sharepoint)

You need to use a FormDigestValue.
Make a GET call to .../_api/contextinfo and store the value of 'FormDigestValue'. Then for all your other calls, add a header of X-RequestDigest: <FormDigestValue>

Related

Client receive different http code from what the server had response

I had this weird going on.
I'm using Jersey for API end-point, HTML and javascript front end. When I authenticate user, server will response 303 to tell the client the user is authenticated and to redirect to the said page (based on user group which the user belongs to), which I put it in Location header.
But when my server response with code 303, the client (html with pure javascript in this case) receive code of 200. But if I change the code to something else, let say 401, the client receive it correctly. This happened only if server response with code 303.
Why I return 303? I'm not pretty sure myself, thought it is the right way to do it, I might just return 200, but I try to do it the proper way as much as I know and can. But that is for another time, suggestion are welcome.
And even when I try to receive the Location from header and token from cookies, it return null. As if something happened to the response from server to client. I don't have anything that change response.
Looking at the application log, everything went fine, nothing miss behave.
It was working fine, but suddenly this weird behavior happened. I already clear browser cache, clean build and deploy the app, restart tomcat, and even restart my dev machine, nothing solve it.
I not found anything related with google. My way of code things might not the right way as I'm quite new with REST.
My javascript:
let xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if(xhr.status === 200) {
console.log("Login API return 200");
console.log("Token: " + xhr.getResponseHeader("Token"));
} else if (xhr.status === 303) {
console.log("Status 303 with response text " + xhr.responseText);
console.log("Status 303 with redirect header of " + xhr.getResponseHeader("redirect"));
// redirect user to the page
window.location.href = xhr.getResponseHeader("Location");
} else if (xhr.status === 401) {
console.log('Unauthorized access to server.');
document.getElementById("expired").style.display = "none";
document.getElementById("credential").style.display = "block";
} else {
console.log("Returned status is " + xhr.status);
console.log("Response text is " + xhr.responseText);
}
}
};
xhr.open('POST', URL + "/api/v1/users/login");
xhr.setRequestHeader("authorization", authHeader);
xhr.send();
My java:
#POST
#Path("/login")
#Produces(MediaType.TEXT_PLAIN)
public Response authUser(#Context HttpHeaders httpHeaders,
#Context HttpServletRequest request) {
log.debug("Server name : " + request.getServerName());
String authHeader = httpHeaders.getRequestHeader("authorization").get(0);
String encodeAuth = authHeader.substring(authHeader.indexOf(' ') + 1);
String decodeAuth = new String(Base64.getDecoder().decode(encodeAuth));
String username = decodeAuth.substring(0, decodeAuth.indexOf(':'));
String password = decodeAuth.substring(decodeAuth.indexOf(':') + 1);
User user = new User(username);
log.debug("Username : " + username);
log.debug("Password : " + password);
// Check user
if (!user.login(username,password)) {
// This is a response for the ajax for unauthorized login
log.warn("Can not authenticate user, either wrong username or password.");
// Return Unauthorized response
return Response
.status(401)
.entity("Unauthorized")
.build();
}
log.debug("Authentication success. Proceed with token and cookie creation");
// Create token
String token = TokenStore.getInstance().putToken(username);
// Get user's group
String userGroup = TokenStore.getInstance().getGroup(token);
log.debug("Token created for the user is " + token);
// Create cookie
NewCookie cookie = new NewCookie("Token",token,"/app/", request.getServerName(),"User token",1800,false);
/* Create redirect URL.
* uriInfo.getBaseUriBuilder() will return http://<host>:<port>/app/api/ which is not
* desired in this scenario, the below is to eliminate /api/ from the URI
* to get only http://<host>:<port>/app
*/
String uri = uriInfo.getBaseUriBuilder().toString();
// nthLastIndexOf(2, "/", uri) is a helper method to get the nth occurrence of a character in a string, from the last character.
int secondLast = nthLastIndexOf(2, "/", uri);
String base = uri.substring(0,secondLast);
String redirect = "";
if(null != userGroup) switch (userGroup) {
case "groupA":
redirect = base + "/pageA";
break;
case "groupB":
redirect = base + "/pageB";
break;
case "groupC":
redirect = base + "/pageC";
break;
default:
break;
}
URI re = null;
log.debug("Created URI for redirect is " + redirect);
try {
re = new URI(redirect);
} catch (URISyntaxException syntax) {
log.error("Cannot convert string to URI", syntax);
} catch (Exception e) {
log.error("Cannot convert string to URI", e);
}
log.debug("Return response 303.");
// Return redirect response
return Response
.status(303)
.entity(token)
.header("Location", redirect)
.cookie(cookie)
.build();
}

I could not acquire the image from the API of spotify

I'm trying to create a site in which, through the Spotify Web API, I display the information of a certain element (Artist, Track, Album, etc ...). In the background of the object of the answer, however, I would like to set the image that is provided directly by Spotify but, after several attempts, I still can not.
This is my function.
function ricercaArtista(){
var xhr = new XMLHttpRequest();
var artist =document.getElementById("artista").value;
xhr.onreadystatechange = function() {
if (xhr.readyState == 4 && xhr.status == 200) {
var response = JSON.parse(xhr.responseText);
var result = '';
for(var i = 0; i < response.artists.items.length; i++){
console.log(response.artists.items[i]);
result +='<div class="panel panel-primary" style="background:url('+
response.artists.items[i].images[1].url+');"><div class="panel-body">' +
'name : ' + response.artists.items[i].name + '<br/>' +
'popularity : ' + response.artists.items[i].popularity + '<br/>' +
'type : ' + response.artists.items[i].type + '</div></div>';
}
alert
document.getElementById("artists").innerHTML = result;
}
};
xhr.open('GET', "https://api.spotify.com/v1/search?q="+artist+"&type=artist&market=IT&limit=10&offset=5", true);
xhr.setRequestHeader('Accept', 'application/json');
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.setRequestHeader('Authorization', 'Bearer BQAnHZ_1kZFp_6rNx7jWXz-wfK9KTp2gTmuviXisgsJy8IAjnF_Hbo701Y5UMu7viFb0vaKG6wBAcLQMhfNUBjzGZpt1M3UaWGEKWDVmziEh-s6ECFNeVFCifdD3C38w3q_jGdnovDUlek2f463hnyPUlpoC4xb2uA');
xhr.send();
}
the error of the Console of Chrome is:
Index.html:29 Uncaught TypeError: Cannot read property 'url' of undefined at XMLHttpRequest.xhr.onreadystatechange
#Pointy is right, some the records has an empty array on Image path.
Look at the fetch below it returns different records to search Bob marley.
you can see the result in console.
in this case you need to check image path if it's empty Do something.
fetch("https://api.spotify.com/v1/search?q=bob marley&type=artist&market=IT&limit=10&offset=5")
.then(res=> res.json())
.then(data=> console.log(data.artists.items))
.catch(err=> console.log(err));

Javascript serialized POST

I am trying to achieve that when I call the JS function, a post request is send. In my browser I would send:
http://myuser:password#hc2:80/api/callAction?deviceID=185&name=turnOn
This works. Yet in my code it doesn't.
Important to note:
- Chrome does raise an Error: Request doesn't pass access control. If I disable this in Chrome, I doesn't display this error (yet no response from the server either).
<script type="text/javascript">
function changestate() {
var http = new XMLHttpRequest();
http.withCredentials = true;
var user = "bassie"
var pass = "password"
var url = "http://hc2/api/callAction";
var params = "deviceID=185&name=turnOff";
http.open("POST", url, true);
http.setRequestHeader("Authorization", "Basic " + user + ":" + pass);
//Send the proper header information along with the request
http.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
alert(http.responseText);
http.onreadystatechange = function() {//Call a function when the state changes.
if(http.readyState == 4 && http.status == 200) {
alert(http.responseText);
}
}
http.send(params);
}
</script>
The equivalent to putting the URL in the browser's location is a GET request, not POST.
Since you're sending a cross-domain request, you won't be able to read the response (unless you relay through a proxy on your origin server). So you can't read http.responseText, and can simply omit the onreadystatechange function; you'll just have to assume it
function changestate() {
var http = new XMLHttpRequest();
http.withCredentials = true;
var user = "bassie"
var pass = "password"
var url = "http://hc2/api/callAction";
var params = "deviceID=185&name=turnOff";
http.open("GET", url + "?" + params, true);
http.setRequestHeader("Authorization", "Basic " + user + ":" + pass);
http.send();
}
Eventually ended up creating a sort of like proxy. This was the main component. Not in the example (My script gets the HTTP requested) and gets the output. Below the gist of it:
req = urllib.request.Request('http://hc2:80/api/callAction?param1=1&param2=2')
credentials = ('%s:%s' % ('user', 'password'))
encoded_credentials = base64.b64encode(credentials.encode('ascii'))
req.add_header('Authorization', 'Basic %s' %
encoded_credentials.decode("ascii"))
response = urllib.request.urlopen(req)

Dynamics Crm Invoking Workflow from external source

Scenario:
I would like to invoke an already defined workflow or a custom action from a web page which is located outside the CRM Dynamics context. (Let's say MS CRM 2011-2013-2015-2016 and 365)
My solution:
My idea would be about defining a kind of controller page into the CRM context accessible from the web and execute the rest call within that page (through javascript).
This page will be able to read input parameters and execute the right rest call.
Does it make sense? Could you suggest a better implementation?
Thanks in advance!
If you have the resources, you can setup a service utilizing the following methods and then ajax it.
private static void ExecuteWorkflow(Guid workflowId, Guid entityId)
{
try
{
string url = ConfigurationManager.ConnectionStrings["crm"].ConnectionString;
ClientCredentials cc = new ClientCredentials();
cc.Windows.ClientCredential = CredentialCache.DefaultNetworkCredentials;
OrganizationServiceProxy _service = new OrganizationServiceProxy(new Uri(url), null, cc, null);
ExecuteWorkflowRequest request = new ExecuteWorkflowRequest()
{
WorkflowId = workflowId,
EntityId = entityId
};
ExecuteWorkflowResponse r = (ExecuteWorkflowResponse)_service.Execute(request);
_service.Dispose();
}
catch (Exception ex)
{
//Handle Exception
}
}
If you're unable to have the service on the same domain as the CRM server, you should be able to impersonate.
cc.Windows.ClientCredential.Domain = "DOMAIN";
cc.Windows.ClientCredential.Password = "PASSWORD";
cc.Windows.ClientCredential.UserName = "USERNAME";
You can find more details here.
https://msdn.microsoft.com/en-us/library/microsoft.crm.sdk.messages.executeworkflowrequest.aspx
You can invoke a workflow in a js like this:
You can query the workflowId by its name and the type definition.
var entityId = // The GUID of the entity
var workflowId = // The GUID of the workflow
var url = // Your organization root
var orgServicePath = "/XRMServices/2011/Organization.svc/web";
url = url + orgServicePath;
var request;
request = "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\">" +
"<s:Body>" +
"<Execute xmlns=\"http://schemas.microsoft.com/xrm/2011/Contracts/Services\" xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\">" +
"<request i:type=\"b:ExecuteWorkflowRequest\" xmlns:a=\"http://schemas.microsoft.com/xrm/2011/Contracts\" xmlns:b=\"http://schemas.microsoft.com/crm/2011/Contracts\">" +
"<a:Parameters xmlns:c=\"http://schemas.datacontract.org/2004/07/System.Collections.Generic\">" +
"<a:KeyValuePairOfstringanyType>" +
"<c:key>EntityId</c:key>" +
"<c:value i:type=\"d:guid\" xmlns:d=\"http://schemas.microsoft.com/2003/10/Serialization/\">" + entityId + "</c:value>" +
"</a:KeyValuePairOfstringanyType>" +
"<a:KeyValuePairOfstringanyType>" +
"<c:key>WorkflowId</c:key>" +
"<c:value i:type=\"d:guid\" xmlns:d=\"http://schemas.microsoft.com/2003/10/Serialization/\">" + workflowId + "</c:value>" +
"</a:KeyValuePairOfstringanyType>" +
"</a:Parameters>" +
"<a:RequestId i:nil=\"true\" />" +
"<a:RequestName>ExecuteWorkflow</a:RequestName>" +
"</request>" +
"</Execute>" +
"</s:Body>" +
"</s:Envelope>";
var req = new XMLHttpRequest();
req.open("POST", url, false);
// Responses will return XML. It isn't possible to return JSON.
req.setRequestHeader("Accept", "application/xml, text/xml, */*");
req.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
req.setRequestHeader("SOAPAction", "http://schemas.microsoft.com/xrm/2011/Contracts/Services/IOrganizationService/Execute");
req.send(request);
If the request.status is 200 the request was succesfull. This was tested on an CRM2011 enviroment.
I recommend you to create a WCF rest or web api, reference the IOrganizationService and from that use the operation of the CRM service. It is better to call a intermediate WCF than the IOrganizationService directly.

Can't read JSON response from server

I am working on a web app that sends some data to a website. The website updates the data to its database and returns a json array that replaces my webapp page. I am using ajax for making the query. I need to know how to prevent the overwriting of my webpage. The server is not mine so there is possibly a same origin policy problem.
I have checked the xmlhttp.readystate and xmlhttp.status, they are 4 and 0 respectively. According to some posts on stackoverflow the 0 status occurs due to the origin policy but I couldn't get a solution for this because usually the people with this problem had access to changes on the server side programming.
I want to read the json and extract values for my app but if I use the xmlhttp.responseText the server returns a blank string.
Any help is much appreciated.
Thanks
My code:
function sendAndReceive() {
var xmlhttp;
xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function () {
document.getElementById("postevent").innerHTML = "state changed";
document.getElementById("postevent").innerHTML = "" + xmlhttp.readyState + " " + xmlhttp.status;
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
document.getElementById("postevent").innerHTML = "success response";
document.getElementById("postevent").innerHTML = "this is it" + xmlhttp.responseText;
}
}
var url = "http://simple.ap.ernet.in/api/home/resource.php/resource/node?";
url += "key=" + document.getElementById("key").value + "&";
url += "datasetId=" + document.getElementById("datasetId").value + "&";
url += "localId=" + document.getElementById("localId").value + "&";
url += "nodeName=" + document.getElementById("nodeName").value + "&";
url += "nodeDesc=" + document.getElementById("nodeDesc").value + "&";
url += "lat=" + document.getElementById("lat").value + "&";
url += "long=" + document.getElementById("long").value;
document.getElementById("postevent").innerHTML = url;
xmlhttp.open("POST", url, true);
xmlhttp.send();
}
The only known workaround is to let the browser think there is only one origin. The easiest is to put a proxy on your own server relaying to the other server. If your own server is in Apache, you may use mod_proxy (see this question).
But note that if the other server doesn't allow cross-origin requests, it's probably against its policy to do that. And if it's a big server, it may very well detect it and ban your server's IP.

Categories

Resources