Guzzle POST request: required body when execute request - laravel

I have a POST request with Guzzle like this:
// Return a collection
$cart = $this->getCart('2019-10-08 07:08:39');
//Return first entry of the collection with first()
$template = $this->getTemplate($config->key);
$isDetail = null;
foreach ($cart as $item) {
try {
$client = $this->getClient();
$headers = ['Content-Type' => 'application/json'];
$body = [
'user_id' => $item->mystore_user_id,
'title' => $template->title,
'message' => $template->message,
'avatar' => $template->avatar,
'detail_id' => $isDetail,
'schedule' => null
];
print_r($body);
$response = $client->post('push-noti/unicast', $headers, $body);
print_r(response()->json(json_decode($response->getBody(), true)));
} catch (QueryException | \Exception $ex) {
echo "Error!";
}
}
My body variable value is exist in each loop when it printed. But when I use it in $client->post, my request return error with user_id, title, message is required. I really don't know why is it?
Can you tell me what's wrong in my code?
Thank you!

Try
$response = $client->post('push-noti/unicast', ['body' => $body , 'headers' => $headers]);
If you are calling a third party API, replace push-noti/unicast with complete URL.

Related

How to make post request to get data Laravel Guzzle?

Let say, the Secret Key is XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX and md5key is YYYYYYYY.
I made a Query String QS
Qs = “method=RegUserInfo&Key=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX&Time=20140101123456&Username=DemoUser001”;
After urlencode I got q='j4tjorjwarfj3trwise0safrwg2wt4awari0fwjfeoh'
I made MD5 String for building the signature (QS + md5key + Time + Key):
s = BuildMD5(QS + “YYYYYYYY” + “20140101123456” + “XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX”);
I got s='1234567890abcdef'
So will get q=j4tjorjwarfj3trwise0safrwg2wt4awari0fwjfeoh&s=1234567890abcdef
How to resulting POST method query (using “Content-Type: application/x-www-form-urlencoded”)
by POST to http://xxxxx.com/api/api.aspx
My code is
$param = "q=".$q."&s=".$s;
$client = new Client(['headers' => [
'Content-Type' => 'application/x-www-form-urlencoded',
]]);
try{
$response = $client->request('POST','http://xxxxxx.com/api/api.aspx', [
'query' => [$param],
]);
}catch(ClientException $e){
$response = $e->getResponse();
$responseBodyAsString = $response->getBody()->getContents();
dd($responseBodyAsString);
}
}
but I get 403 Forbidden
If you want Content-Type: application/x-www-form-urlencoded you need to use form_params request option.
try{
$client = new \GuzzleHttp\Client(['headers' => ['Authorization' => 'Bearer ' . $your_token]]);
$guzzleResponse = $client->post(
$api_url, [
'form_params' => [
'grant_type' => 'xxxxx',
'key' => 'xxx',
'time' => 'xxxx',
'username' => 'xxxxxx'
]
]);
if ($guzzleResponse->getStatusCode() == 200) {
$response = json_decode($guzzleResponse->getBody(),true);
//perform your action with $response
}
}
catch(\GuzzleHttp\Exception\RequestException $e){
// you can catch here 40X response errors and 500 response errors
}catch(Exception $e){
//other errors
}
know more about form_params

How to return response from async GuzzleHttp request to outer function or handle Exception in yii2?

I need to send async post request in background and save response (status code and request body to DB). I decide to use GuzzleHttp package (v6) for it.
The idea is run function sendAsyncRequest, send async request inside it, then get response from resource in array with keys code, data, return this array to outer function processAsyncRequest and then send it to function logResponse to save it to db.
use GuzzleHttp\Client as GuzzleClient;
class Logger
{
public function processAsyncRequest($client)
{
$response = $this->sendAsyncRequest($client->phone, ['utm_source' => $client->utm_source]);
$this->logResponse($client, $response);
}
public function sendAsyncRequest($phone, $params)
{
$url_params = http_build_query(['utm_source' => $client->utm_source]);
$guzzleClient = new GuzzleClient();
$headers = [
'Content-Type' => 'application/json',
'Authorization' => 'Basic xxxxxxxxxx',
];
$request = new Request('POST', 'url' . $phone . '/tokens/?' . $url_params, $headers);
$promise = $guzzleClient->sendAsync($request);
$promise->then(
function (ResponseInterface $response) {
return [
'code' => $response->getStatusCode(),
'body' => $response->getBody()->__toString(),
];
},
function(RequestException $e) {
return [
'code' => $e->getResponse()->getStatusCode(),
'body' => $e->getMessage(),
];
}
);
$res = $promise->wait();
return $res;
}
public function logResponse($client, $data)
{
$log = new Log();
$log->client_id = $client->id;
$log->url = 'url';
$log->response = $data['code'] . ', ' . $data['body'];
$log->comment = 'reg';
return $log->save();
}
}
The problems are:
function sendAsyncRequest returns object of GuzzleHttp\Psr7\Response, I see the error "Cannot use object of type GuzzleHttp\Psr7\Response as array" and I have no idea how to get my $res array from it.
how to correctly handle exception if promise will return error?

Method not allowed exception while updating a record

Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpExceptionNomessage
I'm getting this error while trying to update a record in the database. Don't know what's the problem. This question might be a duplicate but I've checked all and couldn't find the answer. Please Help me with this.
Controller Update Method:
public function updateEvent(Request $request, $id=''){
$name = $request->name;
$startdate = date_create($request->start_date);
$start_date = $startdate;
$time = $request->start_time;
$start_time = $time;//date("G:i", strtotime($time));
$endDate = date_create($request->end_date);
$end_date =$endDate;
$time_e = $request->end_time;
$end_time = $time_e;//date("G:i", strtotime($time_e));
$location = $request->location;
$description = $request->description;
$calendar_type = $request->calendar_type;
$timezone = $request->timezone;
if ($request->has('isFullDay')) {
$isFullDay = 1;
}else{
$isFullDay = 0;
}
DB::table('events')->where('id', $id)->update(
array(
'name' => $name,
'start_date' => $start_date,
'end_date' => $end_date,
'start_time' => $start_time,
'end_time' => $end_time,
'isFullDay' =>$isFullDay,
'description' => $description,
'calendar_type' => $calendar_type,
'timezone' => $timezone,
'location' => $location,
));
// Event Created and saved to the database
//now we will fetch this events id and store its reminder(if set) to the event_reminder table.
if(!empty($id))
{
if (!empty($request->reminder_type && $request->reminder_number && $request->reminder_duration)) {
DB::table('event_reminders')->where('id', $id)->update([
'event_id' => $id,
'type' => $request->reminder_type,
'number'=> $request->reminder_number,
'duration' => $request->reminder_duration
]);
}
}
else{
DB::table('event_reminders')->insert([
'type' => $request->reminder_type,
'number'=> $request->reminder_number,
'duration' => $request->reminder_duration
]);
}
return redirect()->back();
}
Route:
Route::post('/event/update/{id}', 'EventTasksController#updateEvent');
Form attributes :
<form action="/event/update/{{$event->id}}" method="POST">
{{ method_field('PATCH')}}
i'm calling the same update function inside my calendar page and it working fine there. Don't know why it doesn't work here.
Check the routeing method.
Route::patch('/event/update/{id}', 'EventTasksController#updateEvent');
patch should be the method called on route facade.
Change your route to patch:
Route::patch('/event/update/{id}', 'EventTasksController#updateEvent');
For your comment:
You can send the method to the ajax call by something like data-method attribute on the element you click on,take the method and use it in your ajax call. see this question/answer for help. How to get the data-id attribute?

Better way for testing validation errors

I'm testing a form where user must introduce some text between let's say 100 and 500 characters.
I use to emulate the user input:
$this->actingAs($user)
->visit('myweb/create')
->type($this->faker->text(1000),'description')
->press('Save')
->see('greater than');
Here I'm looking for the greater than piece of text in the response... It depends on the translation specified for that validation error.
How could do the same test without having to depend on the text of the validation error and do it depending only on the error itself?
Controller:
public function store(Request $request)
{
$success = doStuff($request);
if ($success){
Flash::success('Created');
} else {
Flash::error('Fail');
}
return Redirect::back():
}
dd(Session::all()):
`array:3 [
"_token" => "ONoTlU2w7Ii2Npbr27dH5WSXolw6qpQncavQn72e"
"_sf2_meta" => array:3 [
"u" => 1453141086
"c" => 1453141086
"l" => "0"
]
"flash" => array:2 [
"old" => []
"new" => []
]
]
you can do it like so -
$this->assertSessionHas('flash_notification.level', 'danger'); if you are looking for a particular error or success key.
or use
$this->assertSessionHasErrors();
I think there is more clear way to get an exact error message from session.
/** #var ViewErrorBag $errors */
$errors = request()->session()->get('errors');
/** #var array $messages */
$messages = $errors->getBag('default')->getMessages();
$emailErrorMessage = array_shift($messages['email']);
$this->assertEquals('Already in use', $emailErrorMessage);
Pre-requirements: code was tested on Laravel Framework 5.5.14
get the MessageBag object from from session erros and get all the validation error names using $errors->get('name')
$errors = session('errors');
$this->assertSessionHasErrors();
$this->assertEquals($errors->get('name')[0],"The title field is required.");
This works for Laravel 5 +
Your test doesn't have a post call. Here is an example using Jeffery Way's flash package
Controller:
public function store(Request $request, Post $post)
{
$post->fill($request->all());
$post->user_id = $request->user()->id;
$created = false;
try {
$created = $post->save();
} catch (ValidationException $e) {
flash()->error($e->getErrors()->all());
}
if ($created) {
flash()->success('New post has been created.');
}
return back();
}
Test:
public function testStoreSuccess()
{
$data = [
'title' => 'A dog is fit',
'status' => 'active',
'excerpt' => 'Farm dog',
'content' => 'blah blah blah',
];
$this->call('POST', 'post', $data);
$this->assertTrue(Post::where($data)->exists());
$this->assertResponseStatus(302);
$this->assertSessionHas('flash_notification.level', 'success');
$this->assertSessionHas('flash_notification.message', 'New post has been created.');
}
try to split your tests into units, say if you testing a controller function
you may catch valication exception, like so:
} catch (ValidationException $ex) {
if it was generated manually, this is how it should be generated:
throw ValidationException::withMessages([
'abc' => ['my message'],
])->status(400);
you can assert it liks so
$this->assertSame('my message', $ex->errors()['abc'][0]);
if you cannot catch it, but prefer testing routs like so:
$response = $this->json('POST', route('user-post'), [
'name' => $faker->name,
'email' => $faker->email,
]);
then you use $response to assert that the validation has happened, like so
$this->assertSame($response->errors->{'name'}[0], 'The name field is required.');
PS
in the example I used
$faker = \Faker\Factory::create();
ValidationException is used liks this
use Illuminate\Validation\ValidationException;
just remind you that you don't have to generate exceptions manually, use validate method for common cases:
$request->validate(['name' => [
'required',
],
]);
my current laravel version is 5.7

HybridAuth send tweet with image

I'm using the HybridAuth library.
I'd like to be able to post message to my authenticated users twitter profile with images.
The setUserStatus method works well to automatically send a tweet.
I wrote the following method :
function setUserStatus( $status, $image )
{
//$parameters = array( 'status' => $status, 'media[]' => "#{$image}" );
$parameters = array( 'status' => $status, 'media[]' => file_get_contents($image) );
$response = $this->api->post( 'statuses/update_with_media.json', $parameters );
// check the last HTTP status code returned
if ( $this->api->http_code != 200 ){
throw new Exception( "Update user status failed! {$this->providerId} returned an error. " . $this->errorMessageByStatus( $this->api->http_code ) );
}
}
The message I get from twitter is :
Ooophs, we got an error: Update user status failed! Twitter returned an error. 403 Forbidden: The request is understood, but it has been refused.
How Can I get more precise info about error ?
Does anybody allready success in sending a picture attached to a tweet ?
Thanks !
Hugo
Thanks #Heena for making myself wake up on this question, I MADE IT ;)
function setUserStatus( $status )
{
if(is_array($status))
{
$message = $status["message"];
$image_path = $status["image_path"];
}
else
{
$message = $status;
$image_path = null;
}
$media_id = null;
# https://dev.twitter.com/rest/reference/get/help/configuration
$twitter_photo_size_limit = 3145728;
if($image_path!==null)
{
if(file_exists($image_path))
{
if(filesize($image_path) < $twitter_photo_size_limit)
{
# Backup base_url
$original_base_url = $this->api->api_base_url;
# Need to change base_url for uploading media
$this->api->api_base_url = "https://upload.twitter.com/1.1/";
# Call Twitter API media/upload.json
$parameters = array('media' => base64_encode(file_get_contents($image_path)) );
$response = $this->api->post( 'media/upload.json', $parameters );
error_log("Twitter upload response : ".print_r($response, true));
# Restore base_url
$this->api->api_base_url = $original_base_url;
# Retrieve media_id from response
if(isset($response->media_id))
{
$media_id = $response->media_id;
error_log("Twitter media_id : ".$media_id);
}
}
else
{
error_log("Twitter does not accept files larger than ".$twitter_photo_size_limit.". Check ".$image_path);
}
}
else
{
error_log("Can't send file ".$image_path." to Twitter cause does not exist ... ");
}
}
if($media_id!==null)
{
$parameters = array( 'status' => $message, 'media_ids' => $media_id );
}
else
{
$parameters = array( 'status' => $message);
}
$response = $this->api->post( 'statuses/update.json', $parameters );
// check the last HTTP status code returned
if ( $this->api->http_code != 200 ){
throw new Exception( "Update user status failed! {$this->providerId} returned an error. " . $this->errorMessageByStatus( $this->api->http_code ) );
}
}
To make it work you have to do like this :
$config = "/path_to_hybridauth_config.php";
$hybridauth = new Hybrid_Auth( $config );
$adapter = $hybridauth->authenticate( "Twitter" );
$twitter_status = array(
"message" => "Hi there! this is just a random update to test some stuff",
"image_path" => "/path_to_your_image.jpg"
);
$res = $adapter->setUserStatus( $twitter_status );
Enjoy !
I did not understand it for hybridauth then I used this library
https://github.com/J7mbo/twitter-api-php/archive/master.zip
Then I was successful using code below: (appears elsewhere in stack)
<?php
require_once('TwitterAPIExchange.php');
$settings= array(
'oauth_access_token' => '';
'oauth_access_secret' => '';
'consumer_key' => '';
'consumer_secret' => '';
// paste your keys above properly
)
$url_media = "https://api.twitter.com/1.1/statuses/update_with_media.json";
$requestMethod = "POST";
$tweetmsg = $_POST['post_description']; //POST data from upload form
$twimg = $_FILES['pictureFile']['tmp_name']; // POST data of file upload
$postfields = array(
'status' => $tweetmsg,
'media[]' => '#' . $twimg
);
try {
$twitter = new TwitterAPIExchange($settings);
$twitter->buildOauth($url_media, $requestMethod)
->setPostfields($postfields)
->performRequest();
echo "You just tweeted with an image";
} catch (Exception $ex) {
echo $ex->getMessage();
}
?>

Resources