Skip to content

Commit

Permalink
Merge pull request #359 from patrickomeara/main
Browse files Browse the repository at this point in the history
Add conditional query watcher and convenience methods for update, delete, insert and select queries
  • Loading branch information
freekmurze authored Dec 11, 2024
2 parents d9262d1 + 8570d28 commit 9d67371
Show file tree
Hide file tree
Showing 11 changed files with 412 additions and 18 deletions.
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

0 comments on commit 9d67371

Please sign in to comment.