Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add conditional query watcher and convenience methods for update, delete, insert and select queries #359

Merged
merged 23 commits into from
Dec 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
f2ae7d0
Add ConditionalQueryWatcher
patrickomeara Jul 25, 2024
57ff4ca
Show delete queries
patrickomeara Jul 25, 2024
fb42c39
Show Insert queries
patrickomeara Jul 25, 2024
a3a947a
show select queries
patrickomeara Jul 25, 2024
7bcbad6
Use data provider to clean up tests
patrickomeara Jul 25, 2024
48fb680
Move into separate test
patrickomeara Jul 25, 2024
9e41ad8
This doesn't register only listens
patrickomeara Jul 25, 2024
cc39935
UpdateQueryWatcher
patrickomeara Jul 25, 2024
5893ca3
DeleteQueryWatcher
patrickomeara Jul 25, 2024
abb83b3
InsertQueryWatcher
patrickomeara Jul 25, 2024
b3dfefe
SelectQueryWatcher
patrickomeara Jul 25, 2024
49fe7a1
Add stop to tests
patrickomeara Jul 25, 2024
252f68c
test conditional query
patrickomeara Jul 26, 2024
abf3d60
Test multiple conditional watchers with nesting and return type
patrickomeara Jul 26, 2024
860f838
Run register and type watches
patrickomeara Jul 26, 2024
15abf7c
format
patrickomeara Jul 26, 2024
26caf6a
Don't use __construct
patrickomeara Jul 26, 2024
a0dcbec
Only child and parent classes of ConditionalQueryWatcher can call reg…
patrickomeara Jul 26, 2024
cf5bfc3
Let ConditionalQueryWatcher handle binding and naming
patrickomeara Jul 26, 2024
dd4a49b
Pass full QueryExecuted object to callback
patrickomeara Jul 26, 2024
ac4647e
Use ConditionalQueryWatcher to clean up SlowQueryWatcher
patrickomeara Jul 26, 2024
114c2ba
Use Str to support PHP 7.4
patrickomeara Jul 26, 2024
8570d28
Support Laravel 7
patrickomeara Jul 26, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 75 additions & 0 deletions src/Ray.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,19 @@
use Spatie\LaravelRay\Payloads\ResponsePayload;
use Spatie\LaravelRay\Payloads\ViewPayload;
use Spatie\LaravelRay\Watchers\CacheWatcher;
use Spatie\LaravelRay\Watchers\ConditionalQueryWatcher;
use Spatie\LaravelRay\Watchers\DeleteQueryWatcher;
use Spatie\LaravelRay\Watchers\DuplicateQueryWatcher;
use Spatie\LaravelRay\Watchers\EventWatcher;
use Spatie\LaravelRay\Watchers\ExceptionWatcher;
use Spatie\LaravelRay\Watchers\HttpClientWatcher;
use Spatie\LaravelRay\Watchers\InsertQueryWatcher;
use Spatie\LaravelRay\Watchers\JobWatcher;
use Spatie\LaravelRay\Watchers\QueryWatcher;
use Spatie\LaravelRay\Watchers\RequestWatcher;
use Spatie\LaravelRay\Watchers\SelectQueryWatcher;
use Spatie\LaravelRay\Watchers\SlowQueryWatcher;
use Spatie\LaravelRay\Watchers\UpdateQueryWatcher;
use Spatie\LaravelRay\Watchers\ViewWatcher;
use Spatie\LaravelRay\Watchers\Watcher;
use Spatie\Ray\Client;
Expand Down Expand Up @@ -433,6 +438,76 @@ public function stopShowingDuplicateQueries(): self
return $this;
}

public function showConditionalQueries(Closure $condition, $callable = null, $name = 'default')
{
$watcher = ConditionalQueryWatcher::buildWatcherForName($condition, $name);

return $this->handleWatcherCallable($watcher, $callable);
}

public function stopShowingConditionalQueries($name = 'default'): self
{
app(ConditionalQueryWatcher::abstractName($name))->disable();

return $this;
}

public function showUpdateQueries($callable = null)
{
$watcher = app(UpdateQueryWatcher::class);

return $this->handleWatcherCallable($watcher, $callable);
}

public function stopShowingUpdateQueries(): self
{
app(UpdateQueryWatcher::class)->disable();

return $this;
}

public function showDeleteQueries($callable = null)
{
$watcher = app(DeleteQueryWatcher::class);

return $this->handleWatcherCallable($watcher, $callable);
}

public function stopShowingDeleteQueries(): self
{
app(DeleteQueryWatcher::class)->disable();

return $this;
}

public function showInsertQueries($callable = null)
{
$watcher = app(InsertQueryWatcher::class);

return $this->handleWatcherCallable($watcher, $callable);
}

public function stopShowingInsertQueries(): self
{
app(InsertQueryWatcher::class)->disable();

return $this;
}

public function showSelectQueries($callable = null)
{
$watcher = app(SelectQueryWatcher::class);

return $this->handleWatcherCallable($watcher, $callable);
}

public function stopShowingSelectQueries(): self
{
app(SelectQueryWatcher::class)->disable();

return $this;
}

/**
* @param null $callable
*
Expand Down
16 changes: 16 additions & 0 deletions src/RayServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,21 @@
use Spatie\LaravelRay\Payloads\QueryPayload;
use Spatie\LaravelRay\Watchers\ApplicationLogWatcher;
use Spatie\LaravelRay\Watchers\CacheWatcher;
use Spatie\LaravelRay\Watchers\DeleteQueryWatcher;
use Spatie\LaravelRay\Watchers\DeprecatedNoticeWatcher;
use Spatie\LaravelRay\Watchers\DumpWatcher;
use Spatie\LaravelRay\Watchers\DuplicateQueryWatcher;
use Spatie\LaravelRay\Watchers\EventWatcher;
use Spatie\LaravelRay\Watchers\ExceptionWatcher;
use Spatie\LaravelRay\Watchers\HttpClientWatcher;
use Spatie\LaravelRay\Watchers\InsertQueryWatcher;
use Spatie\LaravelRay\Watchers\JobWatcher;
use Spatie\LaravelRay\Watchers\LoggedMailWatcher;
use Spatie\LaravelRay\Watchers\QueryWatcher;
use Spatie\LaravelRay\Watchers\RequestWatcher;
use Spatie\LaravelRay\Watchers\SelectQueryWatcher;
use Spatie\LaravelRay\Watchers\SlowQueryWatcher;
use Spatie\LaravelRay\Watchers\UpdateQueryWatcher;
use Spatie\LaravelRay\Watchers\ViewWatcher;
use Spatie\Ray\Client;
use Spatie\Ray\PayloadFactory;
Expand Down Expand Up @@ -79,6 +83,10 @@ protected function registerSettings(): self
'send_queries_to_ray' => env('SEND_QUERIES_TO_RAY', false),
'send_duplicate_queries_to_ray' => env('SEND_DUPLICATE_QUERIES_TO_RAY', false),
'send_slow_queries_to_ray' => env('SEND_SLOW_QUERIES_TO_RAY', false),
'send_update_queries_to_ray' => env('SEND_UPDATE_QUERIES_TO_RAY', false),
'send_insert_queries_to_ray' => env('SEND_INSERT_QUERIES_TO_RAY', false),
'send_delete_queries_to_ray' => env('SEND_DELETE_QUERIES_TO_RAY', false),
'send_select_queries_to_ray' => env('SEND_SELECT_QUERIES_TO_RAY', false),
'send_requests_to_ray' => env('SEND_REQUESTS_TO_RAY', false),
'send_http_client_requests_to_ray' => env('SEND_HTTP_CLIENT_REQUESTS_TO_RAY', false),
'send_views_to_ray' => env('SEND_VIEWS_TO_RAY', false),
Expand Down Expand Up @@ -142,6 +150,10 @@ protected function registerWatchers(): self
QueryWatcher::class,
DuplicateQueryWatcher::class,
SlowQueryWatcher::class,
InsertQueryWatcher::class,
SelectQueryWatcher::class,
UpdateQueryWatcher::class,
DeleteQueryWatcher::class,
ViewWatcher::class,
CacheWatcher::class,
RequestWatcher::class,
Expand Down Expand Up @@ -169,6 +181,10 @@ protected function bootWatchers(): self
QueryWatcher::class,
DuplicateQueryWatcher::class,
SlowQueryWatcher::class,
InsertQueryWatcher::class,
SelectQueryWatcher::class,
UpdateQueryWatcher::class,
DeleteQueryWatcher::class,
ViewWatcher::class,
CacheWatcher::class,
RequestWatcher::class,
Expand Down
63 changes: 63 additions & 0 deletions src/Watchers/ConditionalQueryWatcher.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<?php

namespace Spatie\LaravelRay\Watchers;

use BadMethodCallException;
use Closure;
use Illuminate\Database\Events\QueryExecuted;
use Illuminate\Support\Facades\Event;
use Spatie\LaravelRay\Payloads\ExecutedQueryPayload;
use Spatie\LaravelRay\Ray;

class ConditionalQueryWatcher extends QueryWatcher
{
protected $conditionalCallback;

public static function buildWatcherForName(Closure $condition, $name)
{
$watcher = new static();

Check failure on line 18 in src/Watchers/ConditionalQueryWatcher.php

View workflow job for this annotation

GitHub Actions / phpstan

Unsafe usage of new static().
$watcher->setConditionalCallback($condition);

return app()->instance(static::abstractName($name), $watcher);
}

public static function abstractName(string $name)
{
return static::class.':'.$name;
}

public function setConditionalCallback($conditionalCallback)
{
$this->conditionalCallback = $conditionalCallback;

$this->listen();
}

public function register(): void
{
throw new BadMethodCallException('ConditionalQueryWatcher cannot be registered. Only its child classes.');
}

public function listen(): void
{
Event::listen(QueryExecuted::class, function (QueryExecuted $query) {
if (! $this->enabled()) {
return;
}

if (! $this->conditionalCallback) {
return;
}

$ray = app(Ray::class);

if (($this->conditionalCallback)($query)) {
$payload = new ExecutedQueryPayload($query);

$ray->sendRequest($payload);
}

optional($this->rayProxy)->applyCalledMethods($ray);
});
}
}
21 changes: 21 additions & 0 deletions src/Watchers/DeleteQueryWatcher.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

namespace Spatie\LaravelRay\Watchers;

use Illuminate\Database\Events\QueryExecuted;
use Illuminate\Support\Str;
use Spatie\Ray\Settings\Settings;

class DeleteQueryWatcher extends ConditionalQueryWatcher
{
public function register(): void
{
$settings = app(Settings::class);

$this->enabled = $settings->send_delete_queries_to_ray ?? false;

$this->setConditionalCallback(function (QueryExecuted $query) {
return Str::startsWith(strtolower($query->sql), 'delete');
});
}
}
21 changes: 21 additions & 0 deletions src/Watchers/InsertQueryWatcher.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

namespace Spatie\LaravelRay\Watchers;

use Illuminate\Database\Events\QueryExecuted;
use Illuminate\Support\Str;
use Spatie\Ray\Settings\Settings;

class InsertQueryWatcher extends ConditionalQueryWatcher
{
public function register(): void
{
$settings = app(Settings::class);

$this->enabled = $settings->send_insert_queries_to_ray ?? false;

$this->setConditionalCallback(function (QueryExecuted $query) {
return Str::startsWith(strtolower($query->sql), 'insert');
});
}
}
21 changes: 21 additions & 0 deletions src/Watchers/SelectQueryWatcher.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

namespace Spatie\LaravelRay\Watchers;

use Illuminate\Database\Events\QueryExecuted;
use Illuminate\Support\Str;
use Spatie\Ray\Settings\Settings;

class SelectQueryWatcher extends ConditionalQueryWatcher
{
public function register(): void
{
$settings = app(Settings::class);

$this->enabled = $settings->send_select_queries_to_ray ?? false;

$this->setConditionalCallback(function (QueryExecuted $query) {
return Str::startsWith(strtolower($query->sql), 'select');
});
}
}
21 changes: 3 additions & 18 deletions src/Watchers/SlowQueryWatcher.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,9 @@
namespace Spatie\LaravelRay\Watchers;

use Illuminate\Database\Events\QueryExecuted;
use Illuminate\Support\Facades\Event;
use Spatie\LaravelRay\Payloads\ExecutedQueryPayload;
use Spatie\LaravelRay\Ray;
use Spatie\Ray\Settings\Settings;

class SlowQueryWatcher extends QueryWatcher
class SlowQueryWatcher extends ConditionalQueryWatcher
{
protected $minimumTimeInMs = 500;

Expand All @@ -19,20 +16,8 @@ public function register(): void
$this->enabled = $settings->send_slow_queries_to_ray ?? false;
$this->minimumTimeInMs = $settings->slow_query_threshold_in_ms ?? $this->minimumTimeInMs;

Event::listen(QueryExecuted::class, function (QueryExecuted $query) {
if (! $this->enabled()) {
return;
}

$ray = app(Ray::class);

if ($query->time >= $this->minimumTimeInMs) {
$payload = new ExecutedQueryPayload($query);

$ray->sendRequest($payload);
}

optional($this->rayProxy)->applyCalledMethods($ray);
$this->setConditionalCallback(function (QueryExecuted $query) {
return $query->time >= $this->minimumTimeInMs;
});
}

Expand Down
21 changes: 21 additions & 0 deletions src/Watchers/UpdateQueryWatcher.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

namespace Spatie\LaravelRay\Watchers;

use Illuminate\Database\Events\QueryExecuted;
use Illuminate\Support\Str;
use Spatie\Ray\Settings\Settings;

class UpdateQueryWatcher extends ConditionalQueryWatcher
{
public function register(): void
{
$settings = app(Settings::class);

$this->enabled = $settings->send_update_queries_to_ray ?? false;

$this->setConditionalCallback(function (QueryExecuted $query) {
return Str::startsWith(strtolower($query->sql), 'update');
});
}
}
20 changes: 20 additions & 0 deletions stub/ray.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,26 @@
*/
'slow_query_threshold_in_ms' => env('RAY_SLOW_QUERY_THRESHOLD_IN_MS', 500),

/*
* When enabled, all update queries will automatically be sent to Ray.
*/
'send_update_queries_to_ray' => env('SEND_UPDATE_QUERIES_TO_RAY', false),

/*
* When enabled, all insert queries will automatically be sent to Ray.
*/
'send_insert_queries_to_ray' => env('SEND_INSERT_QUERIES_TO_RAY', false),

/*
* When enabled, all delete queries will automatically be sent to Ray.
*/
'send_delete_queries_to_ray' => env('SEND_DELETE_QUERIES_TO_RAY', false),

/*
* When enabled, all select queries will automatically be sent to Ray.
*/
'send_select_queries_to_ray' => env('SEND_SELECT_QUERIES_TO_RAY', false),

/*
* When enabled, all requests made to this app will automatically be sent to Ray.
*/
Expand Down
13 changes: 13 additions & 0 deletions tests/TestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@

namespace Spatie\LaravelRay\Tests;

use Illuminate\Database\Events\QueryExecuted;
use Illuminate\Database\Query\Builder;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\View;
use Illuminate\Support\Str;
use Orchestra\Testbench\TestCase as Orchestra;
use Spatie\LaravelRay\Ray;
use Spatie\LaravelRay\RayServiceProvider;
Expand Down Expand Up @@ -70,4 +74,13 @@ protected function useRealUuid()
return Ray::create($this->client);
});
}

protected function assertSqlContains($queryContent, $needle): void
{
$sql = method_exists(Builder::class, 'toRawSql')
? $queryContent['sql']
: Str::replaceArray('?', $queryContent['bindings'], $queryContent['sql']);

$this->assertStringContainsString($needle, $sql);
}
}
Loading
Loading