wp-graphql-upload
wp-graphql-upload copied to clipboard
[ApolloError: Internal server error]
I have made it like this, this is not an error but I also do not understand, if the issue is my setup or the problem with wp-graphql-upload.
Can you please review it? thanks
register_graphql_mutation('uploadProfilePicture', [
'inputFields' => [
'file' => [
'type' => 'Upload',
],
],
'outputFields' => [
'avatarUrl' => [
'type' => 'String',
'resolve' => function ($payload) {
return $payload['avatarUrl'];
}
],
'avatarUrlThumbnail' => [
'type' => 'String',
'resolve' => function ($payload) {
return $payload['avatarUrlThumbnail'];
}
],
'successMessage' => [
'type' => 'Boolean',
'resolve' => function ($payload) {
return $payload['successMessage'];
}
]
],
'mutateAndGetPayload' => function ($input, $context, $info) {
if (!function_exists('wp_handle_sideload')) {
require_once(ABSPATH . 'wp-admin/includes/file.php');
}
$file_return = wp_handle_sideload($input['file'], [
'test_form' => false,
'test_type' => false,
]);
if (isset($file_return['error']) || isset($file_return['upload_error_handler'])) {
throw new \GraphQL\Error\UserError("The file could not be uploaded.");
}
$filename = $file_return['file'];
$attachment = [
'guid' => $file_return['url'],
'post_mime_type' => $file_return['type'],
'post_title' => preg_replace('/\.[^.]+$/', '', basename($filename)),
'post_content' => '',
'post_status' => 'inherit'
];
$attachment_id = wp_insert_attachment($attachment, $filename);
require_once(ABSPATH . 'wp-admin/includes/image.php');
$attach_data = wp_generate_attachment_metadata($attachment_id, $filename);
wp_update_attachment_metadata($attachment_id, $attach_data);
update_field('profile_pic', $attachment_id, 'user_' . $current_user->ID);
$profile_pic = get_field('profile_pic', 'user_' . $current_user->ID);
$avatarUrl = $profile_pic['url'];
$avatarUrlThumbnail = $profile_pic['sizes']['thumbnail'];
return [
'avatarUrl' => $avatarUrl,
'avatarUrlThumbnail' => $avatarUrlThumbnail,
'successMessage' => true,
];
}
]);
Uploading it this way:
const UPLOAD_IMAGE_MUTATION = gql`
mutation UploadProfilePicture($input: UploadProfilePictureInput!) {
uploadProfilePicture(input: $input) {
avatarUrl
avatarUrlThumbnail
}
}
`;
const selectImage = async () => {
// Check for the permission
const permissionResult =
await ImagePicker.requestMediaLibraryPermissionsAsync();
if (permissionResult.granted === false) {
alert("You've refused to allow this app to access your photos!");
return;
}
// Open the image picker
let pickerResult = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
allowsEditing: true, // or false based on your requirement
aspect: [4, 3], // aspect ratio
quality: 1, // quality of the image
});
if (pickerResult.canceled === true) {
return;
}
// Access the selected asset
const selectedAsset = pickerResult.assets[0]; // Assuming single image selection
if (selectedAsset) {
const source = {
uri: selectedAsset.uri,
type: selectedAsset.type, // type is now part of the asset
name: selectedAsset.fileName || "profile-pic.jpg", // name can be accessed or set a default
};
console.log(source);
uploadImage(source);
} else {
console.error("No image selected");
}
};
const [uploadProfilePicture] = useMutation(UPLOAD_IMAGE_MUTATION);
const uploadImage = async (imageFile) => {
// Convert image file to a format suitable for GraphQL upload
let localUri = imageFile.uri;
let filename = localUri.split("/").pop();
// Infer the type of the image
let match = /\.(\w+)$/.exec(filename);
let type = match ? `image/${match[1]}` : imageFile.type;
// Prepare the formData
const formData = new FormData();
formData.append("file", { uri: localUri, name: filename, type });
console.log("Form Data Prepared:", formData);
try {
console.log("Sending request to server");
const response = await uploadProfilePicture({
variables: {
input: { file: imageFile }, // Modify here to match the GraphQL mutation
},
});
console.log("Response received:", response);
// Extract the data from the response
const { avatarUrl, avatarUrlThumbnail, successMessage } =
response.data.uploadProfilePicture;
if (successMessage) {
console.log(successMessage); // Log or handle the success message as needed
// Update user context with new image URLs
setUserData({ avatarUrl, avatarUrlThumbnail });
}
} catch (error) {
console.error("Error during image upload:", error);
console.error("Error details:", error.networkError?.result || error);
}
};
The problem is am still getting this error: ERROR Error during image upload: [ApolloError: Internal server error] ERROR Error details: [ApolloError: Internal server error]
I'd start with formData.append("file", { uri: localUri, name: filename, type });, since you need to feed it an actual File or Blob object.
Then fix your catch statement at the end (or add an error handler to your ApolloClient definition), so you can get the actual error data and not just the generic message and continue your debugging.
I do have an errorLink:
const errorLink = onError(({ graphQLErrors, networkError }) => {
if (graphQLErrors)
graphQLErrors.forEach(({ message, locations, path }) =>
console.error(
`[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`
)
);
if (networkError) console.error(`[Network error]: ${networkError}`);
});
The thing is, am not getting the root of the error... Even tho, according to here, this should work in expo: https://stackoverflow.com/questions/66372873/react-native-expo-post-blob#comment117341523_66372873
const uploadImage = async (imageFile) => {
try {
// Extract the file extension
let fileType = imageFile.uri.substring(
imageFile.uri.lastIndexOf(".") + 1
);
// Prepare the formData
const formData = new FormData();
formData.append("file", {
uri: imageFile.uri,
name: imageFile.name, // You can use a dynamic name based on the context
type: `image/${fileType}`,
});
console.log("Form Data Prepared:", formData);
console.log("Sending request to server");
const response = await uploadProfilePicture({
variables: {
input: { file: formData }, // Modify here to match the GraphQL mutation
},
});
console.log("Response received:", response);
// Extract the data from the response
const { avatarUrl, avatarUrlThumbnail, successMessage } =
response.data.uploadProfilePicture;
if (successMessage) {
console.log(successMessage); // Log or handle the success message as needed
// Update user context with new image URLs
setUserData({ avatarUrl, avatarUrlThumbnail });
}
} catch (error) {
console.error("Error during image upload:", error);
if (
error.networkError &&
error.networkError.result &&
error.networkError.result.errors
) {
error.networkError.result.errors.forEach((e) =>
console.error(e.message)
);
}
}
};
But I'm still getting: LOG {"name": "profile-pic.jpg", "type": "image", "uri": "file:///data/user/0/host.exp.exponent/cache/ExperienceData/%2540anonymous%252FCudl-6de743e2-8e52-4646-b93f-9ed9ba6b740c/ImagePicker/60ced4f4-58f9-443c-ba72-27ad763aae27.jpeg"} LOG Form Data Prepared: {"_parts": [["file", [Object]]]} LOG Sending request to server ERROR [GraphQL error]: Message: Internal server error, Location: [object Object], Path: undefined ERROR Error during image upload: [ApolloError: Internal server error]
Since it's an Internal server error, try enabling WP_DEBUG and GRAPHQL_DEBUG to see more info and check the graphql response in your browser devtools since it might be a malformed response.
Add this to your wp-config.php:
define('WP_DEBUG', true);
define('WP_DEBUG_LOG', true);
define('SCRIPT_DEBUG', true);
define('GRAPHQL_DEBUG', true);
Since it's an Internal server error, try enabling
WP_DEBUGandGRAPHQL_DEBUGto see more info and check the graphql response in your browser devtools since it might be a malformed response.Add this to your
wp-config.php:define('WP_DEBUG', true); define('WP_DEBUG_LOG', true); define('SCRIPT_DEBUG', true); define('GRAPHQL_DEBUG', true);
Thanks for your reply @dre1080 , sadly I have all of them enabled :
define('WP_DEBUG', true);
define('WP_DEBUG_LOG', true);
define('WP_DEBUG_DISPLAY', true);
@ini_set('display_errors',1 );
define('SCRIPT_DEBUG', true);
define('GRAPHQL_DEBUG', true);
Also, just to make sure, I get no error I have added your sample upload mutation in the readme and tried to upload the image to it:
const SIMPLE_UPLOAD_MUTATION = gql`
mutation UploadFile($input: UploadInput!) {
upload(input: $input) {
text
}
}
`;
const selectImage = async () => {
// Check for the permission
const permissionResult =
await ImagePicker.requestMediaLibraryPermissionsAsync();
if (permissionResult.granted === false) {
alert("You've refused to allow this app to access your photos!");
return;
}
// Open the image picker
let pickerResult = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
allowsEditing: true, // or false based on your requirement
aspect: [4, 4], // aspect ratio
quality: 1, // quality of the image
});
if (pickerResult.canceled === true) {
return;
}
// Access the selected asset
const selectedAsset = pickerResult.assets[0]; // Assuming single image selection
if (selectedAsset) {
const source = {
uri: selectedAsset.uri,
type: selectedAsset.type, // type is now part of the asset
name: selectedAsset.fileName || "profile-pic.jpg", // name can be accessed or set a default
};
console.log(source);
uploadImage(source);
} else {
console.error("No image selected");
}
};
const [uploadFileMutation] = useMutation(SIMPLE_UPLOAD_MUTATION);
const uploadImage = async (imageFile) => {
const blob = await new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.onload = function () {
resolve(xhr.response);
};
xhr.onerror = function (e) {
console.log(e);
reject(new TypeError("Network request failed"));
};
xhr.responseType = "blob";
xhr.open("GET", imageFile.uri, true);
xhr.send(null);
});
try {
const response = await uploadFileMutation({
variables: {
input: { file: blob },
},
});
console.log("File uploaded successfully:", response.data.upload.text);
} catch (error) {
console.error("Error uploading file:", error);
}
};
All I get back is: LOG {"name": "profile-pic.jpg", "type": "image", "uri": "file:///data/user/0/host.exp.exponent/cache/ExperienceData/%2540anonymous%252FCudl-6de743e2-8e52-4646-b93f-9ed9ba6b740c/ImagePicker/c5930b63-c616-4b68-a965-657972e121cb.jpeg"} ERROR [GraphQL error]: Message: Internal server error, Location: [object Object], Path: undefined ERROR Error uploading file: [ApolloError: Internal server error]
Finally, I have got this error:
LOG {"name": "profile-pic.jpg", "type": "image", "uri": "file:///data/user/0/host.exp.exponent/cache/ExperienceData/%2540anonymous%252FCudl-6de743e2-8e52-4646-b93f-9ed9ba6b740c/ImagePicker/766861b2-99b5-4ad8-b588-9144a4eeed8a.jpeg"}
LOG {"_data":{"lastModified":0,"name":"766861b2-99b5-4ad8-b588-9144a4eeed8a.jpeg","size":722647,"offset":0,"type":"image/jpeg","blobId":"54ac845e-f2cf-4fa4-93b8-ad5f7a978a97","__collector":{}}}
LOG Sending request to server
ERROR [GraphQL error]: Message: Variable "$input" got invalid value {"_parts":[["file",{"_data":{"lastModified":0,"name":"766861b2-99b5-4ad8-b588-9144a4eeed8a.jpeg","size":722647,"offset":0,"type":"image\/jpeg","blobId":"54ac845e-f2cf-4fa4-93b8-ad5f7a978a97","__collector":[]}}]]}; Field "_parts" is not defined by type UploadProfilePictureInput., Location: [object Object], Path: undefined
Did you try this yet? Maybe there's something off about whatever automagical transformation is happening.
I'd start with
formData.append("file", { uri: localUri, name: filename, type });, since you need to feed it an actualFileorBlobobject.
Did you try this yet? Maybe there's something about whatever automagical transformation is happening.
I'd start with
formData.append("file", { uri: localUri, name: filename, type });, since you need to feed it an actualFileorBlobobject.
yup, already tried to be honest. Not working .. Getting the same error, or similar errors invalid input.
Okay, the error disappeared or changed when tried this one:
const uploadImage = async (imageFile) => {
try {
const blob = await new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.onload = function () {
resolve(xhr.response);
};
xhr.onerror = function (e) {
console.log(e);
reject(new TypeError("Network request failed"));
};
xhr.responseType = "blob";
xhr.open("GET", imageFile.uri, true);
xhr.send(null);
});
console.log(JSON.stringify(blob));
const formData = new FormData();
//formData.append("file", blob, imageFile.name);
formData.append("userpic", blob, "chris1.jpg");
// const test = {
// file: blob
// };
console.log("Sending request to server");
const graphqlResponse = await uploadProfilePicture({
variables: {
input: { file: formData._parts[0].file }, // Modify here to match the GraphQL mutation
},
});
console.log("Response received:", graphqlResponse);
// Extract the data from the response
const { avatarUrl, avatarUrlThumbnail, successMessage } =
response.data.uploadProfilePicture;
if (successMessage) {
console.log(successMessage); // Log or handle the success message as needed
// Update user context with new image URLs
setUserData({ avatarUrl, avatarUrlThumbnail });
}
} catch (error) {
console.error(
"Error during image upload:",
error.graphQLErrors[0].debugMessage
);
if (
error.networkError &&
error.networkError.result &&
error.networkError.result.errors
) {
error.networkError.result.errors.forEach((e) =>
console.error(e.message)
);
}
}
};
LOG {"name": "profile-pic.jpg", "type": "image", "uri": "file:///data/user/0/host.exp.exponent/cache/ExperienceData/%2540anonymous%252FCudl-6de743e2-8e52-4646-b93f-9ed9ba6b740c/ImagePicker/ad852a20-7def-4d9c-a481-0718304cfe09.jpeg"}
LOG {"_data":{"lastModified":0,"name":"ad852a20-7def-4d9c-a481-0718304cfe09.jpeg","size":722647,"offset":0,"type":"image/jpeg","blobId":"d37a64c4-c314-4a49-9e56-2eaa54926960","__collector":{}}}
LOG Sending request to server
ERROR [GraphQL error]: Message: The file could not be uploaded., Location: [object Object], Path: uploadProfilePicture
ERROR Error during image upload: undefined
so it only accepted my input when I tried the blob._data but still failed on server side :(
const uploadImage = async (imageFile) => {
try {
const blob = await new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.onload = function () {
resolve(xhr.response);
};
xhr.onerror = function (e) {
console.log(e);
reject(new TypeError("Network request failed"));
};
xhr.responseType = "blob";
xhr.open("GET", imageFile.uri, true);
xhr.send(null);
});
const formData = new FormData();
formData.append("userpic", blob, "chris1.jpg");
console.log(blob._data);
const graphqlResponse = await uploadProfilePicture({
variables: {
input: { file: blob._data }, // Modify here to match the GraphQL mutation
},
});
// const graphqlResponse = await uploadFileMutation({
// variables: {
// input: { file: formData._parts[0][1]._data },
// },
// });
console.log("Response received:", graphqlResponse);
const { avatarUrl, avatarUrlThumbnail, successMessage } =
response.data.uploadProfilePicture;
if (successMessage) {
console.log(successMessage);
setUserData({ avatarUrl, avatarUrlThumbnail });
}
} catch (error) {
console.error(
"Error during image upload:",
error.graphQLErrors[0].debugMessage
);
console.error("Error during image upload:", error);
}
};
Here the error log :
[15-Nov-2023 17:26:28 UTC] Received file: Array
(
[lastModified] => 0
[name] => 13436d64-b9c0-46a1-a375-e0b049b396c0.jpeg
[size] => 722647
[offset] => 0
[type] => image/jpeg
[blobId] => a5ead176-e0c4-4714-a700-fb475ebf7fcc
[__collector] => Array
(
)
[tmp_name] => /tmp/13436d64-b9c0-46a1-a375-e0b049b396c0.jpeg
)
[15-Nov-2023 17:26:28 UTC] uploadProfilePicture mutation called
[15-Nov-2023 17:26:28 UTC] Handling file upload
[15-Nov-2023 17:26:28 UTC] File upload error: {"error":"Specified file failed upload test."}
That error comes from WP's handle_file_upload().
I've run into something similar myself in the past over in #2 , was able to get around it by using wp_upload_bits() on the server side, since that doesn't rely on the is_file_uploaded() check that seems to be failing.
After using wp_upload_bits it most likely fails to upload the image or the photo.. and save a photo with 0 byte in the uplaod dir:
Code:
register_graphql_mutation('uploadProfilePicture', [
'inputFields' => [
'file' => [
'type' => 'Upload',
],
],
'outputFields' => [
'avatarUrl' => [
'type' => 'String',
'resolve' => function ($payload) {
return $payload['avatarUrl'];
}
],
'avatarUrlThumbnail' => [
'type' => 'String',
'resolve' => function ($payload) {
return $payload['avatarUrlThumbnail'];
}
],
'successMessage' => [
'type' => 'Boolean',
'resolve' => function ($payload) {
return $payload['successMessage'];
}
]
],
'mutateAndGetPayload' => function ($input, $context, $info) {
$upload = wp_upload_bits($input['file']['name'], null, file_get_contents($input['file']['tmp_name']));
if (!$upload['error']) {
error_log('Upload: ' . print_r($upload, true));
$filename = $upload['file'];
$attachment = [
'guid' => $file_return['url'],
'post_mime_type' => $file_return['type'],
'post_title' => preg_replace('/\.[^.]+$/', '', basename($filename)),
'post_content' => '',
'post_status' => 'inherit'
];
$attachment_id = wp_insert_attachment($attachment, $filename);
require_once(ABSPATH . 'wp-admin/includes/image.php');
$attach_data = wp_generate_attachment_metadata($attachment_id, $filename);
wp_update_attachment_metadata($attachment_id, $attach_data);
update_field('profile_pic', $attachment_id, 'user_' . $current_user->ID);
$profile_pic = get_field('profile_pic', 'user_' . $current_user->ID);
$avatarUrl = $profile_pic['url'];
$avatarUrlThumbnail = $profile_pic['sizes']['thumbnail'];
return [
'avatarUrl' => $avatarUrl,
'avatarUrlThumbnail' => $avatarUrlThumbnail,
'successMessage' => true,
];
} else {
error_log('File upload error: ' . $upload['error']);
throw new \GraphQL\Error\UserError("The file could not be uploaded.");
}
}
]);
Log:
[15-Nov-2023 18:25:33 UTC] PHP Warning: file_get_contents(/tmp/photo.jpeg): Failed to open stream: No such file or directory in /home/**PATH**/wp-content/themes/headless-xfive/functions.php on line 404
[15-Nov-2023 18:25:33 UTC] Upload: Array
(
[file] => /home/**PATH**/wp-content/uploads/2023/11/photo-2.jpeg
[url] => https://testDomain/wp-content/uploads/2023/11/photo-2.jpeg
[type] => image/jpeg
[error] =>
)
[15-Nov-2023 18:25:33 UTC] PHP Warning: Undefined variable $file_return in /home/**PATH**/wp-content/themes/headless-xfive/functions.php on line 411
[15-Nov-2023 18:25:33 UTC] PHP Warning: Trying to access array offset on value of type null in /home/**PATH**/wp-content/themes/headless-xfive/functions.php on line 411
[15-Nov-2023 18:25:33 UTC] PHP Warning: Undefined variable $file_return in /home/**PATH**/wp-content/themes/headless-xfive/functions.php on line 412
[15-Nov-2023 18:25:33 UTC] PHP Warning: Trying to access array offset on value of type null in /home/**PATH**/wp-content/themes/headless-xfive/functions.php on line 412
[15-Nov-2023 18:25:33 UTC] PHP Deprecated: preg_replace(): Passing null to parameter #3 ($subject) of type array|string is deprecated in /home/**PATH**/wp-includes/formatting.php on line 5725
[15-Nov-2023 18:25:33 UTC] PHP Warning: Undefined variable $current_user in /home/**PATH**/wp-content/themes/headless-xfive/functions.php on line 424
[15-Nov-2023 18:25:33 UTC] PHP Warning: Attempt to read property "ID" on null in /home/**PATH**/wp-content/themes/headless-xfive/functions.php on line 424
[15-Nov-2023 18:25:33 UTC] PHP Warning: Undefined variable $current_user in /home/**PATH**/wp-content/themes/headless-xfive/functions.php on line 426
[15-Nov-2023 18:25:33 UTC] PHP Warning: Attempt to read property "ID" on null in /home/**PATH**/wp-content/themes/headless-xfive/functions.php on line 426
[15-Nov-2023 18:25:33 UTC] PHP Warning: Undefined array key "sizes" in /home/**PATH**/wp-content/themes/headless-xfive/functions.php on line 428
[15-Nov-2023 18:25:33 UTC] PHP Warning: Trying to access array offset on value of type null in /home/**PATH**/wp-content/themes/headless-xfive/functions.php on line 428