Lumen job dispatching done without database Queue Driver - laravel

What do I have:
Lumen service which processing particular Job
Laravel portal which sending file to that service for processing by it
Once it was using only JS and Ajax it worked almost fine - the only what I had to implement is CORS middleware. However after I moved logic to JWT (using jwt-auth package) and GuzzleHttp (I'm using it to send requests to service API) Job stopped processing throught database queue instead it running as if Queue driver being set to sync.
Following is controller which I'm calling during API call:
public function processPackageById(Request $request) {
$id = $request->package_id;
$package = FilePackage::where('id', '=', $id)->where('package_status_id', '=', 1)->first();
if($package) {
Queue::push(new PackageProcessingJob(
$this->firm,
$this->accounts,
$package
));
return 'dispatching done for ' . $id;
}
return 'dispatching not done for ' . $id;
}
where $this->firm and $this->accounts are injected Repositories for particular models. FilePackage object being created on Laravel site and both shares same database to work with.
As result no job being incerted into jobs table. When I use Postman everything is fine. However when I'm trying to send request from Laravel backend:
public function uploaderPost(Request $request)
{
// Here we get auth token and put into protected valiable `$this->token`
$this->authorizeApi();
$requestData = $request->except('_token');
$package = $requestData['file'];
$uploadPackageRequest =
$this->client->request('POST', config('bulk_api.url') .'/api/bulk/upload?token=' . $this->token,
[
'multipart' => [
[
'name' => 'file',
'contents' => fopen($package->getPathName(), 'r'),
'filename' => $package->getClientOriginalName(),
],
]
]);
$uploadPackageRequestJson = json_decode($uploadPackageRequest->getBody()->getContents());
$uploadPackageRequestStatus = $uploadPackageRequestJson->status;
if($uploadPackageRequestStatus == 1) {
$package = BulkUploadPackage::where('id', '=',$uploadPackageRequestJson->id)->first();
// If package is okay - running it
if($package !== null){
// Here where I expect job to be dispatched (code above)
$runPackageRequest =
$this->client->request('POST', config('api.url') .'/api/bulk/run?token=' . $this->token,
[
'multipart' => [
[
'name' => 'package_id',
'contents' => $package->id
],
]
]);
// Here I'm receiving stream for some reason
dd($runPackageRequest->getBody());
if($runPackageRequest->getStatusCode()==200){
return redirect(url('/success'));
}
}
}
return back();
}
Could anyone advise me what is wrong here and what causes the issue?
Thank you!

Alright, it was really interesting. After echoing config('queue.default') in my contoller it appeared that it's value indeed sync nevertheless that I set everything correctly.
Then I assumed that maybe the reason in Laravel itself and its variables. Indeed in .env file from Laravel side QUEUE_DRIVER being set to sync. After I changed it to QUEUE_DRIVER=database everything started working as expected.
Hope that will help someone in future.

Related

Lumen : 1s, Laravel : 5s for a simple api call?

So I started a project not long ago for an API on Laravel, and I thought why not give Lumen a shot. But in the end, I want to use Sanctum, Socialite, etc... And I read pretty much everywhere that the performance difference is not that big nowadays anyway.
So I migrated my code from Lumen to Laravel, and after a few tweaks, everything works as before... Except that now a very simple API call takes 5s. Granted, it might be my setup - wsl2 isn't particularly fast. But still, the same call in Lumen was taking ~1000ms.
Route::post('register', [AuthController::class, 'register']);
Controller:
public function register(Request $request): JsonResponse {
$this->validate($request, [
'phone' => 'required|string|phone',
'phone_country' => 'required_with:phone',
]);
$phone = phone($request->get('phone'), [$request->get('phone_country')]);
try {
$user = User::createByPhone($phone);
return response()->json(['user' => $user->id, 'message' => 'SMS_SENT'], 201);
} catch (\Exception $e) {
return response()->json(['message' => 'User Registration Failed - ', 'error' => $e], 409);
}
}
Function in model:
public static function createByPhone($phone) {
return DB::transaction(function () use ($phone) {
$user = User::create();
$user->phoneNumbers()->create([
'did' => $phone
]);
return $user;
});
}
So, pretty simple stuff. Now, why is that taking so long? ~6000ms. Am I missing something?
(On a more general note, is there a way to cut from Laravel things that aren't needed for an API only?)
Thanks ahead.
I don't see anything really wrong with your code. I guess this has something to do with the speed wsl2 can read files. We had issues with windows machines and Laravel in Docker. We added Swoole to our project and this helped alot on WSL2.
Laravel now has a first party package called Octane to add Swoole to your project. You can try and install that to see if it helps.

Nova Laravel Unit Testing Expecting Return 201, But Return 403

I'm gonna make a Unit Testing for my resource.
Here my testing function below :
public function testCreateMyResource()
{
$user = factory(\App\User::class)->states('admin')->create();
$this->actingAs($user);
$data = [
'Field' => "Example",
];
$response = $this->actingAs($user)->postJson('/nova-api/my-resource?editing=true&editMode=create',$data);
$response->assertStatus(201);
$response->assertJson(['status' => true]);
$response->assertJson(['message' => "Created!"]);
}
But it was return 403.
I expected to return 201 as I login normally to Nova dashboard and create new record in the form.
It seems like forbidden to access the route inside Testing Class.
Is there anyway to access the route ? Please any body help me to solve this.

Cant get callback from webhook laravel

I am trying to create a webhook for a subscription based API which sends me JSON data whenever an IoT device undergoes a change. I cant seem to fire the function up and i cannot figure out the reason behind it.
The data that the API will give me is as follows:
{"data:{"type":1,"value":1,"dev_id":5,"attr_id":0},
"ack":"ok","action":"upload","mac":"C8EEA63070CF"}
My webhook function:
class webHookController extends Controller
{
public function webhook(Request $request)
{
$options = array(
'cluster' => 'ap2',
'useTLS' => true
);
$pusher = new \Pusher\Pusher(
'cant',
'show',
'these',
$options
);
$pusher->trigger("n-channel", 'n-event',$request['data']);
$thinker = t::where('thinker_MAC',$request['mac'])->first();
$slave = sd::where('connected_thinker_MAC',$request['mac'])->get();
if(count($slave) > 0 && $thinker->user_id != NULL)
{
$pusher->trigger($u->id."-channel", 'n-event',$request);
}
else
{
}
return "hooked";
}
}
My route in api.php:
Route::post('/webhook','webHookController#webhook');
Proof that the subscription works:
I have also added the route to ignore csrf Tokens.
protected $except = [
'/webhook',
];
I can run my function if i use postman .Any Help would be greatly appreciated.
The things were implemented perfectly. Just needed to use ngrok to test. Answering for people who come looking for the answer to the same problem. Seems no one here knew the answer.

Laravel Return control to caller and continue working

I have two APIs that should communicate with each other. The first one sends the second files, which must be processed. However the processing time is unknown and may take a while.
A solution I thought of is the following - when the processing is ready, the second API sends a request to the first one with the result.
The problem is that in all that time, the first API's request is waiting for a response, because I haven't returned yet the control, which leads into a deadlock.
My question is how can I return a result to the first API (something like received: true/false) as soon as possible, continue with the processing and send the result (from the processing) in a separate request.
I am using Laravel 5.2 and Guzzle for the requests.
Firstly, this is proof of concept and not working code and may require some rework.
From your first api, you should be making a call to the second api telling it to process something.
The second api should then respond once it has received the data (not once processed).
First API
use GuzzleHttp\Client;
public function callApiTwo(Request $request)
{
$client = new Client()
$data = [];
$res = $client->post('http://apitwoexample.com', [
'body' => json_encode($data)
]);
if ($res->getStatus() !== 203) {
// error in processing
}
$resData = json_decode($res->getBody()->getContents());
$token = $resData['token'];
return response()->json([
'success' => 'Data is being processed',
'token' => $token
], 202);
}
Second API
The second api should then receive data (files links, images, data etc) and dispatch a job (as mentioned by #jonathon mentioned).
You then need some sort of identifier for the process/job/record. A id or token of some sort. This is sent back to the first api in its response.
public function executeProcess(Request $request)
{
// Perform logic
$token = str_rand(10);
Job::dispatch('/job/which/needs/time/to/run', [
'data' => $data,
'token' => $token
]);
return response()->json([
'success' => 'Data is being processed',
'token' => $token
], 202);
}
public function progressStatus(Request $request)
{
$model = Model::whereToken($request->token)->first();
return response()->json([
'status' => $model->status
], 200);
}
You'll notice the progressStatus method, which i've put as an example to check the progess of a process/job/record by some identifier.
From the first api, this can be fired off within an artisan command or a job which is re-queued to fire again (after a delay) based on whether the second api has confirmed the process completed.
Let me know if this requires more clarity.

How To Test Artisan Commands with ask() in Laravel 5.4

Trying to write a test for laravel php artisan command with ask() function. I have never used mockery before, but when i try to run test, it freezes, so i guess, i'm doing something wrong.
MyCommand.php:
public function handle()
{
$input['answer1'] = $this->ask('Ask question 1');
$input['answer2'] = $this->ask('Ask question 2');
$input['answer3'] = $this->ask('Ask question 3');
//--- processing validation
$validator = Validator::make($input, [
'answer1' => 'required',
'answer2' => 'required',
'answer3' => 'required',
]);
if ($validator->fails()) {
// processing error
}
} else {
// saving to DB
}
}
And my unit test:
$command = m::mock('\App\Console\Commands\Questions');
$command->shouldReceive('ask')
->andReturn('Answer 1')
->shouldReceive('ask')
->andReturn('Answer 2')
->shouldReceive('ask')
->andReturn('Answer 3')
$this->artisan('myCommand:toRun');
$this->assertDatabaseHas('myTable', [
'question1' => 'answer1'
]);
Laravel 5.4 - 5.6
The actual issue here is that running the console command is waiting for user input, however we are running this through PHPUnit so we are unable to enter anything.
Bumping up against limitations in unit testing can initially be frustrating, however limitations you find can end up being a blessing in disguise.
Currently, your implementation is tightly coupled to a view (a console command, so a view to an admin, but still a view none-the-less.) What could be done here is place any logic within a separate class which MyCommand can utilize, and which PHPUnit can actually test on their own. We know that the fundamentals of running a custom command work, as demonstrated in Laravel unit tests, so we can offload our logic in a separate, testable class.
Your new class might look something like this:
class CommandLogic
{
public function getQuestion1Text()
{
return 'Ask question 1';
}
public function getQuestion2Text()
{
return 'Ask question 2';
}
public function getQuestion3Text()
{
return 'Ask question 3';
}
public function submit(array $input)
{
$validator = \Illuminate\Support\Facades\Validator::make($input, [
'answer1' => 'required',
'answer2' => 'required',
'answer3' => 'required',
]);
if ($validator->fails()) {
// processing error
} else {
// saving to DB
}
}
}
...your actual unit test, something like this:
$commandLogic = new CommandLogic();
$sampleInput = [
'answer1' => 'test1',
'answer2' => 'test2',
'answer3' => 'test3',
];
$commandLogic->submit($sampleInput);
$this->assertDatabaseHas('myTable', [
'question1' => 'test1'
]);
...and your console command, something like this:
public function handle()
{
$commandLogic = new CommandLogic();
$input['answer1'] = $this->ask($commandLogic->getQuestion1Text());
$input['answer2'] = $this->ask($commandLogic->getQuestion2Text());
$input['answer3'] = $this->ask($commandLogic->getQuestion3Text());
$commandLogic->submit($input);
}
This enforces the single responsibility principle and separates the moving pieces in your codebase. I know this may feel like a bit of a cop out, but testing this stuff in Laravel 5.4 is tough. If you are willing to upgrade to 5.7 or higher, read below...
Laravel 5.7+
Laravel 5.7 introduced being able to run console tests, which satisfies the exact requirement this question is asking - https://laravel.com/docs/5.7/console-tests. This is more of a full integration test rather than a unit test.

Resources