Open bleepsandblops opened 6 months ago
You can also override the baseUri of the provider, which is probably going to be the easiest thing here, rather than spinning up a whole new provider, or adding a conditional for which API to use. Particularly if the auth is all the same.
{% set data = craft.consume.fetchData('fedex', 'POST', 'documents/v1/etds/upload', {
base_uri: 'https://documentapi.prod.fedex.com/',
}) %}
Hi @engram-design thanks a lot, so would you say something like this should work? Because server is sending me back a 500 (see 500 error below)
$payload = [
'base_uri' => 'https://documentapi.prod.fedex.com/',
'multipart' => [
[
'name' => 'attachment',
'contents' => fopen(Craft::getAlias('@root') . $filePath, 'r'),
'filename' => $fileName
],
[
'name' => 'document',
'contents' => json_encode([
'workflowName' => 'ETDPostshipment',
'name' => $fileName,
'contentType' => 'application/pdf',
'carrierCode' => 'FDXE',
'meta' => [
'trackingNumber' => $trackingNumber,
'shipmentDate' => $shipmentDate,
'shipDocumentType' => 'COMMERCIAL_INVOICE',
'originCountryCode' => 'FR',
'destinationCountryCode' => $order->shippingAddress->countryCode
]
])
]
]
];
$data = Consume::$plugin->getService()->fetchData('fedexTradeUpload', 'POST', 'documents/v1/etds/upload', $payload);
(I've also tried without the json_encode
.)
The 500 error:
"message": "Failed to parse multipart servlet request; nested exception is javax.servlet.ServletException: org.apache.tomcat.util.http.fileupload.FileUploadBase$InvalidContentTypeException: the request doesn't contain a multipart/form-data or multipart/mixed stream, content type header is text/xml; charset=utf-8",
For reference, this works:
$headers = [
'Authorization' => 'Bearer ' . $token,
'X-locale' => 'en_US',
'Content-Type' => 'multipart/form-data'
];
$payload =
[
'workflowName' => 'ETDPostshipment',
'name' => 'XXX.pdf',
'contentType' => 'application/pdf',
'carrierCode' => 'FDXE',
'meta' => [
'trackingNumber' => $trackingNumber,
'shipmentDate' => $shipmentDate,
'shipDocumentType' => 'COMMERCIAL_INVOICE',
'originCountryCode' => 'FR',
'destinationCountryCode' => $order->shippingAddress->countryCode
]
];
$jsonPayload = json_encode($payload);
$filePath = '/storage/XXX.pdf';
$fileName = 'XXX.pdf'; // The name of the form field
try {
// Create a multipart/form-data stream
$multipartStream = new MultipartStream([
[
'name' => 'attachment',
'contents' => fopen(Craft::getAlias('@root') . $filePath, 'r'),
'filename' => basename($filePath)
],
[
'name' => 'document',
'contents' => $jsonPayload
]
]);
$headers = [
'Authorization' => 'Bearer ' . $token,
'X-locale' => 'en_US',
'Content-Type' => 'multipart/form-data; boundary=' . $multipartStream->getBoundary()
];
// Create a PSR-7 request with the multipart stream as the body
$request = new Request(
'POST',
'https://documentapi.prod.fedex.com/documents/v1/etds/upload', // Replace with your API endpoint
$headers,
$multipartStream
);
// Send the request
$response = $client->send($request);
echo $response->getBody();
} catch (GuzzleException $e) {
dump($e->getResponse()->getBody()->getContents());
}
Ok so I had a dig in the code, and essentially this bit here is missing the 'multipart' right? https://github.com/verbb/auth/blob/d8ca65e982c0b0658dfd36a67abba3710015db23/src/base/ProviderTrait.php#L109
If I add in these lines this bit of code:
if ($multipart = ArrayHelper::remove($options, 'multipart')) {
$options['body'] = $multipart;
$boundary = ArrayHelper::remove($options, 'boundary');
$options['headers']['Content-Type'] = 'multipart/form-data; boundary='.$boundary;
}
and in my call:
$multipartStream = new MultipartStream([
[
'name' => 'attachment',
'contents' => fopen(Craft::getAlias('@root') . $filePath, 'r'),
'filename' => basename($filePath)
],
[
'name' => 'document',
'contents' => json_encode([
'workflowName' => 'ETDPostshipment',
'name' => $fileName,
'contentType' => 'application/pdf',
'carrierCode' => 'FDXE',
'meta' => [
'trackingNumber' => $trackingNumber,
'shipmentDate' => $shipmentDate,
'shipDocumentType' => 'COMMERCIAL_INVOICE',
'originCountryCode' => 'FR',
'destinationCountryCode' => $order->shippingAddress->countryCode
]
])
]
]);
$payload = [
'base_uri' => 'https://documentapi.prod.fedex.com/',
'multipart' => $multipartStream,
'boundary' => $multipartStream->getBoundary()
];
$data = Consume::$plugin->getService()->fetchData('fedexTradeUpload', 'POST', 'documents/v1/etds/upload', $payload);
then it seems to work!
Ah, good call. Yes we actually need to implement the shortcuts that Guzzle normally does, like json
being auto-encoded as JSON, as technically it uses the league/oauth2-client
package's getAuthenticatedRequest()
method to do the authenticated request.
Just added that in https://github.com/verbb/auth/commit/d5e9c1e7477e28c765f54b1749cdfa7246ee8c54
@engram-design thanks! struggling with my composer
would you mind sharing how I get that commit? I don't have auth as a standalone plugin, just through consume's requirements
Yeah, you'll need to manually include the auth package via composer require verbb/auth:"dev-craft-4 as 1.0.15"
which you can (and should) remove once I've tagged a release
This works great FYI - fine to close for me
What are you trying to do?
Hi there,
Thanks a lot for adding the regular Fedex API. Turns out they have another API called Trade Documents Upload API and it has a different URL: https://documentapi.prod.fedex.com/ and then endpoints: for documents: documents/v1/etds/upload for images: documents/v1/etds/upload
As far as I know, auth etc works like the other Fedex API.
Thank you!
What's your proposed solution?
Implement the Fedex Trade Upload API or add an option in the regular Fedex one
Additional context
No response