Laravel Http Client cannot attach files contents required - laravel

I have been looking for the answer but failed to do so.
The problem is simple, I am trying to make an HTTP request using Illuminate\Support\Facades\Http class as documented here https://laravel.com/docs/8.x/http-client#multi-part-requests.
My code:-
$picture = fopen(public_path('/temp_pass/image.jpeg'),'r');
$response = Http::attach('attachment', $picture)
->withHeaders(['Accept'=>'application/json'])
->post('mydomain.com/item/store',['item_id' => 'my_item_id']);
The picture URL is ok. The domain to send to also ok. Unfortunately, I got this error
A 'contents' key is required
I hope great developers here can help me solve this.

The line Http::attach('attachment', $picture) refers to the following file and method.
Illuminate\Http\Client\PendingRequest.php
public function attach($name, $contents = '', $filename = null, array $headers = [])
A 'contents' key is required occurs when $contents is falsy (empty, null, false, 0, etc.) based on my testing.
In your case, this refers to $picture. You should check that $picture actually contains a file stream as expected. Dump it before the HTTP request to test.
dd($picture);
// stream resource #7 ▼
// timed_out: false
// blocked: true
// eof: false
// wrapper_type: "plainfile"
// stream_type: "STDIO"
// mode: "r"
// unread_bytes: 0
// seekable: true
// uri: "/.../image.jpeg"
// options: []
// }

Related

Unable to get Keycloak client initiated client account linking to work

The request to start the client iniated account linking fails.
The console is showing a WARN of type: CLIENT_INITIATED_ACCOUNT_LINKING_ERROR with error: invalid_token.
The url was generated as described here: https://www.keycloak.org/docs/latest/server_development/#client-initiated-account-linking, by php backend system.
Also making sure to use UTF8 encoding when generating the hash
All prerequisites as describe it the section have been fulfilled.
Im' using Keycloak 15.0.2 and Laravel with Socialite to authenticate users.
This is how the hash is generated.
$keycloack_user = Socialite::driver('keycloak')->user();
$bearerToken = $keycloack_user->token;
$tokenParts = explode(".", $bearerToken);
$tokenHeader = base64_decode($tokenParts[0]);
$tokenPayload = base64_decode($tokenParts[1]);
$jwtHeader = json_decode($tokenHeader);
$jwtPayload = json_decode($tokenPayload);
$client_id = $jwtPayload->azp;
$host = $jwtPayload->iss;
$session_state = $jwtPayload->session_state;
$nonce = Str::random(20);
$provider = "google";
$input = $nonce . $session_state . $client_id . $provider;
$utf8encoded = utf8_encode($input);
$hashed = hash('sha256', $utf8encoded);
$encoded = rtrim(strtr(base64_encode($hashed), '+/', '-_'), '=');
Then the linking url is constructed as shown below:
$redirect_uri = urlencode(...);
$full_url = $host . "/broker/". $provider ."/link?client_id=". $client_id ."&redirect_uri=". $redirect_uri ."&nonce=". $nonce ."&hash=" . $encoded;
I'm currently testing a my local machine, without using https for any of the applications. Loging in works fine and when inspecting the JWT token, the needed role mappings are present:
"account": {
"roles": [
"manage-account",
"manage-account-links",
"view-profile"
]
}
But when accessing the url it says "Invalid request" and the Keycloak console indicates the token is invalid.
Update: Solution was to return the result of the hash method as raw binary data
$hashed = hash('sha256', $utf8encoded, true);
I had to work on the same task lately but with the client implemented in JavaScript. I was also stuck for quite a while till I realized how uncommonly keycloak is expecting the encoded hash value. You need to consider following two points:
Encode the hash string into hexadecimal before base64 conversion
Replace + by - and / by _. Besides that remove trailing = symbols
Below you find a working snippet written in JS:
import sjcl from "sjcl";
hexToBase64(hexstring) {
return btoa(hexstring.match(/\w{2}/g).map(function(a) {
return String.fromCharCode(parseInt(a, 16));
}).join(""));
},
// Assume nonce, session_state, clientId, provider to be given
var data = nonce + session_state + clientId + provider;
var myBitArray = sjcl.hash.sha256.hash(data)
var hashedData = sjcl.codec.hex.fromBits(myBitArray)
var base64HashedData = this.hexToBase64(HashedData)
base64HashedData = base64HashedData.replaceAll('+','-').replaceAll('/','_').replaceAll('=','')
base64HashedData is then what you need to pass as hash query parameter to the link endpoint of keycloak.

Cypress - extract URL info

I have this URL :
https://www.acme.com/book/passengers?id=h1c7cafc-5457-4564-af9d-2599c6a37dde&hash=7EPbMqFFQu8T5R3AQr1GCw&gtmsearchtype=City+Break
and want to store these values :
id=h1c7cafc-5457-4564-af9d-2599c6a37dde
hash=7EPbMqFFQu8T5R3AQr1GCw
for use in a later test.
How do I extract these values from the URL? I am using Cypress. Thanks.
Please follow the following steps and that's all there is to it.
You can put this snippet into before() hooks of your spec file and you can access them wherever you want.
cy.location().then(fullUrl => {
let pathName = fullUrl.pathname
let arr = pathName.split('?');
let arrayValues = arr[1].split('&');
cy.log(arrayValues[0]);
cy.log(arrayValues[1]);
cy.log(arrayValues[2]);
})
In case anyone needs the correct answer, use the cy.location('search') to extract the search part of the location data.
Then for convenience, convert it to a javascript object with key/value pairs for each item.
Finally, store it in a Cypress alias to use later in the test.
cy.location('search')
.then(search=> {
const searchValues = search.split('?')[1].split('&')
// yields: [
// id=h1c7cafc-5457-4564-af9d-2599c6a37dde,
// hash=7EPbMqFFQu8T5R3AQr1GCw,
// gtmsearchtype=City+Break
// ]
const searchMap = searchValues.reduce((acc,item) => {
const [key,value] = item.split('=')
acc[key] = value.replace('+', ' ')
return acc
}, {})
// yields: {
// id: "h1c7cafc-5457-4564-af9d-2599c6a37dde",
// hash: "7EPbMqFFQu8T5R3AQr1GCw",
// gtmsearchtype: "City Break"
// }
cy.wrap(searchMap).as('searchMap')
})
Using #Srinu Kodi's answer I got it working changing ...then(fullUrl => ... to
...then((fullUrl) => ...

Laravel issue Credentials are required to create a Client

I need to test send SMS to mobile I get Credentials are required to create a Client error for My Code Here
.env
TWILIO_ACCOUNT_SID=AC15...................
TWILIO_AUTH_TOKEN=c3...................
TWILIO_NUMBER=+1111...
Config\App
'twilio' => [
'TWILIO_AUTH_TOKEN' => env('TWILIO_AUTH_TOKEN'),
'TWILIO_ACCOUNT_SID' => env('TWILIO_ACCOUNT_SID'),
'TWILIO_NUMBER' => env('TWILIO_NUMBER')
],
Controller
$accountSid = env('TWILIO_ACCOUNT_SID');
$authToken = env('TWILIO_AUTH_TOKEN');
$twilioNumber = env('TWILIO_NUMBER');
$client = new Client($accountSid, $authToken);
try {
$client->messages->create(
'0020109.....',
[
"body" => 'test',
"from" => $twilioNumber
// On US phone numbers, you could send an image as well!
// 'mediaUrl' => $imageUrl
]
);
Log::info('Message sent to ' . $twilioNumber);
} catch (TwilioException $e) {
Log::error(
'Could not send SMS notification.' .
' Twilio replied with: ' . $e
);
}
Twilio developer evangelist here.
A quick read over the environment config for Laravel suggests to me that you can use the env method within your config files, as you are doing, but it's not necessarily available in application code. Since you are committing your environment variables to the config object, I think you need to use the config method instead.
$accountSid = config('TWILIO_ACCOUNT_SID');
$authToken = config('TWILIO_AUTH_TOKEN');
$twilioNumber = config('TWILIO_NUMBER');
Let me know if that helps at all.

Laravel write file stream

I am using guzzle to downlaod file from url and save it into my storage.
So I have a code look like this
$response = $this->client->request('GET', $model->url, [
'stream' => true
]);
$body = $response->getBody();
while (!$body->eof()) {
Storage::append($this->filePath, $body->read(1024));;
}
but when I open the folder where my file is located, I see that the file size is changing and sometimes it is zero. So in the end I am getting a invalid file.
How can I solve this problem?

Glide library | image not loading up

im trying to enable this library for my localhost environment.
http://glide.thephpleague.com/1.0/config/integrations/laravel/
Current Laravel Version 5.5
gd2 is enabled in wamp extensions.
I cant seem to find where the problem is.
Path is ok, image exists on it.
See following code for server config.
$server = ServerFactory::create([
'response' => new LaravelResponseFactory(app('request')),
'source' => $source,
//'cache' => new Filesystem(new Adapter('../storage/app/cache/')),
'cache' => $cache,
'cache_path_prefix' => '.cache',
'base_url' => 'transform-img',
]);
now i use this
return $server->getImageResponse($path, request()->all());
it does not give any error.
when i dd() this, i get this response.
StreamedResponse {#1151 ▼
#callback: Closure {#1177 ▶}
#streamed: false
-headersSent: false
+headers: ResponseHeaderBag {#1176 ▶}
#content: null
#version: "1.0"
#statusCode: 200
#statusText: "OK"
#charset: null
}
Callback Closure:
#callback: Closure {#1252 ▼
class: "League\Glide\Responses\SymfonyResponseFactory"
this: LaravelResponseFactory {#1231 …}
use: {▼
$stream: stream resource #543 ▼
timed_out: false
blocked: true
eof: false
wrapper_type: "plainfile"
stream_type: "STDIO"
mode: "rb"
unread_bytes: 0
seekable: true
uri: "D:\wamp\www\Bankrolla\storage\app/public\.cache/img/logo_no_text.png/32c8e67d979eab40a7ef6d1854f1f7cc"
options: []
}
}
file: "D:\wamp\www\Bankrolla\vendor\league\glide-symfony\src\Responses\SymfonyResponseFactory.php"
line: "48 to 54"
}
as statusCode shows 200 and there is no error for file not found, still it does not load any image but shows a placeholder on browser when i navigate.
What can be the issue. if i try to replace image name with any other random string i get error for image not found. so this means it does find the image. thou it fails to render the image.
I have googled, searched on their github comments, could not find any problem similar as mine.
I only get a blank page/image if i load it directly.
also i looked in to the cache directory, it includes the files and those files dimensions are resized. so i am not sure where it goes wrong even when it generates the cache files.
may be i am missing any point here, if anyone can point me to the right direction would be very helpful.
Update:
Value of $source variable:
Filesystem {#1225 ▼
#adapter: Local {#1226 ▼
#pathSeparator: "\"
#permissionMap: array:2 [▼
"file" => array:2 [▼
"public" => 420
"private" => 384
]
"dir" => array:2 [▼
"public" => 493
"private" => 448
]
]
#writeFlags: 2
-linkHandling: 2
#pathPrefix: "D:\wamp\www\Bankrolla\storage\app/public\"
}
#plugins: []
#config: Config {#1229 ▼
#settings: []
#fallback: null
}
}
Storage Directory in my public directory(its a symbolic link of original storage)
Storage Directory of Laravel
The URL i am calling this from.
{localhostDomainHere}/image/img/logo_no_text.png?w=100&h=100&fit=crop-center
Don't know if you already fixed it. But we encountered the same problem a few days ago. After a long search, we found out that the error is caught by a new line in a config:
So check all your config files for space or newline before openings tag. Otherwise, your response is not a valid response anymore and you will get the empty box.
If it is not a config file you need to check all the files that are loaded on the request.
the path mentioned by $source => put images there. if it is 1.jpg, then call the url server_url/img/1.jpg . img is from your route for the function you posted in question. In my case $source as well as $cache was /storage/app and i put image in it and called the route on that image. Hope this helps you.
Check this
This is my code :
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Contracts\Filesystem\Filesystem;
use League\Glide\Responses\LaravelResponseFactory;
use League\Glide\ServerFactory;
class GlideController extends Controller
{
public function show(Filesystem $filesystem, $path)
{
$server = ServerFactory::create([
'response' => new LaravelResponseFactory(app('request')),
'source' => $filesystem->getDriver(),
'cache' => $filesystem->getDriver(),
'cache_path_prefix' => '.cache',
'base_url' => 'img',
]);
return $server->getImageResponse($path, request()->all());
}
}
route :
Route::get('/img/{path}', 'GlideController#show')->where('path', '.*');
this is the content of my storage/app

Resources