Download CSV from shopware 6 administration - shopware6

I want to implement a CSV export from Shopware 6 admin. I have a button, want to open a new window and get a CSV file.
I implemented a controller:
/**
* #Route(
* "/api/winkelwagen/export/csv/{id}",
* methods={"GET"},
* defaults={"auth_required"=true, "_routeScope"={"api"}}
* )
*/
public function export(string $id, Context $context, Request $request): Response
{
/** #var PromotionEntity $promo */
$response->setContent('csv file');
return $response;
}
But to call this controller, you need to be logged in which totally make sense.
My button in the administration currently opens a new window and opens the page:
window.open('http://www.fabian-blechschmidt.de', '_blank');
Which of course doesn't work with the api url, because you needs to be authenticated.
So my question is: How do I implement this authentication and get a CSV file in the backend? :-)
Maybe my approach is totally broken - happy to get a better idea!

To extend on the answer from dneustadt, the code snippet below also actually downloads the csv as a file by loading the data into a data url and clicking it virtually.
const initContainer = Shopware.Application.getContainer('init');
initContainer.httpClient.get(
`winkelwagen/export/csv/${id}`,
{
headers: {
Accept: 'application/vnd.api+json',
Authorization: `Bearer ${Shopware.Service('loginService').getToken()}`,
'Content-Type': 'application/json',
},
},
).then((response) => {
if (response.data) {
const filename = response.headers['content-disposition'].split('filename=')[1];
const link = document.createElement('a');
link.href = URL.createObjectURL(response.data);
link.download = filename;
link.dispatchEvent(new MouseEvent('click'));
link.parentNode.removeChild(link);
}
});

You can use the built in http client and set the bearer token for authentication:
const initContainer = Shopware.Application.getContainer('init');
initContainer.httpClient.get(
`winkelwagen/export/csv/${id}`,
{
headers: {
Accept: 'application/vnd.api+json',
Authorization: `Bearer ${Shopware.Service('loginService').getToken()}`,
'Content-Type': 'application/json',
},
},
);
Change the headers according to your needs.

dneustadt is a great start!
But Rune Laenen's answer is gold!
But I think due to the fact, that I don't return JSON but a CSV file, I needed to change a few things.
For some reason the filename contained " as prefix and suffix - we cut these.
The response.data is neither a file nor a Blob, which is needed for URL.createObjectURL - no problem, we just make one.
and the link doesn't seem to be attached to document, so we make sure no error is thrown
if (response.data) {
let filename = response.headers['content-disposition'].split('filename=')[1];
filename = filename.substring(1, filename.length - 1);
const link = document.createElement('a');
link.href = URL.createObjectURL(new Blob([response.data], {type: response.headers['content-type']}));
link.download = filename;
link.dispatchEvent(new MouseEvent('click'));
try {
link.parentNode.removeChild(link);
} catch (e) {
// do nothing
}
}

I would not recommend to create a link with a data object on the fly. I believe this can create issues with large downloads (but I just assume).
I would suggest to do it like the core and use the access token.
See here https://github.com/shopware/platform/blob/469a7fc7c7a60eea6ae0863f54cb489bc0cbf31c/src/Administration/Resources/app/administration/src/core/service/api/import-export.api.service.js#L82

Related

Upload to S3 - The body of your POST request is not well-formed multipart/form-data

I am trying to upload a file to s3 using this guide: https://www.dtreelabs.com/blog/s3-direct-file-upload-using-presigned-url-from-react-and-rails which long story short describes how to use a presigned url to upload files to S3.
Whenever I send the request to my s3 bucket to upload a given file, I am getting an error The body of your POST request is not well-formed multipart/form-data.
My front end code is:
const handleImageUpload = (file) => {
ApiUtils.getPresignedS3Url({ fileName: file.name }).then((uploadParams) => {
if (uploadParams) {
uploadToS3(uploadParams, file)
}
})
const uploadToS3 = (uploadParams, file) => {
const { url, s3_upload_params: fields } = uploadParams
const formData = new FormData()
formData.append("Content-Type", file.type)
Object.entries(fields).forEach(([k, v]) => {
formData.append(k, v)
})
formData.append("file", file)
fetch(url, {
method: "POST",
headers: {
"Content-Type": "multipart/form-data",
},
undefined,
body: formData,
})
.then((awsResponse) => {
if (awsResponse.ok) {
console.log("success")
} else {
console.log(awsResponse)
}
})
.catch((error) => {
console.log("blew up")
console.log(error)
})
}
Several other stack overflow answers involve using Axios or new XMLHttpRequest. These have resulted in the same error for me.
the end of the payload I am sending to amazon is:
------WebKitFormBoundary7cFRTGgKGqbDhagf
Content-Disposition: form-data; name="file"; filename="uploadMe.html"
Content-Type: text/html
------WebKitFormBoundary7cFRTGgKGqbDhagf--
I believe the issue may be something along the lines of the body of my file isn't being included in the request. I'm investigating this now.
Any help would be appreciated, thank you <3
https://github.com/github/fetch/issues/505#issuecomment-293064470 describes why this is an issue. Posting the text incase the comment ever gets removed:
Setting the Content-Type header manually means it's missing the boundary parameter. Remove that header and allow fetch to generate the full content type. It will look something like this:
Content-Type: multipart/form-data;boundary=----WebKitFormBoundaryyrV7KO0BoCBuDbTL
Fetch knows which content type header to create based on the FormData object passed in as the request body content.
removing "Content-Type": "multipart/form-data" above indeed seems to result in the mujltipart form data being formatted correctly.

Post to /upload from react native

I'm trying to upload a picture to strapi from react native.
async function uploadPicture(uri) {
var data = new FormData();
data.append('files', {
uri: uri.replace('file://', ''),
name: uri,
type: 'image/jpg'
});
// Create the config object for the POST
// You typically have an OAuth2 token that you use for authentication
const config = {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'multipart/form-data;'
},
body: data
};
const fetchPromise = fetch('http://<host>:1337/upload', config)
console.log(fetchPromise)
return await fetchPromise
}
I get a 200 status code but no new picture is listed on the uploads page.
Oh! I figured it out using simple-http-upload-server to test the uploads. The problem was that I was setting the name of the file to be the uri. This would probably cause an error on strapi when creating the file on the server folder. It should return an error code nonetheless.

Call Request From Collection Within Pre-Request Script

I am fully aware that there is a way to make an ajax call from within a request's Pre-request script, a la,
const getBooks = {
url: "http://" + pm.environment.get("host") + "/books",
method: "GET",
header: "G-TOKEN:ROM831ESV"
};
pm.sendRequest(getBooks, function(err, books) {
const ids = _.map(books.json(), function(book) {
return book.id;
});
pm.globals.set("bookIds", ids);
});
pane but is there a way to call a saved request from a collection by name like you can do with postman.setNextRequest('') something akin to...
pm.sendRequest('Get Books', function(err, books) {
const ids = _.map(books.json(), function(book) {
return book.id;
});
});
Apparently this is not possible in the current version of Postman. There is a request out there on github about this exact issue.
https://github.com/postmanlabs/postman-app-support/issues/4845

CORS outlook api : not allowed access

I cannot count how many times I sweared on CORS.
Right now we are trying to access the outlook API to send emails and stuff. We follow the tutorial, do everything on Postman and that works. Now we want to implement it in our Angular 2 application with the following code:
requestAccessToken(code: string)
{
if (code) {
var headers = new Headers();
headers.append("Content-Type", 'application/x-www-form-urlencoded');
var requestoptions = new RequestOptions({
headers: headers,
withCredentials: false // tried true too
})
let body = `grant_type=authorization_code&
redirect_uri=http://localhost:4200&
code=`+ code + `&
client_id=4e...ab&
client_secret=CE.....BC`
this.http.post("https://login.microsoftonline.com/common/oauth2/v2.0/token", body, requestoptions).subscribe((data) =>
{
console.log("data: " + data);
},
error =>
{
console.log("error: " + error);
});
}
}
Our response looks like this:
{
"token_type":"Bearer",
"scope":"calendars.read calendars.read.shared calendars.readwrite calendars.readwrite.shared contacts.read
contacts.read.shared mail.read
user.read",
"expires_in":3599,"ext_expires_in":0,
"access_token":"ey...NjQ",
"refresh_token":"OAQABAAA...Fd8JA"
}
Which is exactly but I want, but however I cannot extract the token out of it and my browser logs the following:
As you can see, the error is logged and not the data and Chrome complains about CORS. I'm really stuck and the only thing the internet says is to change server settings, which is of course not possible with the URL login.microsoftonline.com

Aurelia js fie upload to server

Hi am new to aurelia js , i need to upload file to server,am using autrelia js, materializecss and httpClient.fetch for api call. I dont'know how to send file to server.
view :
<input type="file" files.bind="selectedFiles" change.delegate="onSelectFile($event)">
Model :
onSelectFile(e)
{
var myurl = 'http://cdn.dmsapp.tk/file?authToken=bLNYMtfbHntfloXBuGlSPueilaHtZx&type=jpg&name=sibi.jpg&userId=7&organizationId=1&sourceType=USER_UPLOADS';
this.httpValueConverter.call_http(myurl,'POST',this.selectedFiles[],'fileupload',file_upload)
.then(data => {
console.log(data);
if(data.meta && data.meta.statusCode == 200) {
// this.index_lists = data.index.list;
}
}); }
httpservice :
return this.httpClient.fetch('http://api.dmsapp.tk/'+url,
{
method: method,
body : json(myPostData),
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'authorization': this.authorization}})
.then(response => response.json());
looking for a solution.
If it's a file and you are trying to upload a particular media type,
the header 'Content-Type': 'application/x-www-form-urlencoded' does not seem right to me. Have a look at the appropriate media type here:
http://www.iana.org/assignments/media-types/media-types.xhtml
Also, you serialize data to JSON, if your data is binary you will need to change that to a byte array.
You might find some useful info here:
http://www.petermorlion.com/file-upload-with-aurelia/
Also you set a token both in your URL and your header, I'd recommend to set it in the header only.