How can I do multipart requests mith Mojo::UserAgent? - perl

I'd like to perform a multipart file upload to Google Drive as described here using a Mojo::UserAgent. I currently do it like this:
my $url = Mojo::URL->new('https://www.googleapis.com/upload/drive/v3/files');
$url->query({ fields => 'id,parents',
ocr => 'true',
ocrLanguage => 'de',
uploadType => 'multipart' });
my $tx = $ua->post($url,
json => { parents => [ '0ByFk4UawESNUX1Bwak1Ka1lwVE0' ] },
{
'Content-Type' => 'multipart/related',
'parents' => [ '0ByFk4UawESNUX1Bwak1Ka1lwVE0' ]
},
$content );
but it doesn't work.
I've managed authorization OK (omitted here) and simple file upload works fine. I just can't seem to do the multipart.
I've tried to make sense of the docs here - but to no avail, in the sense that the file gets uploaded OK, but the JSON part gets ignored, and the file gets uploaded in the root folder.

Related

Aura Router AJAX Route Failure - Route Not Found

To preface this question, I'm converting a demo application to utilize RESTful, SEO-friendly URLs; EVERY route with the exception of one of two routes used for AJAX requests works when being used in the application on the web, and ALL the routes have been completely tested using Postman - using a vanilla Nginx configuration.
That being said, here is the offending route definition(s) - the login being the defined route that's failing:
$routing_map->post('login.read', '/services/authentication/login', [
'params' => [
'values' => [
'controller' => '\Infraweb\Toolkit\Services\Authentication',
'action' => 'login',
]
]
])->accepts([
'application/json',
]);
$routing_map->get('logout.read', '/services/authentication/logout', [
'params' => [
'values' => [
'controller' => '\Infraweb\Toolkit\Services\Authentication',
'action' => 'logout',
]
]
])->accepts([
'application/json',
]);
With Postman & xdebug tracing I think I'm seeing that it's (obviously) failing what I believe to be a REGEX check in the Path rule, but I can't quite make it out. It's frustrating to say the least. I looked everywhere I could using web searches before posting here - the Google group for Auraphp doesn't seem to get much traffic these days. It's probable I've done something incorrectly, so I figured it was time to ask the collective user community for some direction. Any and all constructive criticism is greatly welcomed and appreciated.
Thanx in advance, and apologies for wasting anyone's bandwidth on this question...
Let me make something clear. Aura.Router doesn't do the dispatching. It only matches the route. It doesn't handle how your routes are handled.
See the full working example ( In that example the handler is assumed as callable )
$callable = $route->handler;
$response = $callable($request);
In your case if you matched the request ( See matching request )
$matcher = $routerContainer->getMatcher();
$route = $matcher->match($request);
you will get the route, now you need to write appropriate ways how to handle the values from the $route->handler.
This is what I did after var_dump the $route->handler for the /signin route .
array (size=1)
'params' =>
array (size=1)
'values' =>
array (size=2)
'controller' => string '\Infraweb\LoginUI' (length=17)
'action' => string 'read' (length=4)
Full code tried below. As I mentioned before I don't know your route handling logic. So it is up to you to write things properly.
<?php
require __DIR__ . '/vendor/autoload.php';
use Aura\Router\RouterContainer;
$routerContainer = new RouterContainer();
$map = $routerContainer->getMap();
$request = Zend\Diactoros\ServerRequestFactory::fromGlobals(
$_SERVER,
$_GET,
$_POST,
$_COOKIE,
$_FILES
);
$map->get('application.signin.read', '/signin', [
'params' => [
'values' => [
'controller' => '\Infraweb\LoginUI',
'action' => 'read',
]
]
]);
$map->post('login.read', '/services/authentication/login', [
'params' => [
'values' => [
'controller' => '\Infraweb\Toolkit\Services\Authentication',
'action' => 'login',
]
]
])->accepts([
'application/json',
]);
$matcher = $routerContainer->getMatcher();
// .. and try to match the request to a route.
$route = $matcher->match($request);
if (! $route) {
echo "No route found for the request.";
exit;
}
echo '<pre>';
var_dump($route->handler);
exit;
For the record, this is the composer.json
{
"require": {
"aura/router": "^3.1",
"zendframework/zend-diactoros": "^2.1"
}
}
and running via
php -S localhost:8000 index.php
and browsing http://localhost:8000/signin

guzzle 6 post does not work

I am trying to submit a post with JSON content. I always get this message back:
"Client
error: POST
https://sandbox-api-ca.metrc.com//strains/v1/create?licenseNumber=CML17-0000001
resulted in a 400 Bad Request response: {"Message":"No data was
submitted."}"
(All keys and license number are sandbox. I changed keys slightly so auth wont work. )
here is my code
public function metrc()
{
$client = new Client();
$url = 'https://sandbox-api-ca.metrc.com//strains/v1/create?licenseNumber=CML17-0000001';
$request = $client->post($url, [
'headers' => ['Content-Type' => 'application/json'],
'json' => ['name' => "Spring Hill Kush"],
'auth' => ['kH-qsC1oJPzQnyWMrXjw0EQh812jHOX52ALfUIm-dyE3Wy0h', 'fusVbe4Yv6W1DGNuxKNhByXU6RO6jSUPcbRCoRDD98VNXc4D'],
]);
}
Your code is correct, it should works as expected. Seems that the issue is on the server side. Maybe the format of the POST request is not correct?
BTW, 'headers' => ['Content-Type' => 'application/json'] is unnecessary, Guzzle sets the header by itself automatically when you use json option.

Magento 2 Rest API Order edit

I'm trying to figure out how to edit an order after I requested it.
I made a custom attribute whether an order is exported or not.
I first get all orders with status not exported and after I exported them, I want to change the custom attribute to exported.
What is the REST request to edit/update an order? I keep getting error messages like:
{"message":"%fieldName is a required field.","parameters":
{"fieldName":"entity"}
This is my code:
$json = array(
"entity_id" => $id,
"extension_attributes" => array(
"custom_export_attribute" => "exported",
)
);
$webapi = new ApiClient('https://dev.local.nl', self::$username, self::$password);
$response = $webapi->getClient()->request('PUT', '/rest/V1/orders/create', [
'headers' => [
'Authorization' => "Bearer " . $webapi->getToken(),
'Content-Type' => "application/json"
],
'body' => json_encode($json)
]);
return json_decode($response->getBody(), true);
I also tried:
$webapi->getClient()->request('PUT', '/rest/V1/orders/'.$id,
To edit / update order details, the Magento 2 /V1/orders accepts POST request method. As per Magento 2 Dev Doc, it accepts the request body in below format (You can find whole JSON request in documentation page):
{
"entity": {
"entity_id": 0,
"extension_attributes": {
}
}
}
So, you just need to update the $json variable as:
$json = [
"entity"=> [
"entity_id" => $id,
"extension_attributes" => [
"custom_export_attribute" => "exported"
]
]
]
And than invoke with POST request method instead of PUT. In my suggestion prefer to use Create API for new order creation.

Upload files to Owncloud

I wonder, can i upload files to OwnCloud by some post or put request?
My goal - user uploads files to one server1, after submitting form his data
handles and sends to another server2 with Owncloud installed on, then returns
path to file in owncloud back. So record in server1 will have some filename
property points to owncloud storage.
(Note: I am not talking about WebDAV).
Any other capabilities?
The Own Cloud API exposes an endpoint which makes this possible (both for POST and PUT):
post(string $uri, array $options = array()) : \OCP\Http\Client\IResponse
and
put(string $uri, array $options = array()) : \OCP\Http\Client\IResponse
Parameters
string $uri array $options Array such as
'body' => [ 'field' =>
'abc', 'other_field' => '123', 'file_name' => fopen('/path/to/file',
'r'), ], 'headers' => [ 'foo' => 'bar', ], 'cookies' => [' 'foo' =>
'bar', ], 'allow_redirects' => [ 'max' => 10, // allow at most 10
redirects. 'strict' => true, // use "strict" RFC compliant redirects.
'referer' => true, // add a Referer header 'protocols' => ['https'] //
only allow https URLs ], 'save_to' => '/path/to/file', // save to a
file or a stream 'verify' => true, // bool or string to CA file
'debug' => true,
see https://doc.owncloud.org/api/classes/OCP.Http.Client.IClient.html for the relevant section in the API docs

File Upload in Rest API yii2

my controller file inside api/v1/controller/
class ProfileController extends ActiveController
{
public $modelClass = 'app\models\Profile';
public function behaviors()
{
return [
[
'class' => 'yii\filters\ContentNegotiator',
'only' =>
['index', 'view', 'createnew','update','search'],
'formats' =>
['application/json' => Response::FORMAT_JSON,],
],
'verbs' => [
'class' => VerbFilter::className(),
'actions' => [
'index' => ['get'],
'view' => ['get'],
'createnew' => ['post'],
'update' => ['put'],
'delete' => ['delete'],
'deleteall' => ['post'],
'search' => ['get']
],
]
];
}
public function actionCreatenew() {
$model = new Profile();
$model->load(Yii::$app->request->post());
$model->asset = UploadedFile::getInstance($model, 'asset');
$name = $model->user_id;
if($model->asset) {
$model->asset->saveAs('uploads/'.$name.'.
'.$model->asset->extension);
$model->asset = $model->asset->name.'.'.
$model->asset->extension;
}
if($model->save()) {
echo json_encode(array('status'=>"Success",
'data'=>$model->attributes),JSON_PRETTY_PRINT);
} else {
echo json_encode(array('status'=>"Failure",
'error_code'=>400,
'errors'=>$model->errors),JSON_PRETTY_PRINT);
}
}
}
When I try to use access this from Postman like:
POST http://localhost/myapp/api/v1/profiles
I get Invalid Parameter – yii\base\InvalidParamException
Response content must not be an array.
What is the issue?? help would be grateful!! Thanks
You can easily receive single / multi-uploaded files using HTTP POST with form-data encoding in Yii2, directly in your Yii2 Controller / action.
Use this code:
$uploads = UploadedFile::getInstancesByName("upfile");
if (empty($uploads)){
return "Must upload at least 1 file in upfile form-data POST";
}
// $uploads now contains 1 or more UploadedFile instances
$savedfiles = [];
foreach ($uploads as $file){
$path = //Generate your save file path here;
$file->saveAs($path); //Your uploaded file is saved, you can process it further from here
}
If you use Postman API client to test how your API is working, you can configure the upload endpoint to work like this for multi-file uploads:
Note: The upfile[] square brackets are important! Postman will happily let you select multiple files for upload in one slot, but this will not actually work. Doing it the way shown in the screenshot makes an array of files available to the Yii2 action, through the UploadedFile mechanism. This is roughly equivalent to the standard PHP $_FILES superglobal variable but with easier handling.
Single files can be uploaded with or without the [] square brackets after the key name. And of course you can name upfile whatever you like, for your convention.
You should use \yii\web\UploadedFile::getInstanceByName('asset'); instead of getInstance() checkout this Link