Salesforce - SharePoint File Upload 400 Bad Request Error - javascript

I am trying to upload file from Apex to Sharepoint but getting error as '400 Bad Request'.But work from JS CODE. Following is my code snippet :
Apex Code
Http http = new Http();
HttpRequest httpRequestToSend = new HttpRequest();
httpRequestToSend.setEndpoint('https://sample.sharepoint.com/sites/siteName/_api/web/GetFolderByServerRelativeUrl(\''+'/sites/siteName/Shared Documents'+'\')/Files/Add(url=\''+'document3.txt'+'\', overwrite=true)');
httpRequestToSend.setMethod('POST');
httpRequestToSend.setHeader('Authorization', 'Bearer ' + token);
httpRequestToSend.setHeader('Content-Type','application/json; odata=verbose');
httpRequestToSend.setBodyAsBlob(Blob.ValueOf('test Message'));
System.debug('***** httpRequestToSend-->' + httpRequestToSend);
Http http1 = new Http();
HttpResponse httpResponse1 = http1.send(httpRequestToSend);
System.debug('***** httpResponse-->' + httpResponse1.toString());
System.debug(httpResponse1.getBody());
JS CODE
var myHeaders = new Headers();
myHeaders.append("Authorization", "Bearer " + Token);
myHeaders.append("Content-Type", "application/json;odata=verbose");
var requestOptions = {
method: 'POST',
headers: myHeaders,
body: fileBuffer,
};
fetch('https://sample.sharepoint.com/sites/siteName/_api/web/GetFolderByServerRelativeUrl(\'/sites/siteName/Shared Documents\')/Files/Add(url=\'test.txt\', overwrite=true)', requestOptions)
.then(response => response.text())
.then(result => console.log(result))
.catch(error => alert('error', error));
}
Thankyou

As per the documentation salesforce can't communicate to FTP directly that means can't Read/Write file at FTP Server.
I also faced this issue and this is how I resolved it:-
Step1: Create & host an External API on in any language(C#, Python) that takes two parameters one as fileName and the other one as fileData and uploads that file.
Step2: At Salesforce end, consume that API using HttpRequest and pass your file as filedata and fileName.
public void uploadFileToFTP_Service(string fileName,string fileData)
{
string value='{"fileName":"'+fileName+'","fileData": "'+fileData+'"}';
HttpRequest req = new HttpRequest();
req.setEndpoint('http://yourhostedApIPath:9001/data');
req.setMethod('POST');
req.setTimeout(120000);
req.setHeader('content-type','application/json; charset=utf-8');
req.setBody(value);
Http http = new Http();
HttpResponse res = http.send(req);
system.debug('Status code: ' + res.getStatusCode());
}

I was beating my head on this for some time today, and was finally able to get past the "400 Bad Request" error by escaping the space characters in my relative url path (I expect the filename may need the same workaround).
Each space character (" ") must be replaced with the ASCII reference "%20"
so in your example, the endpoint url:
'https://sample.sharepoint.com/sites/siteName/_api/web/GetFolderByServerRelativeUrl(\'/sites/siteName/Shared Documents\')/Files/Add(url=\'test.txt\', overwrite=true)'
should be changed to:
'https://sample.sharepoint.com/sites/siteName/_api/web/GetFolderByServerRelativeUrl(\'/sites/siteName/Shared%20Documents\')/Files/Add(url=\'test.txt\', overwrite=true)'
At least in my case, this corrected the issue.

Related

How to get an image from queued request body (Workbox PWA)

I am using Workbox in my PWA. If app is offline and image posting request fails - it is automatically added to IndexedDB.
const post = (file: File) => {
const formData = new FormData()
formData.append("image", file);
axios.post('http://some.com/upload', formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
}).then(...)
I want to show user all images of queued requests in offline mode. How i get requests:
import { openDB } from 'idb';
const db = await openDB('workbox-background-sync');
const queuedRequests = await db.getAllFromIndex('requests', 'queueName');
Body of request is ArrayBuffer (how request is stored in IndexedDB is showed on the picture). I don't understand how to "extract" image and pass it to
createObjectURL( imageBlob ).
If i decode body like this:
let view = new Uint8Array(body);
let bodyAsString = new TextDecoder().decode(view);
I got post body which contains image bytes after "image/png\r\n\r\n":
------WebKitFormBoundaryfc7IW9qvYqE2SdSm\r\nContent-Disposition: form-data; name="image"; filename="1.png"\r\nContent-Type: image/png\r\n\r\n�PNG\r\n\x1A\n\x00\...
I don't think that parsing payload using regexp, then encoding it is a good approach. How can this be done?

web browser complains when trying to create a blob TypeError: Failed to construct 'Blob': The provided value cannot be converted to a sequence

I have a REST API (POST method) which returns the content of a PDF. I tested using curl and I do can see and open the file on my computer:
curl http://localhost:8080/ponyapp/test/generate-pdf -H 'Content-Type: application/json' -d '[{"fieldA":"valueA", "fieldB":"valueB", ...}]' -i -o ~/Desktop/label.pdf
Now I have a vuejs/js application which needs to use this REST API and be able to save the file into the local computer. My approach (please correct me if I am wrong) is:
call the API to get the response payload
put the payload into a file
download the file into the local computer by using HTML anchor element
For some reason, this is not working
This the response payload:
%PDF-1.4↵%����↵1 0
obj↵<<↵/CreationDate(D:20200301141435+13'00')↵/Title(CourierPost
Label)↵/Creator(PDFsharp 1.50.4000-wpf
(www.pdfsharp.com))↵/Producer(PDFsharp 1.50.4000-wpf
(www.pdfsharp.com))↵>>↵endobj↵2 0 obj↵<<↵/Type/Catalo...
I have tried different variations of this code:
axios
.post(
this.$store.state.baseUrl + "test/generate-pdf",
[this.uberShipment],
{
responseType: "blob",
headers: {
Authorization: "Bearer " + this.getToken()
}
}
)
.then(response => {
let filename = "label" + new Date().toISOString() + ".pdf";
let data = new Blob(response.data, { type: 'application/pdf,' });
let link = document.createElement("a");
link.setAttribute("href", (window.webkitURL || window.URL).createObjectURL(data));
link.setAttribute("download", filename);
link.click();
})
.catch(error => {
...
})
Which fails with the error below:
error = TypeError: Failed to construct 'Blob': The provided value
cannot be converted to a sequence. at eval
(webpack-internal:///./node_modules/cache-loader/dist/cjs.js?!./node_modules/babel-loader/lib/index.js!./node_modules/vuetify-loader/lib/loader.js?!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/components/CreateShipmentAndNZPostDomesticLabel.vue?vue&type=script&lang=js&:604:20)
_this
I would appreciate any advice of why this is not working (either in my approach or in the code)
thank you
You must pass an array to blob constructor
let data = new Blob([response.data], { type: 'application/pdf,' });
Docs

Check if file with korean character exists on server with javascript?

So I am trying to check if a pdf file exists on my server or not.
PDF files are named in korean like abc.com/토보토보.pdf
I have tried :
function UrlExists(url)
{
var http = new XMLHttpRequest();
http.open('HEAD', url, false);
http.send();
console.log(http.status);
}
But the problem is it always encoded to example.com/%C3%AD%C2%86%C2%A0%C3%AB%C2%B3%C2%B4%C3%AD%C2%86%C2%A0%C3%AB%C2%B3%C2%B4.pdf
UrlExists("example.com/토보토보.pdf")
01:51:29.144 VM428:14
HEAD
http://example.com/%ED%86%A0%EB%B3%B4%ED%86%A0%EB%B3%B4.pdf
404 (Not Found)
How do i get the solution to my problem?
I think maybe you want to run the base filename part of your url through encodeURIComponent before sending the http request out.
This should convert your korean text to the escaped text (with the percentage signs) and then it can find it.
source: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent
try window.encodeURI or window.encodeURIComponent:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURI
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent
// encodes characters such as ?,=,/,&,:
console.log(encodeURIComponent('?x=шеллы'));
// expected output: "%3Fx%3D%D1%88%D0%B5%D0%BB%D0%BB%D1%8B"
console.log(encodeURIComponent('?x=test'));
// expected output: "%3Fx%3Dtest"
or try this:
const pdf = {name:"토보토보"};
fetch(url, {
method: 'POST',
body: JSON.stringify(pdf),
headers: new Headers({
'Content-Type': 'application/json'
})
})
.then(function(res){
return res.json()
})
.then(function(v){
// read the result
})
or you could do a GET request by putting JSON as a query param:
const pdf = {name:"토보토보"};
const url = "example.com?pdf=${JSON.stringify(pdf)}"
fetch(url).then(...);

JavaScript read all text from txt file on Google drive or Dropbox

I would like to use JavaScript to read the entire content of a txt file on Google Drive or Dropbox into a string variable. I've seen similar questions, but I would like to have it done purely with JavaScript. I have already tried some ways, but I keep getting "XMLHttpRequest cannot load ... no 'access-control-allow-origin' " and "Failed to execute 'send' " errors. I would also like to do this without any external APIs or installations.
Here is a javascript function to download text file from Google Drive.
Using fetch API.
function get_doc(id){
const url = 'https://www.googleapis.com/drive/v3/files/'+id+'?alt=media'
if(self.fetch){
var setHeaders = new Headers();
setHeaders.append('Authorization', 'Bearer ' + authToken.access_token);
setHeaders.append('Content-Type', mime);
var setOptions = {
method: 'GET',
headers: setHeaders
};
fetch(url,setOptions)
.then(response => { if(response.ok){
var reader = response.body.getReader();
var decoder = new TextDecoder();
reader.read().then(function(result){
var data = {}
data = decoder.decode(result.value, {stream: !result.done});
console.log(data);
});
}
else{
console.log("Response wast not ok");
}
}) .catch(error => {
console.log("There is an error " + error.message);
});
}
}
Refer here in case of Google Docs file.
s007 answer works pretty ok but I have no idea why it doesn't return all texts in the file (some got trimmed). Below I use the files.get function from google drive javascript api. So this works after you authenticate.
this.readFile = function(fileId,callback){
var request = gapi.client.drive.files.get({
fileId: fileId,
alt: 'media'
})
request.then(function(response){
console.log(response); //response.body has the text content of the file
if (typeof callback === "function") callback(response.body);
},function(error){
console.error(error)
})
return request; //for batch request
}

Basic Authentication Using JavaScript

I am building an application that consumes the Caspio API. I am having some trouble authenticating against their API. I have spent 2-3 days trying to figure this out but it may be due to some understanding on my end. I have read countless articles on stackoverflow post and otherwise but have not solved the issue. Below is a code example of my solution based on what i have looked at and i am getting a 400 Status code message; What am i doing wrong here? (Please provide well commented code example and i would prefer to NOT have links posted here referencing other material as i have looked at these extensively. Thanks!):
Some references i have looked at:
1) Pure JavaScript code for HTTP Basic Authentication?
2) How to make http authentication in REST API call from javascript
I would like to use this authentication method as described by caspio below:
As an alternative to including credentials in the request body, a client can use the HTTP Basic authentication scheme. In this case, authentication request will be setup in the following way:
Method: POST
URL: Your token endpoint
Body: grant_type=client_credentials
Header parameter:
Authorization: Basic Basic authentication realm
Below are my Javascript and HTML code.
JavaScript:
var userName = "clientID";
var passWord = "secretKey";
function authenticateUser(user, password)
{
var token = user + ":" + password;
// Should i be encoding this value????? does it matter???
// Base64 Encoding -> btoa
var hash = btoa(token);
return "Basic " + hash;
}
function CallWebAPI() {
// New XMLHTTPRequest
var request = new XMLHttpRequest();
request.open("POST", "https://xxx123.caspio.com/oauth/token", false);
request.setRequestHeader("Authorization", authenticateUser(userName, passWord));
request.send();
// view request status
alert(request.status);
response.innerHTML = request.responseText;
}
HTML:
<div>
<div id="response">
</div>
<input type="button" class="btn btn-primary" value="Call Web API" onclick="javascript:CallWebAPI();" />
After Spending quite a bit of time looking into this, i came up with the solution for this; In this solution i am not using the Basic authentication but instead went with the oAuth authentication protocol. But to use Basic authentication you should be able to specify this in the "setHeaderRequest" with minimal changes to the rest of the code example. I hope this will be able to help someone else in the future:
var token_ // variable will store the token
var userName = "clientID"; // app clientID
var passWord = "secretKey"; // app clientSecret
var caspioTokenUrl = "https://xxx123.caspio.com/oauth/token"; // Your application token endpoint
var request = new XMLHttpRequest();
function getToken(url, clientID, clientSecret) {
var key;
request.open("POST", url, true);
request.setRequestHeader("Content-type", "application/json");
request.send("grant_type=client_credentials&client_id="+clientID+"&"+"client_secret="+clientSecret); // specify the credentials to receive the token on request
request.onreadystatechange = function () {
if (request.readyState == request.DONE) {
var response = request.responseText;
var obj = JSON.parse(response);
key = obj.access_token; //store the value of the accesstoken
token_ = key; // store token in your global variable "token_" or you could simply return the value of the access token from the function
}
}
}
// Get the token
getToken(caspioTokenUrl, userName, passWord);
If you are using the Caspio REST API on some request it may be imperative that you to encode the paramaters for certain request to your endpoint; see the Caspio documentation on this issue;
NOTE: encodedParams is NOT used in this example but was used in my solution.
Now that you have the token stored from the token endpoint you should be able to successfully authenticate for subsequent request from the caspio resource endpoint for your application
function CallWebAPI() {
var request_ = new XMLHttpRequest();
var encodedParams = encodeURIComponent(params);
request_.open("GET", "https://xxx123.caspio.com/rest/v1/tables/", true);
request_.setRequestHeader("Authorization", "Bearer "+ token_);
request_.send();
request_.onreadystatechange = function () {
if (request_.readyState == 4 && request_.status == 200) {
var response = request_.responseText;
var obj = JSON.parse(response);
// handle data as needed...
}
}
}
This solution does only considers how to successfully make the authenticated request using the Caspio API in pure javascript. There are still many flaws i am sure...
Today we use Bearer token more often that Basic Authentication but if you want to have Basic Authentication first to get Bearer token then there is a couple ways:
const request = new XMLHttpRequest();
request.open('GET', url, false, username,password)
request.onreadystatechange = function() {
// D some business logics here if you receive return
if(request.readyState === 4 && request.status === 200) {
console.log(request.responseText);
}
}
request.send()
Full syntax is here
Second Approach using Ajax:
$.ajax
({
type: "GET",
url: "abc.xyz",
dataType: 'json',
async: false,
username: "username",
password: "password",
data: '{ "key":"sample" }',
success: function (){
alert('Thanks for your up vote!');
}
});
Hopefully, this provides you a hint where to start API calls with JS. In Frameworks like Angular, React, etc there are more powerful ways to make API call with Basic Authentication or Oauth Authentication. Just explore it.
To bring this question up to date, a node.js solution (using node-fetch) would be as follows:
const auth = Buffer.from(`${clientId}:${clientSecret}`).toString("base64");
fetch("https://some-oauth2.server.com/connect/token", {
method: "POST",
body: "grant_type=client_credentials",
headers: {
"Content-type": "application/x-www-form-urlencoded",
Authorization: `Basic ${auth}`,
},
})
.then((response) => response.json())
.then((response) => {
console.log(response); //response.access_token is bearer token, response.expires_in is lifetime of token
});
Sensitive requests like this should be server-to-server, and keeping the credential details in the Header rather than QueryString means it's less likely to be visible in web server logs
EncodedParams variable is redefined as params variable will not work. You need to have same predefined call to variable, otherwise it looks possible with a little more work. Cheers! json is not used to its full capabilities in php there are better ways to call json which I don't recall at the moment.
change var to const for the username, password, token_, and key variables.

Categories