A space to discuss GraphQL queries, mutations, troubleshooting, throttling, and best practices.
Hello everyone 👋
I'm currently stuck trying to upload a PDF to the admin settings > files area. I'm able to upload the PDF but when I try to click on it in the admin, I get the following error modal.
Here are the steps I took so far.
I'm sending the data returned from the form <input type="file" name="file"> via a POST request to my custom app, which is a WordPress site. Because the app's "Distribution model" is "Shopify admin", I'm only authenticating via the access token.
Here's rough outline of the JS code that runs on the submit event of the form.
const formData = new FormData(e.target)
const formProps = Object.fromEntries(formData)
async function readFileAsync(blob, method = 'readAsDataURL') {
return new Promise((resolve, reject) => {
let reader = new FileReader
reader.onload = () => resolve(reader.result)
reader.onerror = reject
reader[method](blob)
})
}
const fileDataUrl = await readFileAsync(formProps.file)
formData.append('fileDataUrl', fileDataUrl)
// Hereafter, the formData is send via a POST request using Axios.
On the WordPress/PHP side I then continue to call stagedUploadsCreate and send its query parameters plus fileDataUrl (created via JS above) to the URL provided by the response. Then, I call the fileCreate mutation which, again, does upload the file, but the file doesn't work. My suspicion is that I'm not supplying the correct data to
// NOTE $request is an instance of WP_REST_Request.
$file_data_url = $request->get_param('fileDataUrl');
$file = $request->get_file_params();
$file = $file['file'];
$query = <<<QUERY
mutation stagedUploadsCreate(\$input: [StagedUploadInput!]!) {
stagedUploadsCreate(input: \$input) {
stagedTargets {
url
resourceUrl
parameters {
name
value
}
}
userErrors {
field
message
}
}
}
QUERY;
$input_data = [
[
'fileSize' => $file['size'],
'filename' => $file['name'],
'httpMethod' => 'PUT',
'mimeType' => 'application/pdf',
'resource' => 'FILE',
],
];
// This function is just a small wrapper around `wp_safe_remote_request`.
$res = send_shopify_admin_api_mutation( $query, [ 'input' => $input_data ] );
$staged_targets_url = $res['body']->data->stagedUploadsCreate->stagedTargets[0]->url;
$staged_targets_resource_url = $res['body']->data->stagedUploadsCreate->stagedTargets[0]->resourceUrl;
$staged_targets_parameters = $res['body']->data->stagedUploadsCreate->stagedTargets[0]->parameters;
$file_upload_body = [];
foreach ( $staged_targets_parameters as $param ) {
$file_upload_body[$param->name] = $param->value;
}
$file_upload_body['file'] = $file_data_url;
$file_upload_res = wp_safe_remote_request(
$staged_targets_url,
[
'method' => 'PUT',
'body' => $file_upload_body,
]
);
$query = <<<QUERY
mutation fileCreate(\$files: [FileCreateInput!]!) {
fileCreate(files: \$files) {
files {
alt
createdAt
fileStatus
fileErrors {
code
details
message
}
}
userErrors {
field
message
}
}
}
QUERY;
$input_data = [
[
'contentType' => 'FILE',
'originalSource' => $staged_targets_resource_url,
],
];
$file_create_res = send_shopify_admin_api_mutation( $query, [ 'files' => $input_data ] );
And here are the responses I get for the HTTP requests in the above PHP.
// Response for stagedUploadsCreate:
{
"code": 200,
"success": true,
"message": "OK",
"body": {
"data": {
"stagedUploadsCreate": {
"stagedTargets": [
{
"url": "https://shopify-staged-uploads.storage.googleapis.com/tmp/xxxxxxxxxxx/files/xxxx-xxxxx-xxxxx-x-xxx-xxx/test.pdf?X-Goog-Algorithm=&X-Goog-Credential=&...",
"resourceUrl": "https://shopify-staged-uploads.storage.googleapis.com/tmp/xxxxxxxxxxx/files/xxxx-xxxxx-xxxxx-x-xxx-xxx/test.pdf",
"parameters": [
{
"name": "content_type",
"value": "application/pdf"
},
{
"name": "acl",
"value": "private"
}
]
}
],
"userErrors": []
}
}
},
"cookies": [],
"headers": {}
}
// Response for the $file_upload_res variable.
// If I run the stagedUploadsCreate via the POST method vs. the PUT, I get a 503 "Service Unavailable" response.
{
"code": 200,
"success": true,
"message": "OK",
"body": "",
"cookies": [],
"headers": {}
}
// Response for the fileCreate mutation.
{
"code": 200,
"success": true,
"message": "OK",
"body": {
"data": {
"fileCreate": {
"files": [
{
"alt": "",
"createdAt": "2022-12-28T14:38:35Z",
"fileStatus": "UPLOADED",
"fileErrors": []
}
],
"userErrors": []
}
}
},
"cookies": [],
"headers": {}
}
Again, I think that I'm not supplying the right value to $file_upload_body['file']. I couldn't find any documentation on that.
Thanks for any help in advance.
Hi @mstewe👋
To create a staged target for a PDF file, we need to specify all the fields of the `StagedUploadInput` in the payload like the below:
{
"input": {
"resource": "FILE",
"filename": "test.pdf",
"mimeType": "application/pdf",
"httpMethod": "POST",
"fileSize": "12826"
}
}
This way, the returned parameters array will include a "key" that is required in the form data when when uploading the file to the signed URL.
{
"data": {
"stagedUploadsCreate": {
"stagedTargets": [
{
"url": "https://shopify-staged-uploads.storage.googleapis.com/",
"resourceUrl": "https://shopify-staged-uploads.storage.googleapis.com/tmp/55025631350/files/.../test.pdf",
"parameters": [
{
"name": "key",
"value": "tmp/55025631350/files/.../test.pdf"
}, ...
Hope that helps!
Developer Support @ Shopify
- Was this reply helpful? Click Like to let us know!
- Was your question answered? Mark it as an Accepted Solution
- To learn more visit Shopify.dev or the Shopify Web Design and Development Blog