|
6371
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Jiminny\Component\Utility\Service;
use Illuminate\Cache\RateLimiter;
use Jiminny\Contracts\Http\RateLimited;
use Jiminny\Contracts\Http\RateLimitInterface;
class ProviderRateLimiter
{
protected RateLimiter $rateLimiter;
public function __construct(RateLimiter $rateLimiter)
{
$this->rateLimiter = $rateLimiter;
}
public function canMakeRequest(RateLimited $provider): bool
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$key = $rateLimit->getKey();
if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {
return false;
}
}
return true;
}
public function requestAvailableIn(RateLimited $provider): int
{
return $provider->getRateLimits()->isNotEmpty()
? $provider->getRateLimits()
->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))
->max()
: 0
;
}
public function incrementRequestCount(RateLimited $provider): void
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());
}
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
2
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm\Delete;
use Illuminate\Events\Dispatcher;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use Jiminny\Enums\CrmObject;
use Jiminny\Events\Crm\DetachActivityObject;
use Jiminny\Models\Activity;
use Psr\Log\LoggerInterface;
use Throwable;
trait DeleteCrmEntityTrait
{
public int $tries = 3;
public function timeout(): int
{
return 300; // 5 minutes
}
public function backoff(): array
{
return [30, 90, 180]; // 30 seconds, 1.5 minutes, 3 minutes
}
protected function handleActivities(
Collection $activities,
Dispatcher $dispatcher,
LoggerInterface $logger,
bool $emitEvent = true,
): void {
if ($activities->isEmpty()) {
return;
}
$crmObject = $this->getEntityType();
$entityIdField = $crmObject->value . '_id';
$activities->each(
function (Activity $activity) use ($dispatcher, $logger, $entityIdField, $crmObject, $emitEvent): void {
$stageId = $activity->getStage()?->getId();
$logData = [
$crmObject->value => $this->id,
'activity' => $activity->getId(),
'emitEvent' => $emitEvent,
];
$updateData = [
$entityIdField => null,
];
// For leads and opportunities, also nullify the stage_id
if ($stageId && in_array($crmObject, [CrmObject::LEAD, CrmObject::OPPORTUNITY], true)) {
$updateData['stage_id'] = null;
$logData['stage_id'] = $stageId;
}
$activity->update($updateData);
if ($emitEvent) {
$dispatcher->dispatch(new DetachActivityObject($activity, $crmObject));
}
$logger->info($this->getLogPrefix() . ' Detach from activity', $logData);
// Dispatch job to verify if CRM task/event still exists
if ($activity->hasCrmProviderId()) {
VerifyActivityCrmTaskJob::dispatch($activity->getId());
}
}
);
}
public function failed(Throwable $exception): void
{
$crmObject = $this->getEntityType();
Log::critical($this->getLogPrefix() . ' Job failed permanently', [
$crmObject->value => $this->id,
'exception' => $exception->getMessage(),
]);
}
// Abstract methods that must be implemented by the using class
abstract protected function getLogPrefix(): string;
abstract protected function getEntityType(): CrmObject;
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – DeleteCrmEntityTrait.php
|
NULL
|
6371
|
|
6370
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Jiminny\Component\Utility\Service;
use Illuminate\Cache\RateLimiter;
use Jiminny\Contracts\Http\RateLimited;
use Jiminny\Contracts\Http\RateLimitInterface;
class ProviderRateLimiter
{
protected RateLimiter $rateLimiter;
public function __construct(RateLimiter $rateLimiter)
{
$this->rateLimiter = $rateLimiter;
}
public function canMakeRequest(RateLimited $provider): bool
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$key = $rateLimit->getKey();
if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {
return false;
}
}
return true;
}
public function requestAvailableIn(RateLimited $provider): int
{
return $provider->getRateLimits()->isNotEmpty()
? $provider->getRateLimits()
->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))
->max()
: 0
;
}
public function incrementRequestCount(RateLimited $provider): void
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());
}
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
2
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm\Delete;
use Illuminate\Events\Dispatcher;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use Jiminny\Enums\CrmObject;
use Jiminny\Events\Crm\DetachActivityObject;
use Jiminny\Models\Activity;
use Psr\Log\LoggerInterface;
use Throwable;
trait DeleteCrmEntityTrait
{
public int $tries = 3;
public function timeout(): int
{
return 300; // 5 minutes
}
public function backoff(): array
{
return [30, 90, 180]; // 30 seconds, 1.5 minutes, 3 minutes
}
protected function handleActivities(
Collection $activities,
Dispatcher $dispatcher,
LoggerInterface $logger,
bool $emitEvent = true,
): void {
if ($activities->isEmpty()) {
return;
}
$crmObject = $this->getEntityType();
$entityIdField = $crmObject->value . '_id';
$activities->each(
function (Activity $activity) use ($dispatcher, $logger, $entityIdField, $crmObject, $emitEvent): void {
$stageId = $activity->getStage()?->getId();
$logData = [
$crmObject->value => $this->id,
'activity' => $activity->getId(),
'emitEvent' => $emitEvent,
];
$updateData = [
$entityIdField => null,
];
// For leads and opportunities, also nullify the stage_id
if ($stageId && in_array($crmObject, [CrmObject::LEAD, CrmObject::OPPORTUNITY], true)) {
$updateData['stage_id'] = null;
$logData['stage_id'] = $stageId;
}
$activity->update($updateData);
if ($emitEvent) {
$dispatcher->dispatch(new DetachActivityObject($activity, $crmObject));
}
$logger->info($this->getLogPrefix() . ' Detach from activity', $logData);
// Dispatch job to verify if CRM task/event still exists
if ($activity->hasCrmProviderId()) {
VerifyActivityCrmTaskJob::dispatch($activity->getId());
}
}
);
}
public function failed(Throwable $exception): void
{
$crmObject = $this->getEntityType();
Log::critical($this->getLogPrefix() . ' Job failed permanently', [
$crmObject->value => $this->id,
'exception' => $exception->getMessage(),
]);
}
// Abstract methods that must be implemented by the using class
abstract protected function getLogPrefix(): string;
abstract protected function getEntityType(): CrmObject;
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – DeleteCrmEntityTrait.php
|
NULL
|
6370
|
|
6369
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Jiminny\Component\Utility\Service;
use Illuminate\Cache\RateLimiter;
use Jiminny\Contracts\Http\RateLimited;
use Jiminny\Contracts\Http\RateLimitInterface;
class ProviderRateLimiter
{
protected RateLimiter $rateLimiter;
public function __construct(RateLimiter $rateLimiter)
{
$this->rateLimiter = $rateLimiter;
}
public function canMakeRequest(RateLimited $provider): bool
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$key = $rateLimit->getKey();
if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {
return false;
}
}
return true;
}
public function requestAvailableIn(RateLimited $provider): int
{
return $provider->getRateLimits()->isNotEmpty()
? $provider->getRateLimits()
->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))
->max()
: 0
;
}
public function incrementRequestCount(RateLimited $provider): void
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());
}
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
2
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm\Delete;
use Illuminate\Events\Dispatcher;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use Jiminny\Enums\CrmObject;
use Jiminny\Events\Crm\DetachActivityObject;
use Jiminny\Models\Activity;
use Psr\Log\LoggerInterface;
use Throwable;
trait DeleteCrmEntityTrait
{
public int $tries = 3;
public function timeout(): int
{
return 300; // 5 minutes
}
public function backoff(): array
{
return [30, 90, 180]; // 30 seconds, 1.5 minutes, 3 minutes
}
protected function handleActivities(
Collection $activities,
Dispatcher $dispatcher,
LoggerInterface $logger,
bool $emitEvent = true,
): void {
if ($activities->isEmpty()) {
return;
}
$crmObject = $this->getEntityType();
$entityIdField = $crmObject->value . '_id';
$activities->each(
function (Activity $activity) use ($dispatcher, $logger, $entityIdField, $crmObject, $emitEvent): void {
$stageId = $activity->getStage()?->getId();
$logData = [
$crmObject->value => $this->id,
'activity' => $activity->getId(),
'emitEvent' => $emitEvent,
];
$updateData = [
$entityIdField => null,
];
// For leads and opportunities, also nullify the stage_id
if ($stageId && in_array($crmObject, [CrmObject::LEAD, CrmObject::OPPORTUNITY], true)) {
$updateData['stage_id'] = null;
$logData['stage_id'] = $stageId;
}
$activity->update($updateData);
if ($emitEvent) {
$dispatcher->dispatch(new DetachActivityObject($activity, $crmObject));
}
$logger->info($this->getLogPrefix() . ' Detach from activity', $logData);
// Dispatch job to verify if CRM task/event still exists
if ($activity->hasCrmProviderId()) {
VerifyActivityCrmTaskJob::dispatch($activity->getId());
}
}
);
}
public function failed(Throwable $exception): void
{
$crmObject = $this->getEntityType();
Log::critical($this->getLogPrefix() . ' Job failed permanently', [
$crmObject->value => $this->id,
'exception' => $exception->getMessage(),
]);
}
// Abstract methods that must be implemented by the using class
abstract protected function getLogPrefix(): string;
abstract protected function getEntityType(): CrmObject;
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – DeleteCrmEntityTrait.php
|
NULL
|
6369
|
|
6368
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Jiminny\Component\Utility\Service;
use Illuminate\Cache\RateLimiter;
use Jiminny\Contracts\Http\RateLimited;
use Jiminny\Contracts\Http\RateLimitInterface;
class ProviderRateLimiter
{
protected RateLimiter $rateLimiter;
public function __construct(RateLimiter $rateLimiter)
{
$this->rateLimiter = $rateLimiter;
}
public function canMakeRequest(RateLimited $provider): bool
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$key = $rateLimit->getKey();
if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {
return false;
}
}
return true;
}
public function requestAvailableIn(RateLimited $provider): int
{
return $provider->getRateLimits()->isNotEmpty()
? $provider->getRateLimits()
->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))
->max()
: 0
;
}
public function incrementRequestCount(RateLimited $provider): void
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());
}
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
2
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm\Delete;
use Illuminate\Events\Dispatcher;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use Jiminny\Enums\CrmObject;
use Jiminny\Events\Crm\DetachActivityObject;
use Jiminny\Models\Activity;
use Psr\Log\LoggerInterface;
use Throwable;
trait DeleteCrmEntityTrait
{
public int $tries = 3;
public function timeout(): int
{
return 300; // 5 minutes
}
public function backoff(): array
{
return [30, 90, 180]; // 30 seconds, 1.5 minutes, 3 minutes
}
protected function handleActivities(
Collection $activities,
Dispatcher $dispatcher,
LoggerInterface $logger,
bool $emitEvent = true,
): void {
if ($activities->isEmpty()) {
return;
}
$crmObject = $this->getEntityType();
$entityIdField = $crmObject->value . '_id';
$activities->each(
function (Activity $activity) use ($dispatcher, $logger, $entityIdField, $crmObject, $emitEvent): void {
$stageId = $activity->getStage()?->getId();
$logData = [
$crmObject->value => $this->id,
'activity' => $activity->getId(),
'emitEvent' => $emitEvent,
];
$updateData = [
$entityIdField => null,
];
// For leads and opportunities, also nullify the stage_id
if ($stageId && in_array($crmObject, [CrmObject::LEAD, CrmObject::OPPORTUNITY], true)) {
$updateData['stage_id'] = null;
$logData['stage_id'] = $stageId;
}
$activity->update($updateData);
if ($emitEvent) {
$dispatcher->dispatch(new DetachActivityObject($activity, $crmObject));
}
$logger->info($this->getLogPrefix() . ' Detach from activity', $logData);
// Dispatch job to verify if CRM task/event still exists
if ($activity->hasCrmProviderId()) {
VerifyActivityCrmTaskJob::dispatch($activity->getId());
}
}
);
}
public function failed(Throwable $exception): void
{
$crmObject = $this->getEntityType();
Log::critical($this->getLogPrefix() . ' Job failed permanently', [
$crmObject->value => $this->id,
'exception' => $exception->getMessage(),
]);
}
// Abstract methods that must be implemented by the using class
abstract protected function getLogPrefix(): string;
abstract protected function getEntityType(): CrmObject;
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – DeleteCrmEntityTrait.php
|
NULL
|
6368
|
|
6367
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Jiminny\Component\Utility\Service;
use Illuminate\Cache\RateLimiter;
use Jiminny\Contracts\Http\RateLimited;
use Jiminny\Contracts\Http\RateLimitInterface;
class ProviderRateLimiter
{
protected RateLimiter $rateLimiter;
public function __construct(RateLimiter $rateLimiter)
{
$this->rateLimiter = $rateLimiter;
}
public function canMakeRequest(RateLimited $provider): bool
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$key = $rateLimit->getKey();
if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {
return false;
}
}
return true;
}
public function requestAvailableIn(RateLimited $provider): int
{
return $provider->getRateLimits()->isNotEmpty()
? $provider->getRateLimits()
->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))
->max()
: 0
;
}
public function incrementRequestCount(RateLimited $provider): void
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());
}
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
2
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm\Delete;
use Illuminate\Events\Dispatcher;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use Jiminny\Enums\CrmObject;
use Jiminny\Events\Crm\DetachActivityObject;
use Jiminny\Models\Activity;
use Psr\Log\LoggerInterface;
use Throwable;
trait DeleteCrmEntityTrait
{
public int $tries = 3;
public function timeout(): int
{
return 300; // 5 minutes
}
public function backoff(): array
{
return [30, 90, 180]; // 30 seconds, 1.5 minutes, 3 minutes
}
protected function handleActivities(
Collection $activities,
Dispatcher $dispatcher,
LoggerInterface $logger,
bool $emitEvent = true,
): void {
if ($activities->isEmpty()) {
return;
}
$crmObject = $this->getEntityType();
$entityIdField = $crmObject->value . '_id';
$activities->each(
function (Activity $activity) use ($dispatcher, $logger, $entityIdField, $crmObject, $emitEvent): void {
$stageId = $activity->getStage()?->getId();
$logData = [
$crmObject->value => $this->id,
'activity' => $activity->getId(),
'emitEvent' => $emitEvent,
];
$updateData = [
$entityIdField => null,
];
// For leads and opportunities, also nullify the stage_id
if ($stageId && in_array($crmObject, [CrmObject::LEAD, CrmObject::OPPORTUNITY], true)) {
$updateData['stage_id'] = null;
$logData['stage_id'] = $stageId;
}
$activity->update($updateData);
if ($emitEvent) {
$dispatcher->dispatch(new DetachActivityObject($activity, $crmObject));
}
$logger->info($this->getLogPrefix() . ' Detach from activity', $logData);
// Dispatch job to verify if CRM task/event still exists
if ($activity->hasCrmProviderId()) {
VerifyActivityCrmTaskJob::dispatch($activity->getId());
}
}
);
}
public function failed(Throwable $exception): void
{
$crmObject = $this->getEntityType();
Log::critical($this->getLogPrefix() . ' Job failed permanently', [
$crmObject->value => $this->id,
'exception' => $exception->getMessage(),
]);
}
// Abstract methods that must be implemented by the using class
abstract protected function getLogPrefix(): string;
abstract protected function getEntityType(): CrmObject;
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – DeleteCrmEntityTrait.php
|
NULL
|
6367
|
|
6366
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Jiminny\Component\Utility\Service;
use Illuminate\Cache\RateLimiter;
use Jiminny\Contracts\Http\RateLimited;
use Jiminny\Contracts\Http\RateLimitInterface;
class ProviderRateLimiter
{
protected RateLimiter $rateLimiter;
public function __construct(RateLimiter $rateLimiter)
{
$this->rateLimiter = $rateLimiter;
}
public function canMakeRequest(RateLimited $provider): bool
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$key = $rateLimit->getKey();
if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {
return false;
}
}
return true;
}
public function requestAvailableIn(RateLimited $provider): int
{
return $provider->getRateLimits()->isNotEmpty()
? $provider->getRateLimits()
->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))
->max()
: 0
;
}
public function incrementRequestCount(RateLimited $provider): void
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());
}
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
2
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm\Delete;
use Illuminate\Events\Dispatcher;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use Jiminny\Enums\CrmObject;
use Jiminny\Events\Crm\DetachActivityObject;
use Jiminny\Models\Activity;
use Psr\Log\LoggerInterface;
use Throwable;
trait DeleteCrmEntityTrait
{
public int $tries = 3;
public function timeout(): int
{
return 300; // 5 minutes
}
public function backoff(): array
{
return [30, 90, 180]; // 30 seconds, 1.5 minutes, 3 minutes
}
protected function handleActivities(
Collection $activities,
Dispatcher $dispatcher,
LoggerInterface $logger,
bool $emitEvent = true,
): void {
if ($activities->isEmpty()) {
return;
}
$crmObject = $this->getEntityType();
$entityIdField = $crmObject->value . '_id';
$activities->each(
function (Activity $activity) use ($dispatcher, $logger, $entityIdField, $crmObject, $emitEvent): void {
$stageId = $activity->getStage()?->getId();
$logData = [
$crmObject->value => $this->id,
'activity' => $activity->getId(),
'emitEvent' => $emitEvent,
];
$updateData = [
$entityIdField => null,
];
// For leads and opportunities, also nullify the stage_id
if ($stageId && in_array($crmObject, [CrmObject::LEAD, CrmObject::OPPORTUNITY], true)) {
$updateData['stage_id'] = null;
$logData['stage_id'] = $stageId;
}
$activity->update($updateData);
if ($emitEvent) {
$dispatcher->dispatch(new DetachActivityObject($activity, $crmObject));
}
$logger->info($this->getLogPrefix() . ' Detach from activity', $logData);
// Dispatch job to verify if CRM task/event still exists
if ($activity->hasCrmProviderId()) {
VerifyActivityCrmTaskJob::dispatch($activity->getId());
}
}
);
}
public function failed(Throwable $exception): void
{
$crmObject = $this->getEntityType();
Log::critical($this->getLogPrefix() . ' Job failed permanently', [
$crmObject->value => $this->id,
'exception' => $exception->getMessage(),
]);
}
// Abstract methods that must be implemented by the using class
abstract protected function getLogPrefix(): string;
abstract protected function getEntityType(): CrmObject;
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – DeleteCrmEntityTrait.php
|
NULL
|
6366
|
|
6365
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Jiminny\Component\Utility\Service;
use Illuminate\Cache\RateLimiter;
use Jiminny\Contracts\Http\RateLimited;
use Jiminny\Contracts\Http\RateLimitInterface;
class ProviderRateLimiter
{
protected RateLimiter $rateLimiter;
public function __construct(RateLimiter $rateLimiter)
{
$this->rateLimiter = $rateLimiter;
}
public function canMakeRequest(RateLimited $provider): bool
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$key = $rateLimit->getKey();
if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {
return false;
}
}
return true;
}
public function requestAvailableIn(RateLimited $provider): int
{
return $provider->getRateLimits()->isNotEmpty()
? $provider->getRateLimits()
->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))
->max()
: 0
;
}
public function incrementRequestCount(RateLimited $provider): void
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());
}
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
2
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm\Delete;
use Illuminate\Events\Dispatcher;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use Jiminny\Enums\CrmObject;
use Jiminny\Events\Crm\DetachActivityObject;
use Jiminny\Models\Activity;
use Psr\Log\LoggerInterface;
use Throwable;
trait DeleteCrmEntityTrait
{
public int $tries = 3;
public function timeout(): int
{
return 300; // 5 minutes
}
public function backoff(): array
{
return [30, 90, 180]; // 30 seconds, 1.5 minutes, 3 minutes
}
protected function handleActivities(
Collection $activities,
Dispatcher $dispatcher,
LoggerInterface $logger,
bool $emitEvent = true,
): void {
if ($activities->isEmpty()) {
return;
}
$crmObject = $this->getEntityType();
$entityIdField = $crmObject->value . '_id';
$activities->each(
function (Activity $activity) use ($dispatcher, $logger, $entityIdField, $crmObject, $emitEvent): void {
$stageId = $activity->getStage()?->getId();
$logData = [
$crmObject->value => $this->id,
'activity' => $activity->getId(),
'emitEvent' => $emitEvent,
];
$updateData = [
$entityIdField => null,
];
// For leads and opportunities, also nullify the stage_id
if ($stageId && in_array($crmObject, [CrmObject::LEAD, CrmObject::OPPORTUNITY], true)) {
$updateData['stage_id'] = null;
$logData['stage_id'] = $stageId;
}
$activity->update($updateData);
if ($emitEvent) {
$dispatcher->dispatch(new DetachActivityObject($activity, $crmObject));
}
$logger->info($this->getLogPrefix() . ' Detach from activity', $logData);
// Dispatch job to verify if CRM task/event still exists
if ($activity->hasCrmProviderId()) {
VerifyActivityCrmTaskJob::dispatch($activity->getId());
}
}
);
}
public function failed(Throwable $exception): void
{
$crmObject = $this->getEntityType();
Log::critical($this->getLogPrefix() . ' Job failed permanently', [
$crmObject->value => $this->id,
'exception' => $exception->getMessage(),
]);
}
// Abstract methods that must be implemented by the using class
abstract protected function getLogPrefix(): string;
abstract protected function getEntityType(): CrmObject;
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – DeleteCrmEntityTrait.php
|
NULL
|
6365
|
|
6364
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Jiminny\Component\Utility\Service;
use Illuminate\Cache\RateLimiter;
use Jiminny\Contracts\Http\RateLimited;
use Jiminny\Contracts\Http\RateLimitInterface;
class ProviderRateLimiter
{
protected RateLimiter $rateLimiter;
public function __construct(RateLimiter $rateLimiter)
{
$this->rateLimiter = $rateLimiter;
}
public function canMakeRequest(RateLimited $provider): bool
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$key = $rateLimit->getKey();
if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {
return false;
}
}
return true;
}
public function requestAvailableIn(RateLimited $provider): int
{
return $provider->getRateLimits()->isNotEmpty()
? $provider->getRateLimits()
->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))
->max()
: 0
;
}
public function incrementRequestCount(RateLimited $provider): void
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());
}
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
2
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm\Delete;
use Illuminate\Events\Dispatcher;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use Jiminny\Enums\CrmObject;
use Jiminny\Events\Crm\DetachActivityObject;
use Jiminny\Models\Activity;
use Psr\Log\LoggerInterface;
use Throwable;
trait DeleteCrmEntityTrait
{
public int $tries = 3;
public function timeout(): int
{
return 300; // 5 minutes
}
public function backoff(): array
{
return [30, 90, 180]; // 30 seconds, 1.5 minutes, 3 minutes
}
protected function handleActivities(
Collection $activities,
Dispatcher $dispatcher,
LoggerInterface $logger,
bool $emitEvent = true,
): void {
if ($activities->isEmpty()) {
return;
}
$crmObject = $this->getEntityType();
$entityIdField = $crmObject->value . '_id';
$activities->each(
function (Activity $activity) use ($dispatcher, $logger, $entityIdField, $crmObject, $emitEvent): void {
$stageId = $activity->getStage()?->getId();
$logData = [
$crmObject->value => $this->id,
'activity' => $activity->getId(),
'emitEvent' => $emitEvent,
];
$updateData = [
$entityIdField => null,
];
// For leads and opportunities, also nullify the stage_id
if ($stageId && in_array($crmObject, [CrmObject::LEAD, CrmObject::OPPORTUNITY], true)) {
$updateData['stage_id'] = null;
$logData['stage_id'] = $stageId;
}
$activity->update($updateData);
if ($emitEvent) {
$dispatcher->dispatch(new DetachActivityObject($activity, $crmObject));
}
$logger->info($this->getLogPrefix() . ' Detach from activity', $logData);
// Dispatch job to verify if CRM task/event still exists
if ($activity->hasCrmProviderId()) {
VerifyActivityCrmTaskJob::dispatch($activity->getId());
}
}
);
}
public function failed(Throwable $exception): void
{
$crmObject = $this->getEntityType();
Log::critical($this->getLogPrefix() . ' Job failed permanently', [
$crmObject->value => $this->id,
'exception' => $exception->getMessage(),
]);
}
// Abstract methods that must be implemented by the using class
abstract protected function getLogPrefix(): string;
abstract protected function getEntityType(): CrmObject;
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – DeleteCrmEntityTrait.php
|
NULL
|
6364
|
|
6363
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Jiminny\Component\Utility\Service;
use Illuminate\Cache\RateLimiter;
use Jiminny\Contracts\Http\RateLimited;
use Jiminny\Contracts\Http\RateLimitInterface;
class ProviderRateLimiter
{
protected RateLimiter $rateLimiter;
public function __construct(RateLimiter $rateLimiter)
{
$this->rateLimiter = $rateLimiter;
}
public function canMakeRequest(RateLimited $provider): bool
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$key = $rateLimit->getKey();
if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {
return false;
}
}
return true;
}
public function requestAvailableIn(RateLimited $provider): int
{
return $provider->getRateLimits()->isNotEmpty()
? $provider->getRateLimits()
->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))
->max()
: 0
;
}
public function incrementRequestCount(RateLimited $provider): void
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());
}
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
2
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm\Delete;
use Illuminate\Events\Dispatcher;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use Jiminny\Enums\CrmObject;
use Jiminny\Events\Crm\DetachActivityObject;
use Jiminny\Models\Activity;
use Psr\Log\LoggerInterface;
use Throwable;
trait DeleteCrmEntityTrait
{
public int $tries = 3;
public function timeout(): int
{
return 300; // 5 minutes
}
public function backoff(): array
{
return [30, 90, 180]; // 30 seconds, 1.5 minutes, 3 minutes
}
protected function handleActivities(
Collection $activities,
Dispatcher $dispatcher,
LoggerInterface $logger,
bool $emitEvent = true,
): void {
if ($activities->isEmpty()) {
return;
}
$crmObject = $this->getEntityType();
$entityIdField = $crmObject->value . '_id';
$activities->each(
function (Activity $activity) use ($dispatcher, $logger, $entityIdField, $crmObject, $emitEvent): void {
$stageId = $activity->getStage()?->getId();
$logData = [
$crmObject->value => $this->id,
'activity' => $activity->getId(),
'emitEvent' => $emitEvent,
];
$updateData = [
$entityIdField => null,
];
// For leads and opportunities, also nullify the stage_id
if ($stageId && in_array($crmObject, [CrmObject::LEAD, CrmObject::OPPORTUNITY], true)) {
$updateData['stage_id'] = null;
$logData['stage_id'] = $stageId;
}
$activity->update($updateData);
if ($emitEvent) {
$dispatcher->dispatch(new DetachActivityObject($activity, $crmObject));
}
$logger->info($this->getLogPrefix() . ' Detach from activity', $logData);
// Dispatch job to verify if CRM task/event still exists
if ($activity->hasCrmProviderId()) {
VerifyActivityCrmTaskJob::dispatch($activity->getId());
}
}
);
}
public function failed(Throwable $exception): void
{
$crmObject = $this->getEntityType();
Log::critical($this->getLogPrefix() . ' Job failed permanently', [
$crmObject->value => $this->id,
'exception' => $exception->getMessage(),
]);
}
// Abstract methods that must be implemented by the using class
abstract protected function getLogPrefix(): string;
abstract protected function getEntityType(): CrmObject;
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – DeleteCrmEntityTrait.php
|
NULL
|
6363
|
|
6362
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Jiminny\Component\Utility\Service;
use Illuminate\Cache\RateLimiter;
use Jiminny\Contracts\Http\RateLimited;
use Jiminny\Contracts\Http\RateLimitInterface;
class ProviderRateLimiter
{
protected RateLimiter $rateLimiter;
public function __construct(RateLimiter $rateLimiter)
{
$this->rateLimiter = $rateLimiter;
}
public function canMakeRequest(RateLimited $provider): bool
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$key = $rateLimit->getKey();
if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {
return false;
}
}
return true;
}
public function requestAvailableIn(RateLimited $provider): int
{
return $provider->getRateLimits()->isNotEmpty()
? $provider->getRateLimits()
->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))
->max()
: 0
;
}
public function incrementRequestCount(RateLimited $provider): void
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());
}
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
2
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm\Delete;
use Illuminate\Events\Dispatcher;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use Jiminny\Enums\CrmObject;
use Jiminny\Events\Crm\DetachActivityObject;
use Jiminny\Models\Activity;
use Psr\Log\LoggerInterface;
use Throwable;
trait DeleteCrmEntityTrait
{
public int $tries = 3;
public function timeout(): int
{
return 300; // 5 minutes
}
public function backoff(): array
{
return [30, 90, 180]; // 30 seconds, 1.5 minutes, 3 minutes
}
protected function handleActivities(
Collection $activities,
Dispatcher $dispatcher,
LoggerInterface $logger,
bool $emitEvent = true,
): void {
if ($activities->isEmpty()) {
return;
}
$crmObject = $this->getEntityType();
$entityIdField = $crmObject->value . '_id';
$activities->each(
function (Activity $activity) use ($dispatcher, $logger, $entityIdField, $crmObject, $emitEvent): void {
$stageId = $activity->getStage()?->getId();
$logData = [
$crmObject->value => $this->id,
'activity' => $activity->getId(),
'emitEvent' => $emitEvent,
];
$updateData = [
$entityIdField => null,
];
// For leads and opportunities, also nullify the stage_id
if ($stageId && in_array($crmObject, [CrmObject::LEAD, CrmObject::OPPORTUNITY], true)) {
$updateData['stage_id'] = null;
$logData['stage_id'] = $stageId;
}
$activity->update($updateData);
if ($emitEvent) {
$dispatcher->dispatch(new DetachActivityObject($activity, $crmObject));
}
$logger->info($this->getLogPrefix() . ' Detach from activity', $logData);
// Dispatch job to verify if CRM task/event still exists
if ($activity->hasCrmProviderId()) {
VerifyActivityCrmTaskJob::dispatch($activity->getId());
}
}
);
}
public function failed(Throwable $exception): void
{
$crmObject = $this->getEntityType();
Log::critical($this->getLogPrefix() . ' Job failed permanently', [
$crmObject->value => $this->id,
'exception' => $exception->getMessage(),
]);
}
// Abstract methods that must be implemented by the using class
abstract protected function getLogPrefix(): string;
abstract protected function getEntityType(): CrmObject;
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – DeleteCrmEntityTrait.php
|
NULL
|
6362
|
|
6361
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Jiminny\Component\Utility\Service;
use Illuminate\Cache\RateLimiter;
use Jiminny\Contracts\Http\RateLimited;
use Jiminny\Contracts\Http\RateLimitInterface;
class ProviderRateLimiter
{
protected RateLimiter $rateLimiter;
public function __construct(RateLimiter $rateLimiter)
{
$this->rateLimiter = $rateLimiter;
}
public function canMakeRequest(RateLimited $provider): bool
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$key = $rateLimit->getKey();
if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {
return false;
}
}
return true;
}
public function requestAvailableIn(RateLimited $provider): int
{
return $provider->getRateLimits()->isNotEmpty()
? $provider->getRateLimits()
->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))
->max()
: 0
;
}
public function incrementRequestCount(RateLimited $provider): void
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());
}
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
2
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm\Delete;
use Illuminate\Events\Dispatcher;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use Jiminny\Enums\CrmObject;
use Jiminny\Events\Crm\DetachActivityObject;
use Jiminny\Models\Activity;
use Psr\Log\LoggerInterface;
use Throwable;
trait DeleteCrmEntityTrait
{
public int $tries = 3;
public function timeout(): int
{
return 300; // 5 minutes
}
public function backoff(): array
{
return [30, 90, 180]; // 30 seconds, 1.5 minutes, 3 minutes
}
protected function handleActivities(
Collection $activities,
Dispatcher $dispatcher,
LoggerInterface $logger,
bool $emitEvent = true,
): void {
if ($activities->isEmpty()) {
return;
}
$crmObject = $this->getEntityType();
$entityIdField = $crmObject->value . '_id';
$activities->each(
function (Activity $activity) use ($dispatcher, $logger, $entityIdField, $crmObject, $emitEvent): void {
$stageId = $activity->getStage()?->getId();
$logData = [
$crmObject->value => $this->id,
'activity' => $activity->getId(),
'emitEvent' => $emitEvent,
];
$updateData = [
$entityIdField => null,
];
// For leads and opportunities, also nullify the stage_id
if ($stageId && in_array($crmObject, [CrmObject::LEAD, CrmObject::OPPORTUNITY], true)) {
$updateData['stage_id'] = null;
$logData['stage_id'] = $stageId;
}
$activity->update($updateData);
if ($emitEvent) {
$dispatcher->dispatch(new DetachActivityObject($activity, $crmObject));
}
$logger->info($this->getLogPrefix() . ' Detach from activity', $logData);
// Dispatch job to verify if CRM task/event still exists
if ($activity->hasCrmProviderId()) {
VerifyActivityCrmTaskJob::dispatch($activity->getId());
}
}
);
}
public function failed(Throwable $exception): void
{
$crmObject = $this->getEntityType();
Log::critical($this->getLogPrefix() . ' Job failed permanently', [
$crmObject->value => $this->id,
'exception' => $exception->getMessage(),
]);
}
// Abstract methods that must be implemented by the using class
abstract protected function getLogPrefix(): string;
abstract protected function getEntityType(): CrmObject;
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – DeleteCrmEntityTrait.php
|
NULL
|
6361
|
|
6360
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Jiminny\Component\Utility\Service;
use Illuminate\Cache\RateLimiter;
use Jiminny\Contracts\Http\RateLimited;
use Jiminny\Contracts\Http\RateLimitInterface;
class ProviderRateLimiter
{
protected RateLimiter $rateLimiter;
public function __construct(RateLimiter $rateLimiter)
{
$this->rateLimiter = $rateLimiter;
}
public function canMakeRequest(RateLimited $provider): bool
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$key = $rateLimit->getKey();
if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {
return false;
}
}
return true;
}
public function requestAvailableIn(RateLimited $provider): int
{
return $provider->getRateLimits()->isNotEmpty()
? $provider->getRateLimits()
->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))
->max()
: 0
;
}
public function incrementRequestCount(RateLimited $provider): void
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());
}
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
2
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm\Delete;
use Illuminate\Events\Dispatcher;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use Jiminny\Enums\CrmObject;
use Jiminny\Events\Crm\DetachActivityObject;
use Jiminny\Models\Activity;
use Psr\Log\LoggerInterface;
use Throwable;
trait DeleteCrmEntityTrait
{
public int $tries = 3;
public function timeout(): int
{
return 300; // 5 minutes
}
public function backoff(): array
{
return [30, 90, 180]; // 30 seconds, 1.5 minutes, 3 minutes
}
protected function handleActivities(
Collection $activities,
Dispatcher $dispatcher,
LoggerInterface $logger,
bool $emitEvent = true,
): void {
if ($activities->isEmpty()) {
return;
}
$crmObject = $this->getEntityType();
$entityIdField = $crmObject->value . '_id';
$activities->each(
function (Activity $activity) use ($dispatcher, $logger, $entityIdField, $crmObject, $emitEvent): void {
$stageId = $activity->getStage()?->getId();
$logData = [
$crmObject->value => $this->id,
'activity' => $activity->getId(),
'emitEvent' => $emitEvent,
];
$updateData = [
$entityIdField => null,
];
// For leads and opportunities, also nullify the stage_id
if ($stageId && in_array($crmObject, [CrmObject::LEAD, CrmObject::OPPORTUNITY], true)) {
$updateData['stage_id'] = null;
$logData['stage_id'] = $stageId;
}
$activity->update($updateData);
if ($emitEvent) {
$dispatcher->dispatch(new DetachActivityObject($activity, $crmObject));
}
$logger->info($this->getLogPrefix() . ' Detach from activity', $logData);
// Dispatch job to verify if CRM task/event still exists
if ($activity->hasCrmProviderId()) {
VerifyActivityCrmTaskJob::dispatch($activity->getId());
}
}
);
}
public function failed(Throwable $exception): void
{
$crmObject = $this->getEntityType();
Log::critical($this->getLogPrefix() . ' Job failed permanently', [
$crmObject->value => $this->id,
'exception' => $exception->getMessage(),
]);
}
// Abstract methods that must be implemented by the using class
abstract protected function getLogPrefix(): string;
abstract protected function getEntityType(): CrmObject;
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – DeleteCrmEntityTrait.php
|
NULL
|
6360
|
|
6359
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Jiminny\Component\Utility\Service;
use Illuminate\Cache\RateLimiter;
use Jiminny\Contracts\Http\RateLimited;
use Jiminny\Contracts\Http\RateLimitInterface;
class ProviderRateLimiter
{
protected RateLimiter $rateLimiter;
public function __construct(RateLimiter $rateLimiter)
{
$this->rateLimiter = $rateLimiter;
}
public function canMakeRequest(RateLimited $provider): bool
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$key = $rateLimit->getKey();
if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {
return false;
}
}
return true;
}
public function requestAvailableIn(RateLimited $provider): int
{
return $provider->getRateLimits()->isNotEmpty()
? $provider->getRateLimits()
->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))
->max()
: 0
;
}
public function incrementRequestCount(RateLimited $provider): void
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());
}
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
2
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm\Delete;
use Illuminate\Events\Dispatcher;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use Jiminny\Enums\CrmObject;
use Jiminny\Events\Crm\DetachActivityObject;
use Jiminny\Models\Activity;
use Psr\Log\LoggerInterface;
use Throwable;
trait DeleteCrmEntityTrait
{
public int $tries = 3;
public function timeout(): int
{
return 300; // 5 minutes
}
public function backoff(): array
{
return [30, 90, 180]; // 30 seconds, 1.5 minutes, 3 minutes
}
protected function handleActivities(
Collection $activities,
Dispatcher $dispatcher,
LoggerInterface $logger,
bool $emitEvent = true,
): void {
if ($activities->isEmpty()) {
return;
}
$crmObject = $this->getEntityType();
$entityIdField = $crmObject->value . '_id';
$activities->each(
function (Activity $activity) use ($dispatcher, $logger, $entityIdField, $crmObject, $emitEvent): void {
$stageId = $activity->getStage()?->getId();
$logData = [
$crmObject->value => $this->id,
'activity' => $activity->getId(),
'emitEvent' => $emitEvent,
];
$updateData = [
$entityIdField => null,
];
// For leads and opportunities, also nullify the stage_id
if ($stageId && in_array($crmObject, [CrmObject::LEAD, CrmObject::OPPORTUNITY], true)) {
$updateData['stage_id'] = null;
$logData['stage_id'] = $stageId;
}
$activity->update($updateData);
if ($emitEvent) {
$dispatcher->dispatch(new DetachActivityObject($activity, $crmObject));
}
$logger->info($this->getLogPrefix() . ' Detach from activity', $logData);
// Dispatch job to verify if CRM task/event still exists
if ($activity->hasCrmProviderId()) {
VerifyActivityCrmTaskJob::dispatch($activity->getId());
}
}
);
}
public function failed(Throwable $exception): void
{
$crmObject = $this->getEntityType();
Log::critical($this->getLogPrefix() . ' Job failed permanently', [
$crmObject->value => $this->id,
'exception' => $exception->getMessage(),
]);
}
// Abstract methods that must be implemented by the using class
abstract protected function getLogPrefix(): string;
abstract protected function getEntityType(): CrmObject;
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – DeleteCrmEntityTrait.php
|
NULL
|
6359
|
|
6358
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Jiminny\Component\Utility\Service;
use Illuminate\Cache\RateLimiter;
use Jiminny\Contracts\Http\RateLimited;
use Jiminny\Contracts\Http\RateLimitInterface;
class ProviderRateLimiter
{
protected RateLimiter $rateLimiter;
public function __construct(RateLimiter $rateLimiter)
{
$this->rateLimiter = $rateLimiter;
}
public function canMakeRequest(RateLimited $provider): bool
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$key = $rateLimit->getKey();
if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {
return false;
}
}
return true;
}
public function requestAvailableIn(RateLimited $provider): int
{
return $provider->getRateLimits()->isNotEmpty()
? $provider->getRateLimits()
->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))
->max()
: 0
;
}
public function incrementRequestCount(RateLimited $provider): void
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());
}
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
2
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm\Delete;
use Illuminate\Events\Dispatcher;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use Jiminny\Enums\CrmObject;
use Jiminny\Events\Crm\DetachActivityObject;
use Jiminny\Models\Activity;
use Psr\Log\LoggerInterface;
use Throwable;
trait DeleteCrmEntityTrait
{
public int $tries = 3;
public function timeout(): int
{
return 300; // 5 minutes
}
public function backoff(): array
{
return [30, 90, 180]; // 30 seconds, 1.5 minutes, 3 minutes
}
protected function handleActivities(
Collection $activities,
Dispatcher $dispatcher,
LoggerInterface $logger,
bool $emitEvent = true,
): void {
if ($activities->isEmpty()) {
return;
}
$crmObject = $this->getEntityType();
$entityIdField = $crmObject->value . '_id';
$activities->each(
function (Activity $activity) use ($dispatcher, $logger, $entityIdField, $crmObject, $emitEvent): void {
$stageId = $activity->getStage()?->getId();
$logData = [
$crmObject->value => $this->id,
'activity' => $activity->getId(),
'emitEvent' => $emitEvent,
];
$updateData = [
$entityIdField => null,
];
// For leads and opportunities, also nullify the stage_id
if ($stageId && in_array($crmObject, [CrmObject::LEAD, CrmObject::OPPORTUNITY], true)) {
$updateData['stage_id'] = null;
$logData['stage_id'] = $stageId;
}
$activity->update($updateData);
if ($emitEvent) {
$dispatcher->dispatch(new DetachActivityObject($activity, $crmObject));
}
$logger->info($this->getLogPrefix() . ' Detach from activity', $logData);
// Dispatch job to verify if CRM task/event still exists
if ($activity->hasCrmProviderId()) {
VerifyActivityCrmTaskJob::dispatch($activity->getId());
}
}
);
}
public function failed(Throwable $exception): void
{
$crmObject = $this->getEntityType();
Log::critical($this->getLogPrefix() . ' Job failed permanently', [
$crmObject->value => $this->id,
'exception' => $exception->getMessage(),
]);
}
// Abstract methods that must be implemented by the using class
abstract protected function getLogPrefix(): string;
abstract protected function getEntityType(): CrmObject;
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – DeleteCrmEntityTrait.php
|
NULL
|
6358
|
|
6357
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Jiminny\Component\Utility\Service;
use Illuminate\Cache\RateLimiter;
use Jiminny\Contracts\Http\RateLimited;
use Jiminny\Contracts\Http\RateLimitInterface;
class ProviderRateLimiter
{
protected RateLimiter $rateLimiter;
public function __construct(RateLimiter $rateLimiter)
{
$this->rateLimiter = $rateLimiter;
}
public function canMakeRequest(RateLimited $provider): bool
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$key = $rateLimit->getKey();
if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {
return false;
}
}
return true;
}
public function requestAvailableIn(RateLimited $provider): int
{
return $provider->getRateLimits()->isNotEmpty()
? $provider->getRateLimits()
->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))
->max()
: 0
;
}
public function incrementRequestCount(RateLimited $provider): void
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());
}
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
2
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm\Delete;
use Illuminate\Events\Dispatcher;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use Jiminny\Enums\CrmObject;
use Jiminny\Events\Crm\DetachActivityObject;
use Jiminny\Models\Activity;
use Psr\Log\LoggerInterface;
use Throwable;
trait DeleteCrmEntityTrait
{
public int $tries = 3;
public function timeout(): int
{
return 300; // 5 minutes
}
public function backoff(): array
{
return [30, 90, 180]; // 30 seconds, 1.5 minutes, 3 minutes
}
protected function handleActivities(
Collection $activities,
Dispatcher $dispatcher,
LoggerInterface $logger,
bool $emitEvent = true,
): void {
if ($activities->isEmpty()) {
return;
}
$crmObject = $this->getEntityType();
$entityIdField = $crmObject->value . '_id';
$activities->each(
function (Activity $activity) use ($dispatcher, $logger, $entityIdField, $crmObject, $emitEvent): void {
$stageId = $activity->getStage()?->getId();
$logData = [
$crmObject->value => $this->id,
'activity' => $activity->getId(),
'emitEvent' => $emitEvent,
];
$updateData = [
$entityIdField => null,
];
// For leads and opportunities, also nullify the stage_id
if ($stageId && in_array($crmObject, [CrmObject::LEAD, CrmObject::OPPORTUNITY], true)) {
$updateData['stage_id'] = null;
$logData['stage_id'] = $stageId;
}
$activity->update($updateData);
if ($emitEvent) {
$dispatcher->dispatch(new DetachActivityObject($activity, $crmObject));
}
$logger->info($this->getLogPrefix() . ' Detach from activity', $logData);
// Dispatch job to verify if CRM task/event still exists
if ($activity->hasCrmProviderId()) {
VerifyActivityCrmTaskJob::dispatch($activity->getId());
}
}
);
}
public function failed(Throwable $exception): void
{
$crmObject = $this->getEntityType();
Log::critical($this->getLogPrefix() . ' Job failed permanently', [
$crmObject->value => $this->id,
'exception' => $exception->getMessage(),
]);
}
// Abstract methods that must be implemented by the using class
abstract protected function getLogPrefix(): string;
abstract protected function getEntityType(): CrmObject;
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – DeleteCrmEntityTrait.php
|
NULL
|
6357
|
|
6356
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Jiminny\Component\Utility\Service;
use Illuminate\Cache\RateLimiter;
use Jiminny\Contracts\Http\RateLimited;
use Jiminny\Contracts\Http\RateLimitInterface;
class ProviderRateLimiter
{
protected RateLimiter $rateLimiter;
public function __construct(RateLimiter $rateLimiter)
{
$this->rateLimiter = $rateLimiter;
}
public function canMakeRequest(RateLimited $provider): bool
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$key = $rateLimit->getKey();
if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {
return false;
}
}
return true;
}
public function requestAvailableIn(RateLimited $provider): int
{
return $provider->getRateLimits()->isNotEmpty()
? $provider->getRateLimits()
->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))
->max()
: 0
;
}
public function incrementRequestCount(RateLimited $provider): void
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());
}
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
2
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm\Delete;
use Illuminate\Events\Dispatcher;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use Jiminny\Enums\CrmObject;
use Jiminny\Events\Crm\DetachActivityObject;
use Jiminny\Models\Activity;
use Psr\Log\LoggerInterface;
use Throwable;
trait DeleteCrmEntityTrait
{
public int $tries = 3;
public function timeout(): int
{
return 300; // 5 minutes
}
public function backoff(): array
{
return [30, 90, 180]; // 30 seconds, 1.5 minutes, 3 minutes
}
protected function handleActivities(
Collection $activities,
Dispatcher $dispatcher,
LoggerInterface $logger,
bool $emitEvent = true,
): void {
if ($activities->isEmpty()) {
return;
}
$crmObject = $this->getEntityType();
$entityIdField = $crmObject->value . '_id';
$activities->each(
function (Activity $activity) use ($dispatcher, $logger, $entityIdField, $crmObject, $emitEvent): void {
$stageId = $activity->getStage()?->getId();
$logData = [
$crmObject->value => $this->id,
'activity' => $activity->getId(),
'emitEvent' => $emitEvent,
];
$updateData = [
$entityIdField => null,
];
// For leads and opportunities, also nullify the stage_id
if ($stageId && in_array($crmObject, [CrmObject::LEAD, CrmObject::OPPORTUNITY], true)) {
$updateData['stage_id'] = null;
$logData['stage_id'] = $stageId;
}
$activity->update($updateData);
if ($emitEvent) {
$dispatcher->dispatch(new DetachActivityObject($activity, $crmObject));
}
$logger->info($this->getLogPrefix() . ' Detach from activity', $logData);
// Dispatch job to verify if CRM task/event still exists
if ($activity->hasCrmProviderId()) {
VerifyActivityCrmTaskJob::dispatch($activity->getId());
}
}
);
}
public function failed(Throwable $exception): void
{
$crmObject = $this->getEntityType();
Log::critical($this->getLogPrefix() . ' Job failed permanently', [
$crmObject->value => $this->id,
'exception' => $exception->getMessage(),
]);
}
// Abstract methods that must be implemented by the using class
abstract protected function getLogPrefix(): string;
abstract protected function getEntityType(): CrmObject;
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – DeleteCrmEntityTrait.php
|
NULL
|
6356
|
|
6355
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Jiminny\Component\Utility\Service;
use Illuminate\Cache\RateLimiter;
use Jiminny\Contracts\Http\RateLimited;
use Jiminny\Contracts\Http\RateLimitInterface;
class ProviderRateLimiter
{
protected RateLimiter $rateLimiter;
public function __construct(RateLimiter $rateLimiter)
{
$this->rateLimiter = $rateLimiter;
}
public function canMakeRequest(RateLimited $provider): bool
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$key = $rateLimit->getKey();
if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {
return false;
}
}
return true;
}
public function requestAvailableIn(RateLimited $provider): int
{
return $provider->getRateLimits()->isNotEmpty()
? $provider->getRateLimits()
->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))
->max()
: 0
;
}
public function incrementRequestCount(RateLimited $provider): void
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());
}
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
2
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm\Delete;
use Illuminate\Events\Dispatcher;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use Jiminny\Enums\CrmObject;
use Jiminny\Events\Crm\DetachActivityObject;
use Jiminny\Models\Activity;
use Psr\Log\LoggerInterface;
use Throwable;
trait DeleteCrmEntityTrait
{
public int $tries = 3;
public function timeout(): int
{
return 300; // 5 minutes
}
public function backoff(): array
{
return [30, 90, 180]; // 30 seconds, 1.5 minutes, 3 minutes
}
protected function handleActivities(
Collection $activities,
Dispatcher $dispatcher,
LoggerInterface $logger,
bool $emitEvent = true,
): void {
if ($activities->isEmpty()) {
return;
}
$crmObject = $this->getEntityType();
$entityIdField = $crmObject->value . '_id';
$activities->each(
function (Activity $activity) use ($dispatcher, $logger, $entityIdField, $crmObject, $emitEvent): void {
$stageId = $activity->getStage()?->getId();
$logData = [
$crmObject->value => $this->id,
'activity' => $activity->getId(),
'emitEvent' => $emitEvent,
];
$updateData = [
$entityIdField => null,
];
// For leads and opportunities, also nullify the stage_id
if ($stageId && in_array($crmObject, [CrmObject::LEAD, CrmObject::OPPORTUNITY], true)) {
$updateData['stage_id'] = null;
$logData['stage_id'] = $stageId;
}
$activity->update($updateData);
if ($emitEvent) {
$dispatcher->dispatch(new DetachActivityObject($activity, $crmObject));
}
$logger->info($this->getLogPrefix() . ' Detach from activity', $logData);
// Dispatch job to verify if CRM task/event still exists
if ($activity->hasCrmProviderId()) {
VerifyActivityCrmTaskJob::dispatch($activity->getId());
}
}
);
}
public function failed(Throwable $exception): void
{
$crmObject = $this->getEntityType();
Log::critical($this->getLogPrefix() . ' Job failed permanently', [
$crmObject->value => $this->id,
'exception' => $exception->getMessage(),
]);
}
// Abstract methods that must be implemented by the using class
abstract protected function getLogPrefix(): string;
abstract protected function getEntityType(): CrmObject;
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – DeleteCrmEntityTrait.php
|
NULL
|
6355
|
|
6354
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Jiminny\Component\Utility\Service;
use Illuminate\Cache\RateLimiter;
use Jiminny\Contracts\Http\RateLimited;
use Jiminny\Contracts\Http\RateLimitInterface;
class ProviderRateLimiter
{
protected RateLimiter $rateLimiter;
public function __construct(RateLimiter $rateLimiter)
{
$this->rateLimiter = $rateLimiter;
}
public function canMakeRequest(RateLimited $provider): bool
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$key = $rateLimit->getKey();
if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {
return false;
}
}
return true;
}
public function requestAvailableIn(RateLimited $provider): int
{
return $provider->getRateLimits()->isNotEmpty()
? $provider->getRateLimits()
->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))
->max()
: 0
;
}
public function incrementRequestCount(RateLimited $provider): void
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());
}
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
2
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm\Delete;
use Illuminate\Events\Dispatcher;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use Jiminny\Enums\CrmObject;
use Jiminny\Events\Crm\DetachActivityObject;
use Jiminny\Models\Activity;
use Psr\Log\LoggerInterface;
use Throwable;
trait DeleteCrmEntityTrait
{
public int $tries = 3;
public function timeout(): int
{
return 300; // 5 minutes
}
public function backoff(): array
{
return [30, 90, 180]; // 30 seconds, 1.5 minutes, 3 minutes
}
protected function handleActivities(
Collection $activities,
Dispatcher $dispatcher,
LoggerInterface $logger,
bool $emitEvent = true,
): void {
if ($activities->isEmpty()) {
return;
}
$crmObject = $this->getEntityType();
$entityIdField = $crmObject->value . '_id';
$activities->each(
function (Activity $activity) use ($dispatcher, $logger, $entityIdField, $crmObject, $emitEvent): void {
$stageId = $activity->getStage()?->getId();
$logData = [
$crmObject->value => $this->id,
'activity' => $activity->getId(),
'emitEvent' => $emitEvent,
];
$updateData = [
$entityIdField => null,
];
// For leads and opportunities, also nullify the stage_id
if ($stageId && in_array($crmObject, [CrmObject::LEAD, CrmObject::OPPORTUNITY], true)) {
$updateData['stage_id'] = null;
$logData['stage_id'] = $stageId;
}
$activity->update($updateData);
if ($emitEvent) {
$dispatcher->dispatch(new DetachActivityObject($activity, $crmObject));
}
$logger->info($this->getLogPrefix() . ' Detach from activity', $logData);
// Dispatch job to verify if CRM task/event still exists
if ($activity->hasCrmProviderId()) {
VerifyActivityCrmTaskJob::dispatch($activity->getId());
}
}
);
}
public function failed(Throwable $exception): void
{
$crmObject = $this->getEntityType();
Log::critical($this->getLogPrefix() . ' Job failed permanently', [
$crmObject->value => $this->id,
'exception' => $exception->getMessage(),
]);
}
// Abstract methods that must be implemented by the using class
abstract protected function getLogPrefix(): string;
abstract protected function getEntityType(): CrmObject;
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – DeleteCrmEntityTrait.php
|
NULL
|
6354
|
|
6353
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Jiminny\Component\Utility\Service;
use Illuminate\Cache\RateLimiter;
use Jiminny\Contracts\Http\RateLimited;
use Jiminny\Contracts\Http\RateLimitInterface;
class ProviderRateLimiter
{
protected RateLimiter $rateLimiter;
public function __construct(RateLimiter $rateLimiter)
{
$this->rateLimiter = $rateLimiter;
}
public function canMakeRequest(RateLimited $provider): bool
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$key = $rateLimit->getKey();
if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {
return false;
}
}
return true;
}
public function requestAvailableIn(RateLimited $provider): int
{
return $provider->getRateLimits()->isNotEmpty()
? $provider->getRateLimits()
->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))
->max()
: 0
;
}
public function incrementRequestCount(RateLimited $provider): void
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());
}
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
2
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm\Delete;
use Illuminate\Events\Dispatcher;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use Jiminny\Enums\CrmObject;
use Jiminny\Events\Crm\DetachActivityObject;
use Jiminny\Models\Activity;
use Psr\Log\LoggerInterface;
use Throwable;
trait DeleteCrmEntityTrait
{
public int $tries = 3;
public function timeout(): int
{
return 300; // 5 minutes
}
public function backoff(): array
{
return [30, 90, 180]; // 30 seconds, 1.5 minutes, 3 minutes
}
protected function handleActivities(
Collection $activities,
Dispatcher $dispatcher,
LoggerInterface $logger,
bool $emitEvent = true,
): void {
if ($activities->isEmpty()) {
return;
}
$crmObject = $this->getEntityType();
$entityIdField = $crmObject->value . '_id';
$activities->each(
function (Activity $activity) use ($dispatcher, $logger, $entityIdField, $crmObject, $emitEvent): void {
$stageId = $activity->getStage()?->getId();
$logData = [
$crmObject->value => $this->id,
'activity' => $activity->getId(),
'emitEvent' => $emitEvent,
];
$updateData = [
$entityIdField => null,
];
// For leads and opportunities, also nullify the stage_id
if ($stageId && in_array($crmObject, [CrmObject::LEAD, CrmObject::OPPORTUNITY], true)) {
$updateData['stage_id'] = null;
$logData['stage_id'] = $stageId;
}
$activity->update($updateData);
if ($emitEvent) {
$dispatcher->dispatch(new DetachActivityObject($activity, $crmObject));
}
$logger->info($this->getLogPrefix() . ' Detach from activity', $logData);
// Dispatch job to verify if CRM task/event still exists
if ($activity->hasCrmProviderId()) {
VerifyActivityCrmTaskJob::dispatch($activity->getId());
}
}
);
}
public function failed(Throwable $exception): void
{
$crmObject = $this->getEntityType();
Log::critical($this->getLogPrefix() . ' Job failed permanently', [
$crmObject->value => $this->id,
'exception' => $exception->getMessage(),
]);
}
// Abstract methods that must be implemented by the using class
abstract protected function getLogPrefix(): string;
abstract protected function getEntityType(): CrmObject;
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – DeleteCrmEntityTrait.php
|
NULL
|
6353
|
|
6352
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Jiminny\Component\Utility\Service;
use Illuminate\Cache\RateLimiter;
use Jiminny\Contracts\Http\RateLimited;
use Jiminny\Contracts\Http\RateLimitInterface;
class ProviderRateLimiter
{
protected RateLimiter $rateLimiter;
public function __construct(RateLimiter $rateLimiter)
{
$this->rateLimiter = $rateLimiter;
}
public function canMakeRequest(RateLimited $provider): bool
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$key = $rateLimit->getKey();
if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {
return false;
}
}
return true;
}
public function requestAvailableIn(RateLimited $provider): int
{
return $provider->getRateLimits()->isNotEmpty()
? $provider->getRateLimits()
->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))
->max()
: 0
;
}
public function incrementRequestCount(RateLimited $provider): void
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());
}
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
2
Previous Highlighted Error...
|
PhpStorm
|
faVsco.js – DeleteCrmEntityTrait.php
|
NULL
|
6352
|
|
6351
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Jiminny\Component\Utility\Service;
use Illuminate\Cache\RateLimiter;
use Jiminny\Contracts\Http\RateLimited;
use Jiminny\Contracts\Http\RateLimitInterface;
class ProviderRateLimiter
{
protected RateLimiter $rateLimiter;
public function __construct(RateLimiter $rateLimiter)
{
$this->rateLimiter = $rateLimiter;
}
public function canMakeRequest(RateLimited $provider): bool
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$key = $rateLimit->getKey();
if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {
return false;
}
}
return true;
}
public function requestAvailableIn(RateLimited $provider): int
{
return $provider->getRateLimits()->isNotEmpty()
? $provider->getRateLimits()
->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))
->max()
: 0
;
}
public function incrementRequestCount(RateLimited $provider): void
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());
}
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
2
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm\Delete;
use Illuminate\Events\Dispatcher;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use Jiminny\Enums\CrmObject;
use Jiminny\Events\Crm\DetachActivityObject;
use Jiminny\Models\Activity;
use Psr\Log\LoggerInterface;
use Throwable;
trait DeleteCrmEntityTrait
{
public int $tries = 3;
public function timeout(): int
{
return 300; // 5 minutes
}
public function backoff(): array
{
return [30, 90, 180]; // 30 seconds, 1.5 minutes, 3 minutes
}
protected function handleActivities(
Collection $activities,
Dispatcher $dispatcher,
LoggerInterface $logger,
bool $emitEvent = true,
): void {
if ($activities->isEmpty()) {
return;
}
$crmObject = $this->getEntityType();
$entityIdField = $crmObject->value . '_id';
$activities->each(
function (Activity $activity) use ($dispatcher, $logger, $entityIdField, $crmObject, $emitEvent): void {
$stageId = $activity->getStage()?->getId();
$logData = [
$crmObject->value => $this->id,
'activity' => $activity->getId(),
'emitEvent' => $emitEvent,
];
$updateData = [
$entityIdField => null,
];
// For leads and opportunities, also nullify the stage_id
if ($stageId && in_array($crmObject, [CrmObject::LEAD, CrmObject::OPPORTUNITY], true)) {
$updateData['stage_id'] = null;
$logData['stage_id'] = $stageId;
}
$activity->update($updateData);
if ($emitEvent) {
$dispatcher->dispatch(new DetachActivityObject($activity, $crmObject));
}
$logger->info($this->getLogPrefix() . ' Detach from activity', $logData);
// Dispatch job to verify if CRM task/event still exists
if ($activity->hasCrmProviderId()) {
VerifyActivityCrmTaskJob::dispatch($activity->getId());
}
}
);
}
public function failed(Throwable $exception): void
{
$crmObject = $this->getEntityType();
Log::critical($this->getLogPrefix() . ' Job failed permanently', [
$crmObject->value => $this->id,
'exception' => $exception->getMessage(),
]);
}
// Abstract methods that must be implemented by the using class
abstract protected function getLogPrefix(): string;
abstract protected function getEntityType(): CrmObject;
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – DeleteCrmEntityTrait.php
|
NULL
|
6351
|
|
6350
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Jiminny\Component\Utility\Service;
use Illuminate\Cache\RateLimiter;
use Jiminny\Contracts\Http\RateLimited;
use Jiminny\Contracts\Http\RateLimitInterface;
class ProviderRateLimiter
{
protected RateLimiter $rateLimiter;
public function __construct(RateLimiter $rateLimiter)
{
$this->rateLimiter = $rateLimiter;
}
public function canMakeRequest(RateLimited $provider): bool
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$key = $rateLimit->getKey();
if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {
return false;
}
}
return true;
}
public function requestAvailableIn(RateLimited $provider): int
{
return $provider->getRateLimits()->isNotEmpty()
? $provider->getRateLimits()
->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))
->max()
: 0
;
}
public function incrementRequestCount(RateLimited $provider): void
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());
}
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
2
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm\Delete;
use Illuminate\Events\Dispatcher;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use Jiminny\Enums\CrmObject;
use Jiminny\Events\Crm\DetachActivityObject;
use Jiminny\Models\Activity;
use Psr\Log\LoggerInterface;
use Throwable;
trait DeleteCrmEntityTrait
{
public int $tries = 3;
public function timeout(): int
{
return 300; // 5 minutes
}
public function backoff(): array
{
return [30, 90, 180]; // 30 seconds, 1.5 minutes, 3 minutes
}
protected function handleActivities(
Collection $activities,
Dispatcher $dispatcher,
LoggerInterface $logger,
bool $emitEvent = true,
): void {
if ($activities->isEmpty()) {
return;
}
$crmObject = $this->getEntityType();
$entityIdField = $crmObject->value . '_id';
$activities->each(
function (Activity $activity) use ($dispatcher, $logger, $entityIdField, $crmObject, $emitEvent): void {
$stageId = $activity->getStage()?->getId();
$logData = [
$crmObject->value => $this->id,
'activity' => $activity->getId(),
'emitEvent' => $emitEvent,
];
$updateData = [
$entityIdField => null,
];
// For leads and opportunities, also nullify the stage_id
if ($stageId && in_array($crmObject, [CrmObject::LEAD, CrmObject::OPPORTUNITY], true)) {
$updateData['stage_id'] = null;
$logData['stage_id'] = $stageId;
}
$activity->update($updateData);
if ($emitEvent) {
$dispatcher->dispatch(new DetachActivityObject($activity, $crmObject));
}
$logger->info($this->getLogPrefix() . ' Detach from activity', $logData);
// Dispatch job to verify if CRM task/event still exists
if ($activity->hasCrmProviderId()) {
VerifyActivityCrmTaskJob::dispatch($activity->getId());
}
}
);
}
public function failed(Throwable $exception): void
{
$crmObject = $this->getEntityType();
Log::critical($this->getLogPrefix() . ' Job failed permanently', [
$crmObject->value => $this->id,
'exception' => $exception->getMessage(),
]);
}
// Abstract methods that must be implemented by the using class
abstract protected function getLogPrefix(): string;
abstract protected function getEntityType(): CrmObject;
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – DeleteCrmEntityTrait.php
|
NULL
|
6350
|
|
6349
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Jiminny\Component\Utility\Service;
use Illuminate\Cache\RateLimiter;
use Jiminny\Contracts\Http\RateLimited;
use Jiminny\Contracts\Http\RateLimitInterface;
class ProviderRateLimiter
{
protected RateLimiter $rateLimiter;
public function __construct(RateLimiter $rateLimiter)
{
$this->rateLimiter = $rateLimiter;
}
public function canMakeRequest(RateLimited $provider): bool
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$key = $rateLimit->getKey();
if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {
return false;
}
}
return true;
}
public function requestAvailableIn(RateLimited $provider): int
{
return $provider->getRateLimits()->isNotEmpty()
? $provider->getRateLimits()
->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))
->max()
: 0
;
}
public function incrementRequestCount(RateLimited $provider): void
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());
}
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
2
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm\Delete;
use Illuminate\Events\Dispatcher;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use Jiminny\Enums\CrmObject;
use Jiminny\Events\Crm\DetachActivityObject;
use Jiminny\Models\Activity;
use Psr\Log\LoggerInterface;
use Throwable;
trait DeleteCrmEntityTrait
{
public int $tries = 3;
public function timeout(): int
{
return 300; // 5 minutes
}
public function backoff(): array
{
return [30, 90, 180]; // 30 seconds, 1.5 minutes, 3 minutes
}
protected function handleActivities(
Collection $activities,
Dispatcher $dispatcher,
LoggerInterface $logger,
bool $emitEvent = true,
): void {
if ($activities->isEmpty()) {
return;
}
$crmObject = $this->getEntityType();
$entityIdField = $crmObject->value . '_id';
$activities->each(
function (Activity $activity) use ($dispatcher, $logger, $entityIdField, $crmObject, $emitEvent): void {
$stageId = $activity->getStage()?->getId();
$logData = [
$crmObject->value => $this->id,
'activity' => $activity->getId(),
'emitEvent' => $emitEvent,
];
$updateData = [
$entityIdField => null,
];
// For leads and opportunities, also nullify the stage_id
if ($stageId && in_array($crmObject, [CrmObject::LEAD, CrmObject::OPPORTUNITY], true)) {
$updateData['stage_id'] = null;
$logData['stage_id'] = $stageId;
}
$activity->update($updateData);
if ($emitEvent) {
$dispatcher->dispatch(new DetachActivityObject($activity, $crmObject));
}
$logger->info($this->getLogPrefix() . ' Detach from activity', $logData);
// Dispatch job to verify if CRM task/event still exists
if ($activity->hasCrmProviderId()) {
VerifyActivityCrmTaskJob::dispatch($activity->getId());
}
}
);
}
public function failed(Throwable $exception): void
{
$crmObject = $this->getEntityType();
Log::critical($this->getLogPrefix() . ' Job failed permanently', [
$crmObject->value => $this->id,
'exception' => $exception->getMessage(),
]);
}
// Abstract methods that must be implemented by the using class
abstract protected function getLogPrefix(): string;
abstract protected function getEntityType(): CrmObject;
}...
|
PhpStorm
|
faVsco.js – DeleteCrmEntityTrait.php
|
NULL
|
6349
|
|
6348
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Jiminny\Component\Utility\Service;
use Illuminate\Cache\RateLimiter;
use Jiminny\Contracts\Http\RateLimited;
use Jiminny\Contracts\Http\RateLimitInterface;
class ProviderRateLimiter
{
protected RateLimiter $rateLimiter;
public function __construct(RateLimiter $rateLimiter)
{
$this->rateLimiter = $rateLimiter;
}
public function canMakeRequest(RateLimited $provider): bool
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$key = $rateLimit->getKey();
if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {
return false;
}
}
return true;
}
public function requestAvailableIn(RateLimited $provider): int
{
return $provider->getRateLimits()->isNotEmpty()
? $provider->getRateLimits()
->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))
->max()
: 0
;
}
public function incrementRequestCount(RateLimited $provider): void
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());
}
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Activity\Import;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Events\Dispatcher;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\Queue\Constants;
use Jiminny\Component\Utility\Service\ProviderRateLimiter;
use Jiminny\Contracts\Services\Crm\ServiceInterface;
use Jiminny\DTO\ImportCall\Call;
use Jiminny\Component\TranscriptionSummary\Events\TranscriptionAiSummaryReadyEvent;
use Jiminny\Events\Activities\AiAutomation\ActivityProspectAdded;
use Jiminny\Exceptions\SocialAccountTokenInvalidException;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Participant;
use Jiminny\Models\Team;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmObjectsResolver;
use Jiminny\Services\Crm\ProspectSearchStrategyFactory;
use Jiminny\Services\Crm\ProviderRegistry;
use Psr\Log\LoggerInterface;
class MatchCrmData implements ShouldQueue
{
use Dispatchable;
use InteractsWithQueue;
use Queueable;
// AWS visibility timeout allows a maximum of 12 hours. This is 1 minute less.
private const int MAX_DELAY = 43140;
// Infrastructure allows max 3 retries (1 initial execution + 3 retries)
public int $tries = 4;
private Call $call;
private int $activityId;
private Team $team;
private ServiceInterface $crmService;
private LoggerInterface $logger;
private array $logContext;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(Call $call, int $activityId)
{
$this->call = $call;
$this->activityId = $activityId;
$this->logContext = [
'activity_id' => $activityId,
'call_id' => $call->getCallId(),
'provider' => $call->getProvider(),
];
$this->onQueue(Constants::QUEUE_DIALERS);
}
public function handle(
CrmObjectsResolver $crmObjectsResolver,
ProviderRegistry $providerRegistry,
ProviderRateLimiter $rateLimiter,
ActivityRepository $activityRepository,
LoggerInterface $logger,
Dispatcher $eventDispatcher,
): void {
$this->logger = $logger;
// Activity is already augmented with CRM data, no need to perform the same operation
if ($this->call->isActivityUpdatedWithCrm()) {
$this->logMessage('Skipping activity. Already updated with CRM data');
return;
}
/** @var Activity $activity */
$activity = $activityRepository->findById($this->activityId);
try {
$this->initialiseCrmService($activity, $providerRegistry);
} catch (SocialAccountTokenInvalidException $exception) {
$this->logMessage('Invalid token, retrying');
$this->release(self::MAX_DELAY); // Try again tomorrow
return;
}
$prospectSearchStrategy = ProspectSearchStrategyFactory::match($this->team);
if ($prospectSearchStrategy->ignoreCrmMatchData()) {
// Ignore any associated opportunity
$this->logger->info('[MatchCrmData] Ignoring crm data because of prospect strategy', [
'activity_id' => $this->activityId,
'strategy' => get_class($prospectSearchStrategy),
]);
return;
}
if (! $rateLimiter->canMakeRequest($activity->getCrm())) {
$this->logMessage('Rate limit reached, retrying');
$this->release($rateLimiter->requestAvailableIn($activity->getCrm()) + random_int(1, 60));
return;
}
$this->logMessage('Resolving CRM objects');
$rateLimiter->incrementRequestCount($activity->getCrm());
$crmObjects = $crmObjectsResolver->resolveFromCall($this->crmService, $this->call);
if (empty($crmObjects)) {
$this->logMessage('Could not resolve CRM objects, retrying');
$this->release(3600);
return;
}
[$lead, $account, $opportunity, $contact, $stage] = $crmObjects;
$activity->update([
'lead_id' => $lead->id ?? null,
'contact_id' => $contact->id ?? null,
'account_id' => $account->id ?? null,
'opportunity_id' => $opportunity->id ?? null,
'stage_id' => $stage->id ?? null,
]);
$activity->refresh();
$eventDispatcher->dispatch(new ActivityProspectAdded(
activity: $activity,
eventSource: 'match-crm-data'
));
if ($activity->getProspectName() !== null) {
$activity->setTitleFromCallData($this->call);
/** @var Participant $prospectParticipant */
$prospectParticipant = $activity
->participants()
->where(function (Builder $query) use ($activity) {
$query
->whereNull('user_id')
->orWhere('user_id', '!=', $activity->getUserId())
;
})
->first()
;
$activity->updateParticipantCrmData($crmObjects, $prospectParticipant);
}
$this->logMessage('Activity updated');
$this->triggerSummaryPushIfReady($activity, $eventDispatcher);
}
private function triggerSummaryPushIfReady(Activity $activity, Dispatcher $eventDispatcher): void
{
if (! $activity->hasTranscriptionId()) {
return;
}
if ($activity->hasProspectActivitySummaryLog()) {
$this->logMessage('Summary already sent to prospect, skipping summary push after CRM matching');
return;
}
$this->logMessage('Triggering summary push after CRM matching');
$eventDispatcher->dispatch(new TranscriptionAiSummaryReadyEvent($activity->getUuid()));
}
private function initialiseCrmService(Activity $activity, ProviderRegistry $providerRegistry): void
{
$this->team = $activity->getUser()->getTeam();
$crmProviderName = $this->team->getCrmConfiguration()->getProviderName();
$crmService = $providerRegistry->get($crmProviderName);
$crmService->setUser($this->team->getOwner());
$this->crmService = $crmService;
$this->logContext['team'] = $this->team->getSlug();
$this->logContext['team_id'] = $this->team->getId();
}
private function logMessage(string $message): void
{
$this->logger->info(sprintf('[MatchCrmData] %s', $message), $this->logContext);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – MatchCrmData.php
|
NULL
|
6348
|
|
6347
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Jiminny\Component\Utility\Service;
use Illuminate\Cache\RateLimiter;
use Jiminny\Contracts\Http\RateLimited;
use Jiminny\Contracts\Http\RateLimitInterface;
class ProviderRateLimiter
{
protected RateLimiter $rateLimiter;
public function __construct(RateLimiter $rateLimiter)
{
$this->rateLimiter = $rateLimiter;
}
public function canMakeRequest(RateLimited $provider): bool
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$key = $rateLimit->getKey();
if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {
return false;
}
}
return true;
}
public function requestAvailableIn(RateLimited $provider): int
{
return $provider->getRateLimits()->isNotEmpty()
? $provider->getRateLimits()
->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))
->max()
: 0
;
}
public function incrementRequestCount(RateLimited $provider): void
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());
}
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Activity\Import;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Events\Dispatcher;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\Queue\Constants;
use Jiminny\Component\Utility\Service\ProviderRateLimiter;
use Jiminny\Contracts\Services\Crm\ServiceInterface;
use Jiminny\DTO\ImportCall\Call;
use Jiminny\Component\TranscriptionSummary\Events\TranscriptionAiSummaryReadyEvent;
use Jiminny\Events\Activities\AiAutomation\ActivityProspectAdded;
use Jiminny\Exceptions\SocialAccountTokenInvalidException;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Participant;
use Jiminny\Models\Team;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmObjectsResolver;
use Jiminny\Services\Crm\ProspectSearchStrategyFactory;
use Jiminny\Services\Crm\ProviderRegistry;
use Psr\Log\LoggerInterface;
class MatchCrmData implements ShouldQueue
{
use Dispatchable;
use InteractsWithQueue;
use Queueable;
// AWS visibility timeout allows a maximum of 12 hours. This is 1 minute less.
private const int MAX_DELAY = 43140;
// Infrastructure allows max 3 retries (1 initial execution + 3 retries)
public int $tries = 4;
private Call $call;
private int $activityId;
private Team $team;
private ServiceInterface $crmService;
private LoggerInterface $logger;
private array $logContext;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(Call $call, int $activityId)
{
$this->call = $call;
$this->activityId = $activityId;
$this->logContext = [
'activity_id' => $activityId,
'call_id' => $call->getCallId(),
'provider' => $call->getProvider(),
];
$this->onQueue(Constants::QUEUE_DIALERS);
}
public function handle(
CrmObjectsResolver $crmObjectsResolver,
ProviderRegistry $providerRegistry,
ProviderRateLimiter $rateLimiter,
ActivityRepository $activityRepository,
LoggerInterface $logger,
Dispatcher $eventDispatcher,
): void {
$this->logger = $logger;
// Activity is already augmented with CRM data, no need to perform the same operation
if ($this->call->isActivityUpdatedWithCrm()) {
$this->logMessage('Skipping activity. Already updated with CRM data');
return;
}
/** @var Activity $activity */
$activity = $activityRepository->findById($this->activityId);
try {
$this->initialiseCrmService($activity, $providerRegistry);
} catch (SocialAccountTokenInvalidException $exception) {
$this->logMessage('Invalid token, retrying');
$this->release(self::MAX_DELAY); // Try again tomorrow
return;
}
$prospectSearchStrategy = ProspectSearchStrategyFactory::match($this->team);
if ($prospectSearchStrategy->ignoreCrmMatchData()) {
// Ignore any associated opportunity
$this->logger->info('[MatchCrmData] Ignoring crm data because of prospect strategy', [
'activity_id' => $this->activityId,
'strategy' => get_class($prospectSearchStrategy),
]);
return;
}
if (! $rateLimiter->canMakeRequest($activity->getCrm())) {
$this->logMessage('Rate limit reached, retrying');
$this->release($rateLimiter->requestAvailableIn($activity->getCrm()) + random_int(1, 60));
return;
}
$this->logMessage('Resolving CRM objects');
$rateLimiter->incrementRequestCount($activity->getCrm());
$crmObjects = $crmObjectsResolver->resolveFromCall($this->crmService, $this->call);
if (empty($crmObjects)) {
$this->logMessage('Could not resolve CRM objects, retrying');
$this->release(3600);
return;
}
[$lead, $account, $opportunity, $contact, $stage] = $crmObjects;
$activity->update([
'lead_id' => $lead->id ?? null,
'contact_id' => $contact->id ?? null,
'account_id' => $account->id ?? null,
'opportunity_id' => $opportunity->id ?? null,
'stage_id' => $stage->id ?? null,
]);
$activity->refresh();
$eventDispatcher->dispatch(new ActivityProspectAdded(
activity: $activity,
eventSource: 'match-crm-data'
));
if ($activity->getProspectName() !== null) {
$activity->setTitleFromCallData($this->call);
/** @var Participant $prospectParticipant */
$prospectParticipant = $activity
->participants()
->where(function (Builder $query) use ($activity) {
$query
->whereNull('user_id')
->orWhere('user_id', '!=', $activity->getUserId())
;
})
->first()
;
$activity->updateParticipantCrmData($crmObjects, $prospectParticipant);
}
$this->logMessage('Activity updated');
$this->triggerSummaryPushIfReady($activity, $eventDispatcher);
}
private function triggerSummaryPushIfReady(Activity $activity, Dispatcher $eventDispatcher): void
{
if (! $activity->hasTranscriptionId()) {
return;
}
if ($activity->hasProspectActivitySummaryLog()) {
$this->logMessage('Summary already sent to prospect, skipping summary push after CRM matching');
return;
}
$this->logMessage('Triggering summary push after CRM matching');
$eventDispatcher->dispatch(new TranscriptionAiSummaryReadyEvent($activity->getUuid()));
}
private function initialiseCrmService(Activity $activity, ProviderRegistry $providerRegistry): void
{
$this->team = $activity->getUser()->getTeam();
$crmProviderName = $this->team->getCrmConfiguration()->getProviderName();
$crmService = $providerRegistry->get($crmProviderName);
$crmService->setUser($this->team->getOwner());
$this->crmService = $crmService;
$this->logContext['team'] = $this->team->getSlug();
$this->logContext['team_id'] = $this->team->getId();
}
private function logMessage(string $message): void
{
$this->logger->info(sprintf('[MatchCrmData] %s', $message), $this->logContext);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – MatchCrmData.php
|
NULL
|
6347
|
|
6346
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Jiminny\Component\Utility\Service;
use Illuminate\Cache\RateLimiter;
use Jiminny\Contracts\Http\RateLimited;
use Jiminny\Contracts\Http\RateLimitInterface;
class ProviderRateLimiter
{
protected RateLimiter $rateLimiter;
public function __construct(RateLimiter $rateLimiter)
{
$this->rateLimiter = $rateLimiter;
}
public function canMakeRequest(RateLimited $provider): bool
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$key = $rateLimit->getKey();
if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {
return false;
}
}
return true;
}
public function requestAvailableIn(RateLimited $provider): int
{
return $provider->getRateLimits()->isNotEmpty()
? $provider->getRateLimits()
->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))
->max()
: 0
;
}
public function incrementRequestCount(RateLimited $provider): void
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());
}
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Activity\Import;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Events\Dispatcher;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\Queue\Constants;
use Jiminny\Component\Utility\Service\ProviderRateLimiter;
use Jiminny\Contracts\Services\Crm\ServiceInterface;
use Jiminny\DTO\ImportCall\Call;
use Jiminny\Component\TranscriptionSummary\Events\TranscriptionAiSummaryReadyEvent;
use Jiminny\Events\Activities\AiAutomation\ActivityProspectAdded;
use Jiminny\Exceptions\SocialAccountTokenInvalidException;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Participant;
use Jiminny\Models\Team;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmObjectsResolver;
use Jiminny\Services\Crm\ProspectSearchStrategyFactory;
use Jiminny\Services\Crm\ProviderRegistry;
use Psr\Log\LoggerInterface;
class MatchCrmData implements ShouldQueue
{
use Dispatchable;
use InteractsWithQueue;
use Queueable;
// AWS visibility timeout allows a maximum of 12 hours. This is 1 minute less.
private const int MAX_DELAY = 43140;
// Infrastructure allows max 3 retries (1 initial execution + 3 retries)
public int $tries = 4;
private Call $call;
private int $activityId;
private Team $team;
private ServiceInterface $crmService;
private LoggerInterface $logger;
private array $logContext;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(Call $call, int $activityId)
{
$this->call = $call;
$this->activityId = $activityId;
$this->logContext = [
'activity_id' => $activityId,
'call_id' => $call->getCallId(),
'provider' => $call->getProvider(),
];
$this->onQueue(Constants::QUEUE_DIALERS);
}
public function handle(
CrmObjectsResolver $crmObjectsResolver,
ProviderRegistry $providerRegistry,
ProviderRateLimiter $rateLimiter,
ActivityRepository $activityRepository,
LoggerInterface $logger,
Dispatcher $eventDispatcher,
): void {
$this->logger = $logger;
// Activity is already augmented with CRM data, no need to perform the same operation
if ($this->call->isActivityUpdatedWithCrm()) {
$this->logMessage('Skipping activity. Already updated with CRM data');
return;
}
/** @var Activity $activity */
$activity = $activityRepository->findById($this->activityId);
try {
$this->initialiseCrmService($activity, $providerRegistry);
} catch (SocialAccountTokenInvalidException $exception) {
$this->logMessage('Invalid token, retrying');
$this->release(self::MAX_DELAY); // Try again tomorrow
return;
}
$prospectSearchStrategy = ProspectSearchStrategyFactory::match($this->team);
if ($prospectSearchStrategy->ignoreCrmMatchData()) {
// Ignore any associated opportunity
$this->logger->info('[MatchCrmData] Ignoring crm data because of prospect strategy', [
'activity_id' => $this->activityId,
'strategy' => get_class($prospectSearchStrategy),
]);
return;
}
if (! $rateLimiter->canMakeRequest($activity->getCrm())) {
$this->logMessage('Rate limit reached, retrying');
$this->release($rateLimiter->requestAvailableIn($activity->getCrm()) + random_int(1, 60));
return;
}
$this->logMessage('Resolving CRM objects');
$rateLimiter->incrementRequestCount($activity->getCrm());
$crmObjects = $crmObjectsResolver->resolveFromCall($this->crmService, $this->call);
if (empty($crmObjects)) {
$this->logMessage('Could not resolve CRM objects, retrying');
$this->release(3600);
return;
}
[$lead, $account, $opportunity, $contact, $stage] = $crmObjects;
$activity->update([
'lead_id' => $lead->id ?? null,
'contact_id' => $contact->id ?? null,
'account_id' => $account->id ?? null,
'opportunity_id' => $opportunity->id ?? null,
'stage_id' => $stage->id ?? null,
]);
$activity->refresh();
$eventDispatcher->dispatch(new ActivityProspectAdded(
activity: $activity,
eventSource: 'match-crm-data'
));
if ($activity->getProspectName() !== null) {
$activity->setTitleFromCallData($this->call);
/** @var Participant $prospectParticipant */
$prospectParticipant = $activity
->participants()
->where(function (Builder $query) use ($activity) {
$query
->whereNull('user_id')
->orWhere('user_id', '!=', $activity->getUserId())
;
})
->first()
;
$activity->updateParticipantCrmData($crmObjects, $prospectParticipant);
}
$this->logMessage('Activity updated');
$this->triggerSummaryPushIfReady($activity, $eventDispatcher);
}
private function triggerSummaryPushIfReady(Activity $activity, Dispatcher $eventDispatcher): void
{
if (! $activity->hasTranscriptionId()) {
return;
}
if ($activity->hasProspectActivitySummaryLog()) {
$this->logMessage('Summary already sent to prospect, skipping summary push after CRM matching');
return;
}
$this->logMessage('Triggering summary push after CRM matching');
$eventDispatcher->dispatch(new TranscriptionAiSummaryReadyEvent($activity->getUuid()));
}
private function initialiseCrmService(Activity $activity, ProviderRegistry $providerRegistry): void
{
$this->team = $activity->getUser()->getTeam();
$crmProviderName = $this->team->getCrmConfiguration()->getProviderName();
$crmService = $providerRegistry->get($crmProviderName);
$crmService->setUser($this->team->getOwner());
$this->crmService = $crmService;
$this->logContext['team'] = $this->team->getSlug();
$this->logContext['team_id'] = $this->team->getId();
}
private function logMessage(string $message): void
{
$this->logger->info(sprintf('[MatchCrmData] %s', $message), $this->logContext);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – MatchCrmData.php
|
NULL
|
6346
|
|
6345
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Jiminny\Component\Utility\Service;
use Illuminate\Cache\RateLimiter;
use Jiminny\Contracts\Http\RateLimited;
use Jiminny\Contracts\Http\RateLimitInterface;
class ProviderRateLimiter
{
protected RateLimiter $rateLimiter;
public function __construct(RateLimiter $rateLimiter)
{
$this->rateLimiter = $rateLimiter;
}
public function canMakeRequest(RateLimited $provider): bool
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$key = $rateLimit->getKey();
if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {
return false;
}
}
return true;
}
public function requestAvailableIn(RateLimited $provider): int
{
return $provider->getRateLimits()->isNotEmpty()
? $provider->getRateLimits()
->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))
->max()
: 0
;
}
public function incrementRequestCount(RateLimited $provider): void
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());
}
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Activity\Import;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Events\Dispatcher;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\Queue\Constants;
use Jiminny\Component\Utility\Service\ProviderRateLimiter;
use Jiminny\Contracts\Services\Crm\ServiceInterface;
use Jiminny\DTO\ImportCall\Call;
use Jiminny\Component\TranscriptionSummary\Events\TranscriptionAiSummaryReadyEvent;
use Jiminny\Events\Activities\AiAutomation\ActivityProspectAdded;
use Jiminny\Exceptions\SocialAccountTokenInvalidException;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Participant;
use Jiminny\Models\Team;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmObjectsResolver;
use Jiminny\Services\Crm\ProspectSearchStrategyFactory;
use Jiminny\Services\Crm\ProviderRegistry;
use Psr\Log\LoggerInterface;
class MatchCrmData implements ShouldQueue
{
use Dispatchable;
use InteractsWithQueue;
use Queueable;
// AWS visibility timeout allows a maximum of 12 hours. This is 1 minute less.
private const int MAX_DELAY = 43140;
// Infrastructure allows max 3 retries (1 initial execution + 3 retries)
public int $tries = 4;
private Call $call;
private int $activityId;
private Team $team;
private ServiceInterface $crmService;
private LoggerInterface $logger;
private array $logContext;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(Call $call, int $activityId)
{
$this->call = $call;
$this->activityId = $activityId;
$this->logContext = [
'activity_id' => $activityId,
'call_id' => $call->getCallId(),
'provider' => $call->getProvider(),
];
$this->onQueue(Constants::QUEUE_DIALERS);
}
public function handle(
CrmObjectsResolver $crmObjectsResolver,
ProviderRegistry $providerRegistry,
ProviderRateLimiter $rateLimiter,
ActivityRepository $activityRepository,
LoggerInterface $logger,
Dispatcher $eventDispatcher,
): void {
$this->logger = $logger;
// Activity is already augmented with CRM data, no need to perform the same operation
if ($this->call->isActivityUpdatedWithCrm()) {
$this->logMessage('Skipping activity. Already updated with CRM data');
return;
}
/** @var Activity $activity */
$activity = $activityRepository->findById($this->activityId);
try {
$this->initialiseCrmService($activity, $providerRegistry);
} catch (SocialAccountTokenInvalidException $exception) {
$this->logMessage('Invalid token, retrying');
$this->release(self::MAX_DELAY); // Try again tomorrow
return;
}
$prospectSearchStrategy = ProspectSearchStrategyFactory::match($this->team);
if ($prospectSearchStrategy->ignoreCrmMatchData()) {
// Ignore any associated opportunity
$this->logger->info('[MatchCrmData] Ignoring crm data because of prospect strategy', [
'activity_id' => $this->activityId,
'strategy' => get_class($prospectSearchStrategy),
]);
return;
}
if (! $rateLimiter->canMakeRequest($activity->getCrm())) {
$this->logMessage('Rate limit reached, retrying');
$this->release($rateLimiter->requestAvailableIn($activity->getCrm()) + random_int(1, 60));
return;
}
$this->logMessage('Resolving CRM objects');
$rateLimiter->incrementRequestCount($activity->getCrm());
$crmObjects = $crmObjectsResolver->resolveFromCall($this->crmService, $this->call);
if (empty($crmObjects)) {
$this->logMessage('Could not resolve CRM objects, retrying');
$this->release(3600);
return;
}
[$lead, $account, $opportunity, $contact, $stage] = $crmObjects;
$activity->update([
'lead_id' => $lead->id ?? null,
'contact_id' => $contact->id ?? null,
'account_id' => $account->id ?? null,
'opportunity_id' => $opportunity->id ?? null,
'stage_id' => $stage->id ?? null,
]);
$activity->refresh();
$eventDispatcher->dispatch(new ActivityProspectAdded(
activity: $activity,
eventSource: 'match-crm-data'
));
if ($activity->getProspectName() !== null) {
$activity->setTitleFromCallData($this->call);
/** @var Participant $prospectParticipant */
$prospectParticipant = $activity
->participants()
->where(function (Builder $query) use ($activity) {
$query
->whereNull('user_id')
->orWhere('user_id', '!=', $activity->getUserId())
;
})
->first()
;
$activity->updateParticipantCrmData($crmObjects, $prospectParticipant);
}
$this->logMessage('Activity updated');
$this->triggerSummaryPushIfReady($activity, $eventDispatcher);
}
private function triggerSummaryPushIfReady(Activity $activity, Dispatcher $eventDispatcher): void
{
if (! $activity->hasTranscriptionId()) {
return;
}
if ($activity->hasProspectActivitySummaryLog()) {
$this->logMessage('Summary already sent to prospect, skipping summary push after CRM matching');
return;
}
$this->logMessage('Triggering summary push after CRM matching');
$eventDispatcher->dispatch(new TranscriptionAiSummaryReadyEvent($activity->getUuid()));
}
private function initialiseCrmService(Activity $activity, ProviderRegistry $providerRegistry): void
{
$this->team = $activity->getUser()->getTeam();
$crmProviderName = $this->team->getCrmConfiguration()->getProviderName();
$crmService = $providerRegistry->get($crmProviderName);
$crmService->setUser($this->team->getOwner());
$this->crmService = $crmService;
$this->logContext['team'] = $this->team->getSlug();
$this->logContext['team_id'] = $this->team->getId();
}
private function logMessage(string $message): void
{
$this->logger->info(sprintf('[MatchCrmData] %s', $message), $this->logContext);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – MatchCrmData.php
|
NULL
|
6345
|
|
6344
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Jiminny\Component\Utility\Service;
use Illuminate\Cache\RateLimiter;
use Jiminny\Contracts\Http\RateLimited;
use Jiminny\Contracts\Http\RateLimitInterface;
class ProviderRateLimiter
{
protected RateLimiter $rateLimiter;
public function __construct(RateLimiter $rateLimiter)
{
$this->rateLimiter = $rateLimiter;
}
public function canMakeRequest(RateLimited $provider): bool
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$key = $rateLimit->getKey();
if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {
return false;
}
}
return true;
}
public function requestAvailableIn(RateLimited $provider): int
{
return $provider->getRateLimits()->isNotEmpty()
? $provider->getRateLimits()
->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))
->max()
: 0
;
}
public function incrementRequestCount(RateLimited $provider): void
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());
}
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Activity\Import;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Events\Dispatcher;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\Queue\Constants;
use Jiminny\Component\Utility\Service\ProviderRateLimiter;
use Jiminny\Contracts\Services\Crm\ServiceInterface;
use Jiminny\DTO\ImportCall\Call;
use Jiminny\Component\TranscriptionSummary\Events\TranscriptionAiSummaryReadyEvent;
use Jiminny\Events\Activities\AiAutomation\ActivityProspectAdded;
use Jiminny\Exceptions\SocialAccountTokenInvalidException;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Participant;
use Jiminny\Models\Team;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmObjectsResolver;
use Jiminny\Services\Crm\ProspectSearchStrategyFactory;
use Jiminny\Services\Crm\ProviderRegistry;
use Psr\Log\LoggerInterface;
class MatchCrmData implements ShouldQueue
{
use Dispatchable;
use InteractsWithQueue;
use Queueable;
// AWS visibility timeout allows a maximum of 12 hours. This is 1 minute less.
private const int MAX_DELAY = 43140;
// Infrastructure allows max 3 retries (1 initial execution + 3 retries)
public int $tries = 4;
private Call $call;
private int $activityId;
private Team $team;
private ServiceInterface $crmService;
private LoggerInterface $logger;
private array $logContext;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(Call $call, int $activityId)
{
$this->call = $call;
$this->activityId = $activityId;
$this->logContext = [
'activity_id' => $activityId,
'call_id' => $call->getCallId(),
'provider' => $call->getProvider(),
];
$this->onQueue(Constants::QUEUE_DIALERS);
}
public function handle(
CrmObjectsResolver $crmObjectsResolver,
ProviderRegistry $providerRegistry,
ProviderRateLimiter $rateLimiter,
ActivityRepository $activityRepository,
LoggerInterface $logger,
Dispatcher $eventDispatcher,
): void {
$this->logger = $logger;
// Activity is already augmented with CRM data, no need to perform the same operation
if ($this->call->isActivityUpdatedWithCrm()) {
$this->logMessage('Skipping activity. Already updated with CRM data');
return;
}
/** @var Activity $activity */
$activity = $activityRepository->findById($this->activityId);
try {
$this->initialiseCrmService($activity, $providerRegistry);
} catch (SocialAccountTokenInvalidException $exception) {
$this->logMessage('Invalid token, retrying');
$this->release(self::MAX_DELAY); // Try again tomorrow
return;
}
$prospectSearchStrategy = ProspectSearchStrategyFactory::match($this->team);
if ($prospectSearchStrategy->ignoreCrmMatchData()) {
// Ignore any associated opportunity
$this->logger->info('[MatchCrmData] Ignoring crm data because of prospect strategy', [
'activity_id' => $this->activityId,
'strategy' => get_class($prospectSearchStrategy),
]);
return;
}
if (! $rateLimiter->canMakeRequest($activity->getCrm())) {
$this->logMessage('Rate limit reached, retrying');
$this->release($rateLimiter->requestAvailableIn($activity->getCrm()) + random_int(1, 60));
return;
}
$this->logMessage('Resolving CRM objects');
$rateLimiter->incrementRequestCount($activity->getCrm());
$crmObjects = $crmObjectsResolver->resolveFromCall($this->crmService, $this->call);
if (empty($crmObjects)) {
$this->logMessage('Could not resolve CRM objects, retrying');
$this->release(3600);
return;
}
[$lead, $account, $opportunity, $contact, $stage] = $crmObjects;
$activity->update([
'lead_id' => $lead->id ?? null,
'contact_id' => $contact->id ?? null,
'account_id' => $account->id ?? null,
'opportunity_id' => $opportunity->id ?? null,
'stage_id' => $stage->id ?? null,
]);
$activity->refresh();
$eventDispatcher->dispatch(new ActivityProspectAdded(
activity: $activity,
eventSource: 'match-crm-data'
));
if ($activity->getProspectName() !== null) {
$activity->setTitleFromCallData($this->call);
/** @var Participant $prospectParticipant */
$prospectParticipant = $activity
->participants()
->where(function (Builder $query) use ($activity) {
$query
->whereNull('user_id')
->orWhere('user_id', '!=', $activity->getUserId())
;
})
->first()
;
$activity->updateParticipantCrmData($crmObjects, $prospectParticipant);
}
$this->logMessage('Activity updated');
$this->triggerSummaryPushIfReady($activity, $eventDispatcher);
}
private function triggerSummaryPushIfReady(Activity $activity, Dispatcher $eventDispatcher): void
{
if (! $activity->hasTranscriptionId()) {
return;
}
if ($activity->hasProspectActivitySummaryLog()) {
$this->logMessage('Summary already sent to prospect, skipping summary push after CRM matching');
return;
}
$this->logMessage('Triggering summary push after CRM matching');
$eventDispatcher->dispatch(new TranscriptionAiSummaryReadyEvent($activity->getUuid()));
}
private function initialiseCrmService(Activity $activity, ProviderRegistry $providerRegistry): void
{
$this->team = $activity->getUser()->getTeam();
$crmProviderName = $this->team->getCrmConfiguration()->getProviderName();
$crmService = $providerRegistry->get($crmProviderName);
$crmService->setUser($this->team->getOwner());
$this->crmService = $crmService;
$this->logContext['team'] = $this->team->getSlug();
$this->logContext['team_id'] = $this->team->getId();
}
private function logMessage(string $message): void
{
$this->logger->info(sprintf('[MatchCrmData] %s', $message), $this->logContext);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – MatchCrmData.php
|
NULL
|
6344
|
|
6343
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Jiminny\Component\Utility\Service;
use Illuminate\Cache\RateLimiter;
use Jiminny\Contracts\Http\RateLimited;
use Jiminny\Contracts\Http\RateLimitInterface;
class ProviderRateLimiter
{
protected RateLimiter $rateLimiter;
public function __construct(RateLimiter $rateLimiter)
{
$this->rateLimiter = $rateLimiter;
}
public function canMakeRequest(RateLimited $provider): bool
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$key = $rateLimit->getKey();
if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {
return false;
}
}
return true;
}
public function requestAvailableIn(RateLimited $provider): int
{
return $provider->getRateLimits()->isNotEmpty()
? $provider->getRateLimits()
->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))
->max()
: 0
;
}
public function incrementRequestCount(RateLimited $provider): void
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());
}
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Activity\Import;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Events\Dispatcher;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\Queue\Constants;
use Jiminny\Component\Utility\Service\ProviderRateLimiter;
use Jiminny\Contracts\Services\Crm\ServiceInterface;
use Jiminny\DTO\ImportCall\Call;
use Jiminny\Component\TranscriptionSummary\Events\TranscriptionAiSummaryReadyEvent;
use Jiminny\Events\Activities\AiAutomation\ActivityProspectAdded;
use Jiminny\Exceptions\SocialAccountTokenInvalidException;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Participant;
use Jiminny\Models\Team;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmObjectsResolver;
use Jiminny\Services\Crm\ProspectSearchStrategyFactory;
use Jiminny\Services\Crm\ProviderRegistry;
use Psr\Log\LoggerInterface;
class MatchCrmData implements ShouldQueue
{
use Dispatchable;
use InteractsWithQueue;
use Queueable;
// AWS visibility timeout allows a maximum of 12 hours. This is 1 minute less.
private const int MAX_DELAY = 43140;
// Infrastructure allows max 3 retries (1 initial execution + 3 retries)
public int $tries = 4;
private Call $call;
private int $activityId;
private Team $team;
private ServiceInterface $crmService;
private LoggerInterface $logger;
private array $logContext;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(Call $call, int $activityId)
{
$this->call = $call;
$this->activityId = $activityId;
$this->logContext = [
'activity_id' => $activityId,
'call_id' => $call->getCallId(),
'provider' => $call->getProvider(),
];
$this->onQueue(Constants::QUEUE_DIALERS);
}
public function handle(
CrmObjectsResolver $crmObjectsResolver,
ProviderRegistry $providerRegistry,
ProviderRateLimiter $rateLimiter,
ActivityRepository $activityRepository,
LoggerInterface $logger,
Dispatcher $eventDispatcher,
): void {
$this->logger = $logger;
// Activity is already augmented with CRM data, no need to perform the same operation
if ($this->call->isActivityUpdatedWithCrm()) {
$this->logMessage('Skipping activity. Already updated with CRM data');
return;
}
/** @var Activity $activity */
$activity = $activityRepository->findById($this->activityId);
try {
$this->initialiseCrmService($activity, $providerRegistry);
} catch (SocialAccountTokenInvalidException $exception) {
$this->logMessage('Invalid token, retrying');
$this->release(self::MAX_DELAY); // Try again tomorrow
return;
}
$prospectSearchStrategy = ProspectSearchStrategyFactory::match($this->team);
if ($prospectSearchStrategy->ignoreCrmMatchData()) {
// Ignore any associated opportunity
$this->logger->info('[MatchCrmData] Ignoring crm data because of prospect strategy', [
'activity_id' => $this->activityId,
'strategy' => get_class($prospectSearchStrategy),
]);
return;
}
if (! $rateLimiter->canMakeRequest($activity->getCrm())) {
$this->logMessage('Rate limit reached, retrying');
$this->release($rateLimiter->requestAvailableIn($activity->getCrm()) + random_int(1, 60));
return;
}
$this->logMessage('Resolving CRM objects');
$rateLimiter->incrementRequestCount($activity->getCrm());
$crmObjects = $crmObjectsResolver->resolveFromCall($this->crmService, $this->call);
if (empty($crmObjects)) {
$this->logMessage('Could not resolve CRM objects, retrying');
$this->release(3600);
return;
}
[$lead, $account, $opportunity, $contact, $stage] = $crmObjects;
$activity->update([
'lead_id' => $lead->id ?? null,
'contact_id' => $contact->id ?? null,
'account_id' => $account->id ?? null,
'opportunity_id' => $opportunity->id ?? null,
'stage_id' => $stage->id ?? null,
]);
$activity->refresh();
$eventDispatcher->dispatch(new ActivityProspectAdded(
activity: $activity,
eventSource: 'match-crm-data'
));
if ($activity->getProspectName() !== null) {
$activity->setTitleFromCallData($this->call);
/** @var Participant $prospectParticipant */
$prospectParticipant = $activity
->participants()
->where(function (Builder $query) use ($activity) {
$query
->whereNull('user_id')
->orWhere('user_id', '!=', $activity->getUserId())
;
})
->first()
;
$activity->updateParticipantCrmData($crmObjects, $prospectParticipant);
}
$this->logMessage('Activity updated');
$this->triggerSummaryPushIfReady($activity, $eventDispatcher);
}
private function triggerSummaryPushIfReady(Activity $activity, Dispatcher $eventDispatcher): void
{
if (! $activity->hasTranscriptionId()) {
return;
}
if ($activity->hasProspectActivitySummaryLog()) {
$this->logMessage('Summary already sent to prospect, skipping summary push after CRM matching');
return;
}
$this->logMessage('Triggering summary push after CRM matching');
$eventDispatcher->dispatch(new TranscriptionAiSummaryReadyEvent($activity->getUuid()));
}
private function initialiseCrmService(Activity $activity, ProviderRegistry $providerRegistry): void
{
$this->team = $activity->getUser()->getTeam();
$crmProviderName = $this->team->getCrmConfiguration()->getProviderName();
$crmService = $providerRegistry->get($crmProviderName);
$crmService->setUser($this->team->getOwner());
$this->crmService = $crmService;
$this->logContext['team'] = $this->team->getSlug();
$this->logContext['team_id'] = $this->team->getId();
}
private function logMessage(string $message): void
{
$this->logger->info(sprintf('[MatchCrmData] %s', $message), $this->logContext);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – MatchCrmData.php
|
NULL
|
6343
|
|
6342
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Jiminny\Component\Utility\Service;
use Illuminate\Cache\RateLimiter;
use Jiminny\Contracts\Http\RateLimited;
use Jiminny\Contracts\Http\RateLimitInterface;
class ProviderRateLimiter
{
protected RateLimiter $rateLimiter;
public function __construct(RateLimiter $rateLimiter)
{
$this->rateLimiter = $rateLimiter;
}
public function canMakeRequest(RateLimited $provider): bool
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$key = $rateLimit->getKey();
if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {
return false;
}
}
return true;
}
public function requestAvailableIn(RateLimited $provider): int
{
return $provider->getRateLimits()->isNotEmpty()
? $provider->getRateLimits()
->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))
->max()
: 0
;
}
public function incrementRequestCount(RateLimited $provider): void
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());
}
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Activity\Import;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Events\Dispatcher;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\Queue\Constants;
use Jiminny\Component\Utility\Service\ProviderRateLimiter;
use Jiminny\Contracts\Services\Crm\ServiceInterface;
use Jiminny\DTO\ImportCall\Call;
use Jiminny\Component\TranscriptionSummary\Events\TranscriptionAiSummaryReadyEvent;
use Jiminny\Events\Activities\AiAutomation\ActivityProspectAdded;
use Jiminny\Exceptions\SocialAccountTokenInvalidException;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Participant;
use Jiminny\Models\Team;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmObjectsResolver;
use Jiminny\Services\Crm\ProspectSearchStrategyFactory;
use Jiminny\Services\Crm\ProviderRegistry;
use Psr\Log\LoggerInterface;
class MatchCrmData implements ShouldQueue
{
use Dispatchable;
use InteractsWithQueue;
use Queueable;
// AWS visibility timeout allows a maximum of 12 hours. This is 1 minute less.
private const int MAX_DELAY = 43140;
// Infrastructure allows max 3 retries (1 initial execution + 3 retries)
public int $tries = 4;
private Call $call;
private int $activityId;
private Team $team;
private ServiceInterface $crmService;
private LoggerInterface $logger;
private array $logContext;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(Call $call, int $activityId)
{
$this->call = $call;
$this->activityId = $activityId;
$this->logContext = [
'activity_id' => $activityId,
'call_id' => $call->getCallId(),
'provider' => $call->getProvider(),
];
$this->onQueue(Constants::QUEUE_DIALERS);
}
public function handle(
CrmObjectsResolver $crmObjectsResolver,
ProviderRegistry $providerRegistry,
ProviderRateLimiter $rateLimiter,
ActivityRepository $activityRepository,
LoggerInterface $logger,
Dispatcher $eventDispatcher,
): void {
$this->logger = $logger;
// Activity is already augmented with CRM data, no need to perform the same operation
if ($this->call->isActivityUpdatedWithCrm()) {
$this->logMessage('Skipping activity. Already updated with CRM data');
return;
}
/** @var Activity $activity */
$activity = $activityRepository->findById($this->activityId);
try {
$this->initialiseCrmService($activity, $providerRegistry);
} catch (SocialAccountTokenInvalidException $exception) {
$this->logMessage('Invalid token, retrying');
$this->release(self::MAX_DELAY); // Try again tomorrow
return;
}
$prospectSearchStrategy = ProspectSearchStrategyFactory::match($this->team);
if ($prospectSearchStrategy->ignoreCrmMatchData()) {
// Ignore any associated opportunity
$this->logger->info('[MatchCrmData] Ignoring crm data because of prospect strategy', [
'activity_id' => $this->activityId,
'strategy' => get_class($prospectSearchStrategy),
]);
return;
}
if (! $rateLimiter->canMakeRequest($activity->getCrm())) {
$this->logMessage('Rate limit reached, retrying');
$this->release($rateLimiter->requestAvailableIn($activity->getCrm()) + random_int(1, 60));
return;
}
$this->logMessage('Resolving CRM objects');
$rateLimiter->incrementRequestCount($activity->getCrm());
$crmObjects = $crmObjectsResolver->resolveFromCall($this->crmService, $this->call);
if (empty($crmObjects)) {
$this->logMessage('Could not resolve CRM objects, retrying');
$this->release(3600);
return;
}
[$lead, $account, $opportunity, $contact, $stage] = $crmObjects;
$activity->update([
'lead_id' => $lead->id ?? null,
'contact_id' => $contact->id ?? null,
'account_id' => $account->id ?? null,
'opportunity_id' => $opportunity->id ?? null,
'stage_id' => $stage->id ?? null,
]);
$activity->refresh();
$eventDispatcher->dispatch(new ActivityProspectAdded(
activity: $activity,
eventSource: 'match-crm-data'
));
if ($activity->getProspectName() !== null) {
$activity->setTitleFromCallData($this->call);
/** @var Participant $prospectParticipant */
$prospectParticipant = $activity
->participants()
->where(function (Builder $query) use ($activity) {
$query
->whereNull('user_id')
->orWhere('user_id', '!=', $activity->getUserId())
;
})
->first()
;
$activity->updateParticipantCrmData($crmObjects, $prospectParticipant);
}
$this->logMessage('Activity updated');
$this->triggerSummaryPushIfReady($activity, $eventDispatcher);
}
private function triggerSummaryPushIfReady(Activity $activity, Dispatcher $eventDispatcher): void
{
if (! $activity->hasTranscriptionId()) {
return;
}
if ($activity->hasProspectActivitySummaryLog()) {
$this->logMessage('Summary already sent to prospect, skipping summary push after CRM matching');
return;
}
$this->logMessage('Triggering summary push after CRM matching');
$eventDispatcher->dispatch(new TranscriptionAiSummaryReadyEvent($activity->getUuid()));
}
private function initialiseCrmService(Activity $activity, ProviderRegistry $providerRegistry): void
{
$this->team = $activity->getUser()->getTeam();
$crmProviderName = $this->team->getCrmConfiguration()->getProviderName();
$crmService = $providerRegistry->get($crmProviderName);
$crmService->setUser($this->team->getOwner());
$this->crmService = $crmService;
$this->logContext['team'] = $this->team->getSlug();
$this->logContext['team_id'] = $this->team->getId();
}
private function logMessage(string $message): void
{
$this->logger->info(sprintf('[MatchCrmData] %s', $message), $this->logContext);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – MatchCrmData.php
|
NULL
|
6342
|
|
6341
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Jiminny\Component\Utility\Service;
use Illuminate\Cache\RateLimiter;
use Jiminny\Contracts\Http\RateLimited;
use Jiminny\Contracts\Http\RateLimitInterface;
class ProviderRateLimiter
{
protected RateLimiter $rateLimiter;
public function __construct(RateLimiter $rateLimiter)
{
$this->rateLimiter = $rateLimiter;
}
public function canMakeRequest(RateLimited $provider): bool
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$key = $rateLimit->getKey();
if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {
return false;
}
}
return true;
}
public function requestAvailableIn(RateLimited $provider): int
{
return $provider->getRateLimits()->isNotEmpty()
? $provider->getRateLimits()
->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))
->max()
: 0
;
}
public function incrementRequestCount(RateLimited $provider): void
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());
}
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Activity\Import;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Events\Dispatcher;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\Queue\Constants;
use Jiminny\Component\Utility\Service\ProviderRateLimiter;
use Jiminny\Contracts\Services\Crm\ServiceInterface;
use Jiminny\DTO\ImportCall\Call;
use Jiminny\Component\TranscriptionSummary\Events\TranscriptionAiSummaryReadyEvent;
use Jiminny\Events\Activities\AiAutomation\ActivityProspectAdded;
use Jiminny\Exceptions\SocialAccountTokenInvalidException;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Participant;
use Jiminny\Models\Team;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmObjectsResolver;
use Jiminny\Services\Crm\ProspectSearchStrategyFactory;
use Jiminny\Services\Crm\ProviderRegistry;
use Psr\Log\LoggerInterface;
class MatchCrmData implements ShouldQueue
{
use Dispatchable;
use InteractsWithQueue;
use Queueable;
// AWS visibility timeout allows a maximum of 12 hours. This is 1 minute less.
private const int MAX_DELAY = 43140;
// Infrastructure allows max 3 retries (1 initial execution + 3 retries)
public int $tries = 4;
private Call $call;
private int $activityId;
private Team $team;
private ServiceInterface $crmService;
private LoggerInterface $logger;
private array $logContext;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(Call $call, int $activityId)
{
$this->call = $call;
$this->activityId = $activityId;
$this->logContext = [
'activity_id' => $activityId,
'call_id' => $call->getCallId(),
'provider' => $call->getProvider(),
];
$this->onQueue(Constants::QUEUE_DIALERS);
}
public function handle(
CrmObjectsResolver $crmObjectsResolver,
ProviderRegistry $providerRegistry,
ProviderRateLimiter $rateLimiter,
ActivityRepository $activityRepository,
LoggerInterface $logger,
Dispatcher $eventDispatcher,
): void {
$this->logger = $logger;
// Activity is already augmented with CRM data, no need to perform the same operation
if ($this->call->isActivityUpdatedWithCrm()) {
$this->logMessage('Skipping activity. Already updated with CRM data');
return;
}
/** @var Activity $activity */
$activity = $activityRepository->findById($this->activityId);
try {
$this->initialiseCrmService($activity, $providerRegistry);
} catch (SocialAccountTokenInvalidException $exception) {
$this->logMessage('Invalid token, retrying');
$this->release(self::MAX_DELAY); // Try again tomorrow
return;
}
$prospectSearchStrategy = ProspectSearchStrategyFactory::match($this->team);
if ($prospectSearchStrategy->ignoreCrmMatchData()) {
// Ignore any associated opportunity
$this->logger->info('[MatchCrmData] Ignoring crm data because of prospect strategy', [
'activity_id' => $this->activityId,
'strategy' => get_class($prospectSearchStrategy),
]);
return;
}
if (! $rateLimiter->canMakeRequest($activity->getCrm())) {
$this->logMessage('Rate limit reached, retrying');
$this->release($rateLimiter->requestAvailableIn($activity->getCrm()) + random_int(1, 60));
return;
}
$this->logMessage('Resolving CRM objects');
$rateLimiter->incrementRequestCount($activity->getCrm());
$crmObjects = $crmObjectsResolver->resolveFromCall($this->crmService, $this->call);
if (empty($crmObjects)) {
$this->logMessage('Could not resolve CRM objects, retrying');
$this->release(3600);
return;
}
[$lead, $account, $opportunity, $contact, $stage] = $crmObjects;
$activity->update([
'lead_id' => $lead->id ?? null,
'contact_id' => $contact->id ?? null,
'account_id' => $account->id ?? null,
'opportunity_id' => $opportunity->id ?? null,
'stage_id' => $stage->id ?? null,
]);
$activity->refresh();
$eventDispatcher->dispatch(new ActivityProspectAdded(
activity: $activity,
eventSource: 'match-crm-data'
));
if ($activity->getProspectName() !== null) {
$activity->setTitleFromCallData($this->call);
/** @var Participant $prospectParticipant */
$prospectParticipant = $activity
->participants()
->where(function (Builder $query) use ($activity) {
$query
->whereNull('user_id')
->orWhere('user_id', '!=', $activity->getUserId())
;
})
->first()
;
$activity->updateParticipantCrmData($crmObjects, $prospectParticipant);
}
$this->logMessage('Activity updated');
$this->triggerSummaryPushIfReady($activity, $eventDispatcher);
}
private function triggerSummaryPushIfReady(Activity $activity, Dispatcher $eventDispatcher): void
{
if (! $activity->hasTranscriptionId()) {
return;
}
if ($activity->hasProspectActivitySummaryLog()) {
$this->logMessage('Summary already sent to prospect, skipping summary push after CRM matching');
return;
}
$this->logMessage('Triggering summary push after CRM matching');
$eventDispatcher->dispatch(new TranscriptionAiSummaryReadyEvent($activity->getUuid()));
}
private function initialiseCrmService(Activity $activity, ProviderRegistry $providerRegistry): void
{
$this->team = $activity->getUser()->getTeam();
$crmProviderName = $this->team->getCrmConfiguration()->getProviderName();
$crmService = $providerRegistry->get($crmProviderName);
$crmService->setUser($this->team->getOwner());
$this->crmService = $crmService;
$this->logContext['team'] = $this->team->getSlug();
$this->logContext['team_id'] = $this->team->getId();
}
private function logMessage(string $message): void
{
$this->logger->info(sprintf('[MatchCrmData] %s', $message), $this->logContext);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – MatchCrmData.php
|
NULL
|
6341
|
|
6340
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Jiminny\Component\Utility\Service;
use Illuminate\Cache\RateLimiter;
use Jiminny\Contracts\Http\RateLimited;
use Jiminny\Contracts\Http\RateLimitInterface;
class ProviderRateLimiter
{
protected RateLimiter $rateLimiter;
public function __construct(RateLimiter $rateLimiter)
{
$this->rateLimiter = $rateLimiter;
}
public function canMakeRequest(RateLimited $provider): bool
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$key = $rateLimit->getKey();
if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {
return false;
}
}
return true;
}
public function requestAvailableIn(RateLimited $provider): int
{
return $provider->getRateLimits()->isNotEmpty()
? $provider->getRateLimits()
->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))
->max()
: 0
;
}
public function incrementRequestCount(RateLimited $provider): void
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());
}
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Activity\Import;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Events\Dispatcher;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\Queue\Constants;
use Jiminny\Component\Utility\Service\ProviderRateLimiter;
use Jiminny\Contracts\Services\Crm\ServiceInterface;
use Jiminny\DTO\ImportCall\Call;
use Jiminny\Component\TranscriptionSummary\Events\TranscriptionAiSummaryReadyEvent;
use Jiminny\Events\Activities\AiAutomation\ActivityProspectAdded;
use Jiminny\Exceptions\SocialAccountTokenInvalidException;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Participant;
use Jiminny\Models\Team;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmObjectsResolver;
use Jiminny\Services\Crm\ProspectSearchStrategyFactory;
use Jiminny\Services\Crm\ProviderRegistry;
use Psr\Log\LoggerInterface;
class MatchCrmData implements ShouldQueue
{
use Dispatchable;
use InteractsWithQueue;
use Queueable;
// AWS visibility timeout allows a maximum of 12 hours. This is 1 minute less.
private const int MAX_DELAY = 43140;
// Infrastructure allows max 3 retries (1 initial execution + 3 retries)
public int $tries = 4;
private Call $call;
private int $activityId;
private Team $team;
private ServiceInterface $crmService;
private LoggerInterface $logger;
private array $logContext;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(Call $call, int $activityId)
{
$this->call = $call;
$this->activityId = $activityId;
$this->logContext = [
'activity_id' => $activityId,
'call_id' => $call->getCallId(),
'provider' => $call->getProvider(),
];
$this->onQueue(Constants::QUEUE_DIALERS);
}
public function handle(
CrmObjectsResolver $crmObjectsResolver,
ProviderRegistry $providerRegistry,
ProviderRateLimiter $rateLimiter,
ActivityRepository $activityRepository,
LoggerInterface $logger,
Dispatcher $eventDispatcher,
): void {
$this->logger = $logger;
// Activity is already augmented with CRM data, no need to perform the same operation
if ($this->call->isActivityUpdatedWithCrm()) {
$this->logMessage('Skipping activity. Already updated with CRM data');
return;
}
/** @var Activity $activity */
$activity = $activityRepository->findById($this->activityId);
try {
$this->initialiseCrmService($activity, $providerRegistry);
} catch (SocialAccountTokenInvalidException $exception) {
$this->logMessage('Invalid token, retrying');
$this->release(self::MAX_DELAY); // Try again tomorrow
return;
}
$prospectSearchStrategy = ProspectSearchStrategyFactory::match($this->team);
if ($prospectSearchStrategy->ignoreCrmMatchData()) {
// Ignore any associated opportunity
$this->logger->info('[MatchCrmData] Ignoring crm data because of prospect strategy', [
'activity_id' => $this->activityId,
'strategy' => get_class($prospectSearchStrategy),
]);
return;
}
if (! $rateLimiter->canMakeRequest($activity->getCrm())) {
$this->logMessage('Rate limit reached, retrying');
$this->release($rateLimiter->requestAvailableIn($activity->getCrm()) + random_int(1, 60));
return;
}
$this->logMessage('Resolving CRM objects');
$rateLimiter->incrementRequestCount($activity->getCrm());
$crmObjects = $crmObjectsResolver->resolveFromCall($this->crmService, $this->call);
if (empty($crmObjects)) {
$this->logMessage('Could not resolve CRM objects, retrying');
$this->release(3600);
return;
}
[$lead, $account, $opportunity, $contact, $stage] = $crmObjects;
$activity->update([
'lead_id' => $lead->id ?? null,
'contact_id' => $contact->id ?? null,
'account_id' => $account->id ?? null,
'opportunity_id' => $opportunity->id ?? null,
'stage_id' => $stage->id ?? null,
]);
$activity->refresh();
$eventDispatcher->dispatch(new ActivityProspectAdded(
activity: $activity,
eventSource: 'match-crm-data'
));
if ($activity->getProspectName() !== null) {
$activity->setTitleFromCallData($this->call);
/** @var Participant $prospectParticipant */
$prospectParticipant = $activity
->participants()
->where(function (Builder $query) use ($activity) {
$query
->whereNull('user_id')
->orWhere('user_id', '!=', $activity->getUserId())
;
})
->first()
;
$activity->updateParticipantCrmData($crmObjects, $prospectParticipant);
}
$this->logMessage('Activity updated');
$this->triggerSummaryPushIfReady($activity, $eventDispatcher);
}
private function triggerSummaryPushIfReady(Activity $activity, Dispatcher $eventDispatcher): void
{
if (! $activity->hasTranscriptionId()) {
return;
}
if ($activity->hasProspectActivitySummaryLog()) {
$this->logMessage('Summary already sent to prospect, skipping summary push after CRM matching');
return;
}
$this->logMessage('Triggering summary push after CRM matching');
$eventDispatcher->dispatch(new TranscriptionAiSummaryReadyEvent($activity->getUuid()));
}
private function initialiseCrmService(Activity $activity, ProviderRegistry $providerRegistry): void
{
$this->team = $activity->getUser()->getTeam();
$crmProviderName = $this->team->getCrmConfiguration()->getProviderName();
$crmService = $providerRegistry->get($crmProviderName);
$crmService->setUser($this->team->getOwner());
$this->crmService = $crmService;
$this->logContext['team'] = $this->team->getSlug();
$this->logContext['team_id'] = $this->team->getId();
}
private function logMessage(string $message): void
{
$this->logger->info(sprintf('[MatchCrmData] %s', $message), $this->logContext);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – MatchCrmData.php
|
NULL
|
6340
|
|
6339
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Jiminny\Component\Utility\Service;
use Illuminate\Cache\RateLimiter;
use Jiminny\Contracts\Http\RateLimited;
use Jiminny\Contracts\Http\RateLimitInterface;
class ProviderRateLimiter
{
protected RateLimiter $rateLimiter;
public function __construct(RateLimiter $rateLimiter)
{
$this->rateLimiter = $rateLimiter;
}
public function canMakeRequest(RateLimited $provider): bool
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$key = $rateLimit->getKey();
if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {
return false;
}
}
return true;
}
public function requestAvailableIn(RateLimited $provider): int
{
return $provider->getRateLimits()->isNotEmpty()
? $provider->getRateLimits()
->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))
->max()
: 0
;
}
public function incrementRequestCount(RateLimited $provider): void
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());
}
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Activity\Import;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Events\Dispatcher;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\Queue\Constants;
use Jiminny\Component\Utility\Service\ProviderRateLimiter;
use Jiminny\Contracts\Services\Crm\ServiceInterface;
use Jiminny\DTO\ImportCall\Call;
use Jiminny\Component\TranscriptionSummary\Events\TranscriptionAiSummaryReadyEvent;
use Jiminny\Events\Activities\AiAutomation\ActivityProspectAdded;
use Jiminny\Exceptions\SocialAccountTokenInvalidException;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Participant;
use Jiminny\Models\Team;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmObjectsResolver;
use Jiminny\Services\Crm\ProspectSearchStrategyFactory;
use Jiminny\Services\Crm\ProviderRegistry;
use Psr\Log\LoggerInterface;
class MatchCrmData implements ShouldQueue
{
use Dispatchable;
use InteractsWithQueue;
use Queueable;
// AWS visibility timeout allows a maximum of 12 hours. This is 1 minute less.
private const int MAX_DELAY = 43140;
// Infrastructure allows max 3 retries (1 initial execution + 3 retries)
public int $tries = 4;
private Call $call;
private int $activityId;
private Team $team;
private ServiceInterface $crmService;
private LoggerInterface $logger;
private array $logContext;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(Call $call, int $activityId)
{
$this->call = $call;
$this->activityId = $activityId;
$this->logContext = [
'activity_id' => $activityId,
'call_id' => $call->getCallId(),
'provider' => $call->getProvider(),
];
$this->onQueue(Constants::QUEUE_DIALERS);
}
public function handle(
CrmObjectsResolver $crmObjectsResolver,
ProviderRegistry $providerRegistry,
ProviderRateLimiter $rateLimiter,
ActivityRepository $activityRepository,
LoggerInterface $logger,
Dispatcher $eventDispatcher,
): void {
$this->logger = $logger;
// Activity is already augmented with CRM data, no need to perform the same operation
if ($this->call->isActivityUpdatedWithCrm()) {
$this->logMessage('Skipping activity. Already updated with CRM data');
return;
}
/** @var Activity $activity */
$activity = $activityRepository->findById($this->activityId);
try {
$this->initialiseCrmService($activity, $providerRegistry);
} catch (SocialAccountTokenInvalidException $exception) {
$this->logMessage('Invalid token, retrying');
$this->release(self::MAX_DELAY); // Try again tomorrow
return;
}
$prospectSearchStrategy = ProspectSearchStrategyFactory::match($this->team);
if ($prospectSearchStrategy->ignoreCrmMatchData()) {
// Ignore any associated opportunity
$this->logger->info('[MatchCrmData] Ignoring crm data because of prospect strategy', [
'activity_id' => $this->activityId,
'strategy' => get_class($prospectSearchStrategy),
]);
return;
}
if (! $rateLimiter->canMakeRequest($activity->getCrm())) {
$this->logMessage('Rate limit reached, retrying');
$this->release($rateLimiter->requestAvailableIn($activity->getCrm()) + random_int(1, 60));
return;
}
$this->logMessage('Resolving CRM objects');
$rateLimiter->incrementRequestCount($activity->getCrm());
$crmObjects = $crmObjectsResolver->resolveFromCall($this->crmService, $this->call);
if (empty($crmObjects)) {
$this->logMessage('Could not resolve CRM objects, retrying');
$this->release(3600);
return;
}
[$lead, $account, $opportunity, $contact, $stage] = $crmObjects;
$activity->update([
'lead_id' => $lead->id ?? null,
'contact_id' => $contact->id ?? null,
'account_id' => $account->id ?? null,
'opportunity_id' => $opportunity->id ?? null,
'stage_id' => $stage->id ?? null,
]);
$activity->refresh();
$eventDispatcher->dispatch(new ActivityProspectAdded(
activity: $activity,
eventSource: 'match-crm-data'
));
if ($activity->getProspectName() !== null) {
$activity->setTitleFromCallData($this->call);
/** @var Participant $prospectParticipant */
$prospectParticipant = $activity
->participants()
->where(function (Builder $query) use ($activity) {
$query
->whereNull('user_id')
->orWhere('user_id', '!=', $activity->getUserId())
;
})
->first()
;
$activity->updateParticipantCrmData($crmObjects, $prospectParticipant);
}
$this->logMessage('Activity updated');
$this->triggerSummaryPushIfReady($activity, $eventDispatcher);
}
private function triggerSummaryPushIfReady(Activity $activity, Dispatcher $eventDispatcher): void
{
if (! $activity->hasTranscriptionId()) {
return;
}
if ($activity->hasProspectActivitySummaryLog()) {
$this->logMessage('Summary already sent to prospect, skipping summary push after CRM matching');
return;
}
$this->logMessage('Triggering summary push after CRM matching');
$eventDispatcher->dispatch(new TranscriptionAiSummaryReadyEvent($activity->getUuid()));
}
private function initialiseCrmService(Activity $activity, ProviderRegistry $providerRegistry): void
{
$this->team = $activity->getUser()->getTeam();
$crmProviderName = $this->team->getCrmConfiguration()->getProviderName();
$crmService = $providerRegistry->get($crmProviderName);
$crmService->setUser($this->team->getOwner());
$this->crmService = $crmService;
$this->logContext['team'] = $this->team->getSlug();
$this->logContext['team_id'] = $this->team->getId();
}
private function logMessage(string $message): void
{
$this->logger->info(sprintf('[MatchCrmData] %s', $message), $this->logContext);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – MatchCrmData.php
|
NULL
|
6339
|
|
6338
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Jiminny\Component\Utility\Service;
use Illuminate\Cache\RateLimiter;
use Jiminny\Contracts\Http\RateLimited;
use Jiminny\Contracts\Http\RateLimitInterface;
class ProviderRateLimiter
{
protected RateLimiter $rateLimiter;
public function __construct(RateLimiter $rateLimiter)
{
$this->rateLimiter = $rateLimiter;
}
public function canMakeRequest(RateLimited $provider): bool
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$key = $rateLimit->getKey();
if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {
return false;
}
}
return true;
}
public function requestAvailableIn(RateLimited $provider): int
{
return $provider->getRateLimits()->isNotEmpty()
? $provider->getRateLimits()
->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))
->max()
: 0
;
}
public function incrementRequestCount(RateLimited $provider): void
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());
}
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Activity\Import;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Events\Dispatcher;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\Queue\Constants;
use Jiminny\Component\Utility\Service\ProviderRateLimiter;
use Jiminny\Contracts\Services\Crm\ServiceInterface;
use Jiminny\DTO\ImportCall\Call;
use Jiminny\Component\TranscriptionSummary\Events\TranscriptionAiSummaryReadyEvent;
use Jiminny\Events\Activities\AiAutomation\ActivityProspectAdded;
use Jiminny\Exceptions\SocialAccountTokenInvalidException;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Participant;
use Jiminny\Models\Team;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmObjectsResolver;
use Jiminny\Services\Crm\ProspectSearchStrategyFactory;
use Jiminny\Services\Crm\ProviderRegistry;
use Psr\Log\LoggerInterface;
class MatchCrmData implements ShouldQueue
{
use Dispatchable;
use InteractsWithQueue;
use Queueable;
// AWS visibility timeout allows a maximum of 12 hours. This is 1 minute less.
private const int MAX_DELAY = 43140;
// Infrastructure allows max 3 retries (1 initial execution + 3 retries)
public int $tries = 4;
private Call $call;
private int $activityId;
private Team $team;
private ServiceInterface $crmService;
private LoggerInterface $logger;
private array $logContext;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(Call $call, int $activityId)
{
$this->call = $call;
$this->activityId = $activityId;
$this->logContext = [
'activity_id' => $activityId,
'call_id' => $call->getCallId(),
'provider' => $call->getProvider(),
];
$this->onQueue(Constants::QUEUE_DIALERS);
}
public function handle(
CrmObjectsResolver $crmObjectsResolver,
ProviderRegistry $providerRegistry,
ProviderRateLimiter $rateLimiter,
ActivityRepository $activityRepository,
LoggerInterface $logger,
Dispatcher $eventDispatcher,
): void {
$this->logger = $logger;
// Activity is already augmented with CRM data, no need to perform the same operation
if ($this->call->isActivityUpdatedWithCrm()) {
$this->logMessage('Skipping activity. Already updated with CRM data');
return;
}
/** @var Activity $activity */
$activity = $activityRepository->findById($this->activityId);
try {
$this->initialiseCrmService($activity, $providerRegistry);
} catch (SocialAccountTokenInvalidException $exception) {
$this->logMessage('Invalid token, retrying');
$this->release(self::MAX_DELAY); // Try again tomorrow
return;
}
$prospectSearchStrategy = ProspectSearchStrategyFactory::match($this->team);
if ($prospectSearchStrategy->ignoreCrmMatchData()) {
// Ignore any associated opportunity
$this->logger->info('[MatchCrmData] Ignoring crm data because of prospect strategy', [
'activity_id' => $this->activityId,
'strategy' => get_class($prospectSearchStrategy),
]);
return;
}
if (! $rateLimiter->canMakeRequest($activity->getCrm())) {
$this->logMessage('Rate limit reached, retrying');
$this->release($rateLimiter->requestAvailableIn($activity->getCrm()) + random_int(1, 60));
return;
}
$this->logMessage('Resolving CRM objects');
$rateLimiter->incrementRequestCount($activity->getCrm());
$crmObjects = $crmObjectsResolver->resolveFromCall($this->crmService, $this->call);
if (empty($crmObjects)) {
$this->logMessage('Could not resolve CRM objects, retrying');
$this->release(3600);
return;
}
[$lead, $account, $opportunity, $contact, $stage] = $crmObjects;
$activity->update([
'lead_id' => $lead->id ?? null,
'contact_id' => $contact->id ?? null,
'account_id' => $account->id ?? null,
'opportunity_id' => $opportunity->id ?? null,
'stage_id' => $stage->id ?? null,
]);
$activity->refresh();
$eventDispatcher->dispatch(new ActivityProspectAdded(
activity: $activity,
eventSource: 'match-crm-data'
));
if ($activity->getProspectName() !== null) {
$activity->setTitleFromCallData($this->call);
/** @var Participant $prospectParticipant */
$prospectParticipant = $activity
->participants()
->where(function (Builder $query) use ($activity) {
$query
->whereNull('user_id')
->orWhere('user_id', '!=', $activity->getUserId())
;
})
->first()
;
$activity->updateParticipantCrmData($crmObjects, $prospectParticipant);
}
$this->logMessage('Activity updated');
$this->triggerSummaryPushIfReady($activity, $eventDispatcher);
}
private function triggerSummaryPushIfReady(Activity $activity, Dispatcher $eventDispatcher): void
{
if (! $activity->hasTranscriptionId()) {
return;
}
if ($activity->hasProspectActivitySummaryLog()) {
$this->logMessage('Summary already sent to prospect, skipping summary push after CRM matching');
return;
}
$this->logMessage('Triggering summary push after CRM matching');
$eventDispatcher->dispatch(new TranscriptionAiSummaryReadyEvent($activity->getUuid()));
}
private function initialiseCrmService(Activity $activity, ProviderRegistry $providerRegistry): void
{
$this->team = $activity->getUser()->getTeam();
$crmProviderName = $this->team->getCrmConfiguration()->getProviderName();
$crmService = $providerRegistry->get($crmProviderName);
$crmService->setUser($this->team->getOwner());
$this->crmService = $crmService;
$this->logContext['team'] = $this->team->getSlug();
$this->logContext['team_id'] = $this->team->getId();
}
private function logMessage(string $message): void
{
$this->logger->info(sprintf('[MatchCrmData] %s', $message), $this->logContext);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – MatchCrmData.php
|
NULL
|
6338
|
|
6337
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Jiminny\Component\Utility\Service;
use Illuminate\Cache\RateLimiter;
use Jiminny\Contracts\Http\RateLimited;
use Jiminny\Contracts\Http\RateLimitInterface;
class ProviderRateLimiter
{
protected RateLimiter $rateLimiter;
public function __construct(RateLimiter $rateLimiter)
{
$this->rateLimiter = $rateLimiter;
}
public function canMakeRequest(RateLimited $provider): bool
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$key = $rateLimit->getKey();
if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {
return false;
}
}
return true;
}
public function requestAvailableIn(RateLimited $provider): int
{
return $provider->getRateLimits()->isNotEmpty()
? $provider->getRateLimits()
->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))
->max()
: 0
;
}
public function incrementRequestCount(RateLimited $provider): void
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());
}
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Activity\Import;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Events\Dispatcher;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\Queue\Constants;
use Jiminny\Component\Utility\Service\ProviderRateLimiter;
use Jiminny\Contracts\Services\Crm\ServiceInterface;
use Jiminny\DTO\ImportCall\Call;
use Jiminny\Component\TranscriptionSummary\Events\TranscriptionAiSummaryReadyEvent;
use Jiminny\Events\Activities\AiAutomation\ActivityProspectAdded;
use Jiminny\Exceptions\SocialAccountTokenInvalidException;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Participant;
use Jiminny\Models\Team;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmObjectsResolver;
use Jiminny\Services\Crm\ProspectSearchStrategyFactory;
use Jiminny\Services\Crm\ProviderRegistry;
use Psr\Log\LoggerInterface;
class MatchCrmData implements ShouldQueue
{
use Dispatchable;
use InteractsWithQueue;
use Queueable;
// AWS visibility timeout allows a maximum of 12 hours. This is 1 minute less.
private const int MAX_DELAY = 43140;
// Infrastructure allows max 3 retries (1 initial execution + 3 retries)
public int $tries = 4;
private Call $call;
private int $activityId;
private Team $team;
private ServiceInterface $crmService;
private LoggerInterface $logger;
private array $logContext;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(Call $call, int $activityId)
{
$this->call = $call;
$this->activityId = $activityId;
$this->logContext = [
'activity_id' => $activityId,
'call_id' => $call->getCallId(),
'provider' => $call->getProvider(),
];
$this->onQueue(Constants::QUEUE_DIALERS);
}
public function handle(
CrmObjectsResolver $crmObjectsResolver,
ProviderRegistry $providerRegistry,
ProviderRateLimiter $rateLimiter,
ActivityRepository $activityRepository,
LoggerInterface $logger,
Dispatcher $eventDispatcher,
): void {
$this->logger = $logger;
// Activity is already augmented with CRM data, no need to perform the same operation
if ($this->call->isActivityUpdatedWithCrm()) {
$this->logMessage('Skipping activity. Already updated with CRM data');
return;
}
/** @var Activity $activity */
$activity = $activityRepository->findById($this->activityId);
try {
$this->initialiseCrmService($activity, $providerRegistry);
} catch (SocialAccountTokenInvalidException $exception) {
$this->logMessage('Invalid token, retrying');
$this->release(self::MAX_DELAY); // Try again tomorrow
return;
}
$prospectSearchStrategy = ProspectSearchStrategyFactory::match($this->team);
if ($prospectSearchStrategy->ignoreCrmMatchData()) {
// Ignore any associated opportunity
$this->logger->info('[MatchCrmData] Ignoring crm data because of prospect strategy', [
'activity_id' => $this->activityId,
'strategy' => get_class($prospectSearchStrategy),
]);
return;
}
if (! $rateLimiter->canMakeRequest($activity->getCrm())) {
$this->logMessage('Rate limit reached, retrying');
$this->release($rateLimiter->requestAvailableIn($activity->getCrm()) + random_int(1, 60));
return;
}
$this->logMessage('Resolving CRM objects');
$rateLimiter->incrementRequestCount($activity->getCrm());
$crmObjects = $crmObjectsResolver->resolveFromCall($this->crmService, $this->call);
if (empty($crmObjects)) {
$this->logMessage('Could not resolve CRM objects, retrying');
$this->release(3600);
return;
}
[$lead, $account, $opportunity, $contact, $stage] = $crmObjects;
$activity->update([
'lead_id' => $lead->id ?? null,
'contact_id' => $contact->id ?? null,
'account_id' => $account->id ?? null,
'opportunity_id' => $opportunity->id ?? null,
'stage_id' => $stage->id ?? null,
]);
$activity->refresh();
$eventDispatcher->dispatch(new ActivityProspectAdded(
activity: $activity,
eventSource: 'match-crm-data'
));
if ($activity->getProspectName() !== null) {
$activity->setTitleFromCallData($this->call);
/** @var Participant $prospectParticipant */
$prospectParticipant = $activity
->participants()
->where(function (Builder $query) use ($activity) {
$query
->whereNull('user_id')
->orWhere('user_id', '!=', $activity->getUserId())
;
})
->first()
;
$activity->updateParticipantCrmData($crmObjects, $prospectParticipant);
}
$this->logMessage('Activity updated');
$this->triggerSummaryPushIfReady($activity, $eventDispatcher);
}
private function triggerSummaryPushIfReady(Activity $activity, Dispatcher $eventDispatcher): void
{
if (! $activity->hasTranscriptionId()) {
return;
}
if ($activity->hasProspectActivitySummaryLog()) {
$this->logMessage('Summary already sent to prospect, skipping summary push after CRM matching');
return;
}
$this->logMessage('Triggering summary push after CRM matching');
$eventDispatcher->dispatch(new TranscriptionAiSummaryReadyEvent($activity->getUuid()));
}
private function initialiseCrmService(Activity $activity, ProviderRegistry $providerRegistry): void
{
$this->team = $activity->getUser()->getTeam();
$crmProviderName = $this->team->getCrmConfiguration()->getProviderName();
$crmService = $providerRegistry->get($crmProviderName);
$crmService->setUser($this->team->getOwner());
$this->crmService = $crmService;
$this->logContext['team'] = $this->team->getSlug();
$this->logContext['team_id'] = $this->team->getId();
}
private function logMessage(string $message): void
{
$this->logger->info(sprintf('[MatchCrmData] %s', $message), $this->logContext);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – MatchCrmData.php
|
NULL
|
6337
|
|
6336
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Jiminny\Component\Utility\Service;
use Illuminate\Cache\RateLimiter;
use Jiminny\Contracts\Http\RateLimited;
use Jiminny\Contracts\Http\RateLimitInterface;
class ProviderRateLimiter
{
protected RateLimiter $rateLimiter;
public function __construct(RateLimiter $rateLimiter)
{
$this->rateLimiter = $rateLimiter;
}
public function canMakeRequest(RateLimited $provider): bool
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$key = $rateLimit->getKey();
if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {
return false;
}
}
return true;
}
public function requestAvailableIn(RateLimited $provider): int
{
return $provider->getRateLimits()->isNotEmpty()
? $provider->getRateLimits()
->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))
->max()
: 0
;
}
public function incrementRequestCount(RateLimited $provider): void
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());
}
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Activity\Import;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Events\Dispatcher;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\Queue\Constants;
use Jiminny\Component\Utility\Service\ProviderRateLimiter;
use Jiminny\Contracts\Services\Crm\ServiceInterface;
use Jiminny\DTO\ImportCall\Call;
use Jiminny\Component\TranscriptionSummary\Events\TranscriptionAiSummaryReadyEvent;
use Jiminny\Events\Activities\AiAutomation\ActivityProspectAdded;
use Jiminny\Exceptions\SocialAccountTokenInvalidException;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Participant;
use Jiminny\Models\Team;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmObjectsResolver;
use Jiminny\Services\Crm\ProspectSearchStrategyFactory;
use Jiminny\Services\Crm\ProviderRegistry;
use Psr\Log\LoggerInterface;
class MatchCrmData implements ShouldQueue
{
use Dispatchable;
use InteractsWithQueue;
use Queueable;
// AWS visibility timeout allows a maximum of 12 hours. This is 1 minute less.
private const int MAX_DELAY = 43140;
// Infrastructure allows max 3 retries (1 initial execution + 3 retries)
public int $tries = 4;
private Call $call;
private int $activityId;
private Team $team;
private ServiceInterface $crmService;
private LoggerInterface $logger;
private array $logContext;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(Call $call, int $activityId)
{
$this->call = $call;
$this->activityId = $activityId;
$this->logContext = [
'activity_id' => $activityId,
'call_id' => $call->getCallId(),
'provider' => $call->getProvider(),
];
$this->onQueue(Constants::QUEUE_DIALERS);
}
public function handle(
CrmObjectsResolver $crmObjectsResolver,
ProviderRegistry $providerRegistry,
ProviderRateLimiter $rateLimiter,
ActivityRepository $activityRepository,
LoggerInterface $logger,
Dispatcher $eventDispatcher,
): void {
$this->logger = $logger;
// Activity is already augmented with CRM data, no need to perform the same operation
if ($this->call->isActivityUpdatedWithCrm()) {
$this->logMessage('Skipping activity. Already updated with CRM data');
return;
}
/** @var Activity $activity */
$activity = $activityRepository->findById($this->activityId);
try {
$this->initialiseCrmService($activity, $providerRegistry);
} catch (SocialAccountTokenInvalidException $exception) {
$this->logMessage('Invalid token, retrying');
$this->release(self::MAX_DELAY); // Try again tomorrow
return;
}
$prospectSearchStrategy = ProspectSearchStrategyFactory::match($this->team);
if ($prospectSearchStrategy->ignoreCrmMatchData()) {
// Ignore any associated opportunity
$this->logger->info('[MatchCrmData] Ignoring crm data because of prospect strategy', [
'activity_id' => $this->activityId,
'strategy' => get_class($prospectSearchStrategy),
]);
return;
}
if (! $rateLimiter->canMakeRequest($activity->getCrm())) {
$this->logMessage('Rate limit reached, retrying');
$this->release($rateLimiter->requestAvailableIn($activity->getCrm()) + random_int(1, 60));
return;
}
$this->logMessage('Resolving CRM objects');
$rateLimiter->incrementRequestCount($activity->getCrm());
$crmObjects = $crmObjectsResolver->resolveFromCall($this->crmService, $this->call);
if (empty($crmObjects)) {
$this->logMessage('Could not resolve CRM objects, retrying');
$this->release(3600);
return;
}
[$lead, $account, $opportunity, $contact, $stage] = $crmObjects;
$activity->update([
'lead_id' => $lead->id ?? null,
'contact_id' => $contact->id ?? null,
'account_id' => $account->id ?? null,
'opportunity_id' => $opportunity->id ?? null,
'stage_id' => $stage->id ?? null,
]);
$activity->refresh();
$eventDispatcher->dispatch(new ActivityProspectAdded(
activity: $activity,
eventSource: 'match-crm-data'
));
if ($activity->getProspectName() !== null) {
$activity->setTitleFromCallData($this->call);
/** @var Participant $prospectParticipant */
$prospectParticipant = $activity
->participants()
->where(function (Builder $query) use ($activity) {
$query
->whereNull('user_id')
->orWhere('user_id', '!=', $activity->getUserId())
;
})
->first()
;
$activity->updateParticipantCrmData($crmObjects, $prospectParticipant);
}
$this->logMessage('Activity updated');
$this->triggerSummaryPushIfReady($activity, $eventDispatcher);
}
private function triggerSummaryPushIfReady(Activity $activity, Dispatcher $eventDispatcher): void
{
if (! $activity->hasTranscriptionId()) {
return;
}
if ($activity->hasProspectActivitySummaryLog()) {
$this->logMessage('Summary already sent to prospect, skipping summary push after CRM matching');
return;
}
$this->logMessage('Triggering summary push after CRM matching');
$eventDispatcher->dispatch(new TranscriptionAiSummaryReadyEvent($activity->getUuid()));
}
private function initialiseCrmService(Activity $activity, ProviderRegistry $providerRegistry): void
{
$this->team = $activity->getUser()->getTeam();
$crmProviderName = $this->team->getCrmConfiguration()->getProviderName();
$crmService = $providerRegistry->get($crmProviderName);
$crmService->setUser($this->team->getOwner());
$this->crmService = $crmService;
$this->logContext['team'] = $this->team->getSlug();
$this->logContext['team_id'] = $this->team->getId();
}
private function logMessage(string $message): void
{
$this->logger->info(sprintf('[MatchCrmData] %s', $message), $this->logContext);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – MatchCrmData.php
|
NULL
|
6336
|
|
6335
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Jiminny\Component\Utility\Service;
use Illuminate\Cache\RateLimiter;
use Jiminny\Contracts\Http\RateLimited;
use Jiminny\Contracts\Http\RateLimitInterface;
class ProviderRateLimiter
{
protected RateLimiter $rateLimiter;
public function __construct(RateLimiter $rateLimiter)
{
$this->rateLimiter = $rateLimiter;
}
public function canMakeRequest(RateLimited $provider): bool
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$key = $rateLimit->getKey();
if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {
return false;
}
}
return true;
}
public function requestAvailableIn(RateLimited $provider): int
{
return $provider->getRateLimits()->isNotEmpty()
? $provider->getRateLimits()
->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))
->max()
: 0
;
}
public function incrementRequestCount(RateLimited $provider): void
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());
}
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Activity\Import;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Events\Dispatcher;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\Queue\Constants;
use Jiminny\Component\Utility\Service\ProviderRateLimiter;
use Jiminny\Contracts\Services\Crm\ServiceInterface;
use Jiminny\DTO\ImportCall\Call;
use Jiminny\Component\TranscriptionSummary\Events\TranscriptionAiSummaryReadyEvent;
use Jiminny\Events\Activities\AiAutomation\ActivityProspectAdded;
use Jiminny\Exceptions\SocialAccountTokenInvalidException;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Participant;
use Jiminny\Models\Team;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmObjectsResolver;
use Jiminny\Services\Crm\ProspectSearchStrategyFactory;
use Jiminny\Services\Crm\ProviderRegistry;
use Psr\Log\LoggerInterface;
class MatchCrmData implements ShouldQueue
{
use Dispatchable;
use InteractsWithQueue;
use Queueable;
// AWS visibility timeout allows a maximum of 12 hours. This is 1 minute less.
private const int MAX_DELAY = 43140;
// Infrastructure allows max 3 retries (1 initial execution + 3 retries)
public int $tries = 4;
private Call $call;
private int $activityId;
private Team $team;
private ServiceInterface $crmService;
private LoggerInterface $logger;
private array $logContext;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(Call $call, int $activityId)
{
$this->call = $call;
$this->activityId = $activityId;
$this->logContext = [
'activity_id' => $activityId,
'call_id' => $call->getCallId(),
'provider' => $call->getProvider(),
];
$this->onQueue(Constants::QUEUE_DIALERS);
}
public function handle(
CrmObjectsResolver $crmObjectsResolver,
ProviderRegistry $providerRegistry,
ProviderRateLimiter $rateLimiter,
ActivityRepository $activityRepository,
LoggerInterface $logger,
Dispatcher $eventDispatcher,
): void {
$this->logger = $logger;
// Activity is already augmented with CRM data, no need to perform the same operation
if ($this->call->isActivityUpdatedWithCrm()) {
$this->logMessage('Skipping activity. Already updated with CRM data');
return;
}
/** @var Activity $activity */
$activity = $activityRepository->findById($this->activityId);
try {
$this->initialiseCrmService($activity, $providerRegistry);
} catch (SocialAccountTokenInvalidException $exception) {
$this->logMessage('Invalid token, retrying');
$this->release(self::MAX_DELAY); // Try again tomorrow
return;
}
$prospectSearchStrategy = ProspectSearchStrategyFactory::match($this->team);
if ($prospectSearchStrategy->ignoreCrmMatchData()) {
// Ignore any associated opportunity
$this->logger->info('[MatchCrmData] Ignoring crm data because of prospect strategy', [
'activity_id' => $this->activityId,
'strategy' => get_class($prospectSearchStrategy),
]);
return;
}
if (! $rateLimiter->canMakeRequest($activity->getCrm())) {
$this->logMessage('Rate limit reached, retrying');
$this->release($rateLimiter->requestAvailableIn($activity->getCrm()) + random_int(1, 60));
return;
}
$this->logMessage('Resolving CRM objects');
$rateLimiter->incrementRequestCount($activity->getCrm());
$crmObjects = $crmObjectsResolver->resolveFromCall($this->crmService, $this->call);
if (empty($crmObjects)) {
$this->logMessage('Could not resolve CRM objects, retrying');
$this->release(3600);
return;
}
[$lead, $account, $opportunity, $contact, $stage] = $crmObjects;
$activity->update([
'lead_id' => $lead->id ?? null,
'contact_id' => $contact->id ?? null,
'account_id' => $account->id ?? null,
'opportunity_id' => $opportunity->id ?? null,
'stage_id' => $stage->id ?? null,
]);
$activity->refresh();
$eventDispatcher->dispatch(new ActivityProspectAdded(
activity: $activity,
eventSource: 'match-crm-data'
));
if ($activity->getProspectName() !== null) {
$activity->setTitleFromCallData($this->call);
/** @var Participant $prospectParticipant */
$prospectParticipant = $activity
->participants()
->where(function (Builder $query) use ($activity) {
$query
->whereNull('user_id')
->orWhere('user_id', '!=', $activity->getUserId())
;
})
->first()
;
$activity->updateParticipantCrmData($crmObjects, $prospectParticipant);
}
$this->logMessage('Activity updated');
$this->triggerSummaryPushIfReady($activity, $eventDispatcher);
}
private function triggerSummaryPushIfReady(Activity $activity, Dispatcher $eventDispatcher): void
{
if (! $activity->hasTranscriptionId()) {
return;
}
if ($activity->hasProspectActivitySummaryLog()) {
$this->logMessage('Summary already sent to prospect, skipping summary push after CRM matching');
return;
}
$this->logMessage('Triggering summary push after CRM matching');
$eventDispatcher->dispatch(new TranscriptionAiSummaryReadyEvent($activity->getUuid()));
}
private function initialiseCrmService(Activity $activity, ProviderRegistry $providerRegistry): void
{
$this->team = $activity->getUser()->getTeam();
$crmProviderName = $this->team->getCrmConfiguration()->getProviderName();
$crmService = $providerRegistry->get($crmProviderName);
$crmService->setUser($this->team->getOwner());
$this->crmService = $crmService;
$this->logContext['team'] = $this->team->getSlug();
$this->logContext['team_id'] = $this->team->getId();
}
private function logMessage(string $message): void
{
$this->logger->info(sprintf('[MatchCrmData] %s', $message), $this->logContext);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – MatchCrmData.php
|
NULL
|
6335
|
|
6334
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Jiminny\Component\Utility\Service;
use Illuminate\Cache\RateLimiter;
use Jiminny\Contracts\Http\RateLimited;
use Jiminny\Contracts\Http\RateLimitInterface;
class ProviderRateLimiter
{
protected RateLimiter $rateLimiter;
public function __construct(RateLimiter $rateLimiter)
{
$this->rateLimiter = $rateLimiter;
}
public function canMakeRequest(RateLimited $provider): bool
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$key = $rateLimit->getKey();
if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {
return false;
}
}
return true;
}
public function requestAvailableIn(RateLimited $provider): int
{
return $provider->getRateLimits()->isNotEmpty()
? $provider->getRateLimits()
->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))
->max()
: 0
;
}
public function incrementRequestCount(RateLimited $provider): void
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());
}
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Activity\Import;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Events\Dispatcher;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\Queue\Constants;
use Jiminny\Component\Utility\Service\ProviderRateLimiter;
use Jiminny\Contracts\Services\Crm\ServiceInterface;
use Jiminny\DTO\ImportCall\Call;
use Jiminny\Component\TranscriptionSummary\Events\TranscriptionAiSummaryReadyEvent;
use Jiminny\Events\Activities\AiAutomation\ActivityProspectAdded;
use Jiminny\Exceptions\SocialAccountTokenInvalidException;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Participant;
use Jiminny\Models\Team;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmObjectsResolver;
use Jiminny\Services\Crm\ProspectSearchStrategyFactory;
use Jiminny\Services\Crm\ProviderRegistry;
use Psr\Log\LoggerInterface;
class MatchCrmData implements ShouldQueue
{
use Dispatchable;
use InteractsWithQueue;
use Queueable;
// AWS visibility timeout allows a maximum of 12 hours. This is 1 minute less.
private const int MAX_DELAY = 43140;
// Infrastructure allows max 3 retries (1 initial execution + 3 retries)
public int $tries = 4;
private Call $call;
private int $activityId;
private Team $team;
private ServiceInterface $crmService;
private LoggerInterface $logger;
private array $logContext;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(Call $call, int $activityId)
{
$this->call = $call;
$this->activityId = $activityId;
$this->logContext = [
'activity_id' => $activityId,
'call_id' => $call->getCallId(),
'provider' => $call->getProvider(),
];
$this->onQueue(Constants::QUEUE_DIALERS);
}
public function handle(
CrmObjectsResolver $crmObjectsResolver,
ProviderRegistry $providerRegistry,
ProviderRateLimiter $rateLimiter,
ActivityRepository $activityRepository,
LoggerInterface $logger,
Dispatcher $eventDispatcher,
): void {
$this->logger = $logger;
// Activity is already augmented with CRM data, no need to perform the same operation
if ($this->call->isActivityUpdatedWithCrm()) {
$this->logMessage('Skipping activity. Already updated with CRM data');
return;
}
/** @var Activity $activity */
$activity = $activityRepository->findById($this->activityId);
try {
$this->initialiseCrmService($activity, $providerRegistry);
} catch (SocialAccountTokenInvalidException $exception) {
$this->logMessage('Invalid token, retrying');
$this->release(self::MAX_DELAY); // Try again tomorrow
return;
}
$prospectSearchStrategy = ProspectSearchStrategyFactory::match($this->team);
if ($prospectSearchStrategy->ignoreCrmMatchData()) {
// Ignore any associated opportunity
$this->logger->info('[MatchCrmData] Ignoring crm data because of prospect strategy', [
'activity_id' => $this->activityId,
'strategy' => get_class($prospectSearchStrategy),
]);
return;
}
if (! $rateLimiter->canMakeRequest($activity->getCrm())) {
$this->logMessage('Rate limit reached, retrying');
$this->release($rateLimiter->requestAvailableIn($activity->getCrm()) + random_int(1, 60));
return;
}
$this->logMessage('Resolving CRM objects');
$rateLimiter->incrementRequestCount($activity->getCrm());
$crmObjects = $crmObjectsResolver->resolveFromCall($this->crmService, $this->call);
if (empty($crmObjects)) {
$this->logMessage('Could not resolve CRM objects, retrying');
$this->release(3600);
return;
}
[$lead, $account, $opportunity, $contact, $stage] = $crmObjects;
$activity->update([
'lead_id' => $lead->id ?? null,
'contact_id' => $contact->id ?? null,
'account_id' => $account->id ?? null,
'opportunity_id' => $opportunity->id ?? null,
'stage_id' => $stage->id ?? null,
]);
$activity->refresh();
$eventDispatcher->dispatch(new ActivityProspectAdded(
activity: $activity,
eventSource: 'match-crm-data'
));
if ($activity->getProspectName() !== null) {
$activity->setTitleFromCallData($this->call);
/** @var Participant $prospectParticipant */
$prospectParticipant = $activity
->participants()
->where(function (Builder $query) use ($activity) {
$query
->whereNull('user_id')
->orWhere('user_id', '!=', $activity->getUserId())
;
})
->first()
;
$activity->updateParticipantCrmData($crmObjects, $prospectParticipant);
}
$this->logMessage('Activity updated');
$this->triggerSummaryPushIfReady($activity, $eventDispatcher);
}
private function triggerSummaryPushIfReady(Activity $activity, Dispatcher $eventDispatcher): void
{
if (! $activity->hasTranscriptionId()) {
return;
}
if ($activity->hasProspectActivitySummaryLog()) {
$this->logMessage('Summary already sent to prospect, skipping summary push after CRM matching');
return;
}
$this->logMessage('Triggering summary push after CRM matching');
$eventDispatcher->dispatch(new TranscriptionAiSummaryReadyEvent($activity->getUuid()));
}
private function initialiseCrmService(Activity $activity, ProviderRegistry $providerRegistry): void
{
$this->team = $activity->getUser()->getTeam();
$crmProviderName = $this->team->getCrmConfiguration()->getProviderName();
$crmService = $providerRegistry->get($crmProviderName);
$crmService->setUser($this->team->getOwner());
$this->crmService = $crmService;
$this->logContext['team'] = $this->team->getSlug();
$this->logContext['team_id'] = $this->team->getId();
}
private function logMessage(string $message): void
{
$this->logger->info(sprintf('[MatchCrmData] %s', $message), $this->logContext);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – MatchCrmData.php
|
NULL
|
6334
|
|
6333
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Jiminny\Component\Utility\Service;
use Illuminate\Cache\RateLimiter;
use Jiminny\Contracts\Http\RateLimited;
use Jiminny\Contracts\Http\RateLimitInterface;
class ProviderRateLimiter
{
protected RateLimiter $rateLimiter;
public function __construct(RateLimiter $rateLimiter)
{
$this->rateLimiter = $rateLimiter;
}
public function canMakeRequest(RateLimited $provider): bool
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$key = $rateLimit->getKey();
if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {
return false;
}
}
return true;
}
public function requestAvailableIn(RateLimited $provider): int
{
return $provider->getRateLimits()->isNotEmpty()
? $provider->getRateLimits()
->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))
->max()
: 0
;
}
public function incrementRequestCount(RateLimited $provider): void
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());
}
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Activity\Import;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Events\Dispatcher;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\Queue\Constants;
use Jiminny\Component\Utility\Service\ProviderRateLimiter;
use Jiminny\Contracts\Services\Crm\ServiceInterface;
use Jiminny\DTO\ImportCall\Call;
use Jiminny\Component\TranscriptionSummary\Events\TranscriptionAiSummaryReadyEvent;
use Jiminny\Events\Activities\AiAutomation\ActivityProspectAdded;
use Jiminny\Exceptions\SocialAccountTokenInvalidException;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Participant;
use Jiminny\Models\Team;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmObjectsResolver;
use Jiminny\Services\Crm\ProspectSearchStrategyFactory;
use Jiminny\Services\Crm\ProviderRegistry;
use Psr\Log\LoggerInterface;
class MatchCrmData implements ShouldQueue
{
use Dispatchable;
use InteractsWithQueue;
use Queueable;
// AWS visibility timeout allows a maximum of 12 hours. This is 1 minute less.
private const int MAX_DELAY = 43140;
// Infrastructure allows max 3 retries (1 initial execution + 3 retries)
public int $tries = 4;
private Call $call;
private int $activityId;
private Team $team;
private ServiceInterface $crmService;
private LoggerInterface $logger;
private array $logContext;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(Call $call, int $activityId)
{
$this->call = $call;
$this->activityId = $activityId;
$this->logContext = [
'activity_id' => $activityId,
'call_id' => $call->getCallId(),
'provider' => $call->getProvider(),
];
$this->onQueue(Constants::QUEUE_DIALERS);
}
public function handle(
CrmObjectsResolver $crmObjectsResolver,
ProviderRegistry $providerRegistry,
ProviderRateLimiter $rateLimiter,
ActivityRepository $activityRepository,
LoggerInterface $logger,
Dispatcher $eventDispatcher,
): void {
$this->logger = $logger;
// Activity is already augmented with CRM data, no need to perform the same operation
if ($this->call->isActivityUpdatedWithCrm()) {
$this->logMessage('Skipping activity. Already updated with CRM data');
return;
}
/** @var Activity $activity */
$activity = $activityRepository->findById($this->activityId);
try {
$this->initialiseCrmService($activity, $providerRegistry);
} catch (SocialAccountTokenInvalidException $exception) {
$this->logMessage('Invalid token, retrying');
$this->release(self::MAX_DELAY); // Try again tomorrow
return;
}
$prospectSearchStrategy = ProspectSearchStrategyFactory::match($this->team);
if ($prospectSearchStrategy->ignoreCrmMatchData()) {
// Ignore any associated opportunity
$this->logger->info('[MatchCrmData] Ignoring crm data because of prospect strategy', [
'activity_id' => $this->activityId,
'strategy' => get_class($prospectSearchStrategy),
]);
return;
}
if (! $rateLimiter->canMakeRequest($activity->getCrm())) {
$this->logMessage('Rate limit reached, retrying');
$this->release($rateLimiter->requestAvailableIn($activity->getCrm()) + random_int(1, 60));
return;
}
$this->logMessage('Resolving CRM objects');
$rateLimiter->incrementRequestCount($activity->getCrm());
$crmObjects = $crmObjectsResolver->resolveFromCall($this->crmService, $this->call);
if (empty($crmObjects)) {
$this->logMessage('Could not resolve CRM objects, retrying');
$this->release(3600);
return;
}
[$lead, $account, $opportunity, $contact, $stage] = $crmObjects;
$activity->update([
'lead_id' => $lead->id ?? null,
'contact_id' => $contact->id ?? null,
'account_id' => $account->id ?? null,
'opportunity_id' => $opportunity->id ?? null,
'stage_id' => $stage->id ?? null,
]);
$activity->refresh();
$eventDispatcher->dispatch(new ActivityProspectAdded(
activity: $activity,
eventSource: 'match-crm-data'
));
if ($activity->getProspectName() !== null) {
$activity->setTitleFromCallData($this->call);
/** @var Participant $prospectParticipant */
$prospectParticipant = $activity
->participants()
->where(function (Builder $query) use ($activity) {
$query
->whereNull('user_id')
->orWhere('user_id', '!=', $activity->getUserId())
;
})
->first()
;
$activity->updateParticipantCrmData($crmObjects, $prospectParticipant);
}
$this->logMessage('Activity updated');
$this->triggerSummaryPushIfReady($activity, $eventDispatcher);
}
private function triggerSummaryPushIfReady(Activity $activity, Dispatcher $eventDispatcher): void
{
if (! $activity->hasTranscriptionId()) {
return;
}
if ($activity->hasProspectActivitySummaryLog()) {
$this->logMessage('Summary already sent to prospect, skipping summary push after CRM matching');
return;
}
$this->logMessage('Triggering summary push after CRM matching');
$eventDispatcher->dispatch(new TranscriptionAiSummaryReadyEvent($activity->getUuid()));
}
private function initialiseCrmService(Activity $activity, ProviderRegistry $providerRegistry): void
{
$this->team = $activity->getUser()->getTeam();
$crmProviderName = $this->team->getCrmConfiguration()->getProviderName();
$crmService = $providerRegistry->get($crmProviderName);
$crmService->setUser($this->team->getOwner());
$this->crmService = $crmService;
$this->logContext['team'] = $this->team->getSlug();
$this->logContext['team_id'] = $this->team->getId();
}
private function logMessage(string $message): void
{
$this->logger->info(sprintf('[MatchCrmData] %s', $message), $this->logContext);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – MatchCrmData.php
|
NULL
|
6333
|
|
6332
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Jiminny\Component\Utility\Service;
use Illuminate\Cache\RateLimiter;
use Jiminny\Contracts\Http\RateLimited;
use Jiminny\Contracts\Http\RateLimitInterface;
class ProviderRateLimiter
{
protected RateLimiter $rateLimiter;
public function __construct(RateLimiter $rateLimiter)
{
$this->rateLimiter = $rateLimiter;
}
public function canMakeRequest(RateLimited $provider): bool
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$key = $rateLimit->getKey();
if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {
return false;
}
}
return true;
}
public function requestAvailableIn(RateLimited $provider): int
{
return $provider->getRateLimits()->isNotEmpty()
? $provider->getRateLimits()
->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))
->max()
: 0
;
}
public function incrementRequestCount(RateLimited $provider): void
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());
}
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Activity\Import;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Events\Dispatcher;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\Queue\Constants;
use Jiminny\Component\Utility\Service\ProviderRateLimiter;
use Jiminny\Contracts\Services\Crm\ServiceInterface;
use Jiminny\DTO\ImportCall\Call;
use Jiminny\Component\TranscriptionSummary\Events\TranscriptionAiSummaryReadyEvent;
use Jiminny\Events\Activities\AiAutomation\ActivityProspectAdded;
use Jiminny\Exceptions\SocialAccountTokenInvalidException;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Participant;
use Jiminny\Models\Team;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmObjectsResolver;
use Jiminny\Services\Crm\ProspectSearchStrategyFactory;
use Jiminny\Services\Crm\ProviderRegistry;
use Psr\Log\LoggerInterface;
class MatchCrmData implements ShouldQueue
{
use Dispatchable;
use InteractsWithQueue;
use Queueable;
// AWS visibility timeout allows a maximum of 12 hours. This is 1 minute less.
private const int MAX_DELAY = 43140;
// Infrastructure allows max 3 retries (1 initial execution + 3 retries)
public int $tries = 4;
private Call $call;
private int $activityId;
private Team $team;
private ServiceInterface $crmService;
private LoggerInterface $logger;
private array $logContext;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(Call $call, int $activityId)
{
$this->call = $call;
$this->activityId = $activityId;
$this->logContext = [
'activity_id' => $activityId,
'call_id' => $call->getCallId(),
'provider' => $call->getProvider(),
];
$this->onQueue(Constants::QUEUE_DIALERS);
}
public function handle(
CrmObjectsResolver $crmObjectsResolver,
ProviderRegistry $providerRegistry,
ProviderRateLimiter $rateLimiter,
ActivityRepository $activityRepository,
LoggerInterface $logger,
Dispatcher $eventDispatcher,
): void {
$this->logger = $logger;
// Activity is already augmented with CRM data, no need to perform the same operation
if ($this->call->isActivityUpdatedWithCrm()) {
$this->logMessage('Skipping activity. Already updated with CRM data');
return;
}
/** @var Activity $activity */
$activity = $activityRepository->findById($this->activityId);
try {
$this->initialiseCrmService($activity, $providerRegistry);
} catch (SocialAccountTokenInvalidException $exception) {
$this->logMessage('Invalid token, retrying');
$this->release(self::MAX_DELAY); // Try again tomorrow
return;
}
$prospectSearchStrategy = ProspectSearchStrategyFactory::match($this->team);
if ($prospectSearchStrategy->ignoreCrmMatchData()) {
// Ignore any associated opportunity
$this->logger->info('[MatchCrmData] Ignoring crm data because of prospect strategy', [
'activity_id' => $this->activityId,
'strategy' => get_class($prospectSearchStrategy),
]);
return;
}
if (! $rateLimiter->canMakeRequest($activity->getCrm())) {
$this->logMessage('Rate limit reached, retrying');
$this->release($rateLimiter->requestAvailableIn($activity->getCrm()) + random_int(1, 60));
return;
}
$this->logMessage('Resolving CRM objects');
$rateLimiter->incrementRequestCount($activity->getCrm());
$crmObjects = $crmObjectsResolver->resolveFromCall($this->crmService, $this->call);
if (empty($crmObjects)) {
$this->logMessage('Could not resolve CRM objects, retrying');
$this->release(3600);
return;
}
[$lead, $account, $opportunity, $contact, $stage] = $crmObjects;
$activity->update([
'lead_id' => $lead->id ?? null,
'contact_id' => $contact->id ?? null,
'account_id' => $account->id ?? null,
'opportunity_id' => $opportunity->id ?? null,
'stage_id' => $stage->id ?? null,
]);
$activity->refresh();
$eventDispatcher->dispatch(new ActivityProspectAdded(
activity: $activity,
eventSource: 'match-crm-data'
));
if ($activity->getProspectName() !== null) {
$activity->setTitleFromCallData($this->call);
/** @var Participant $prospectParticipant */
$prospectParticipant = $activity
->participants()
->where(function (Builder $query) use ($activity) {
$query
->whereNull('user_id')
->orWhere('user_id', '!=', $activity->getUserId())
;
})
->first()
;
$activity->updateParticipantCrmData($crmObjects, $prospectParticipant);
}
$this->logMessage('Activity updated');
$this->triggerSummaryPushIfReady($activity, $eventDispatcher);
}
private function triggerSummaryPushIfReady(Activity $activity, Dispatcher $eventDispatcher): void
{
if (! $activity->hasTranscriptionId()) {
return;
}
if ($activity->hasProspectActivitySummaryLog()) {
$this->logMessage('Summary already sent to prospect, skipping summary push after CRM matching');
return;
}
$this->logMessage('Triggering summary push after CRM matching');
$eventDispatcher->dispatch(new TranscriptionAiSummaryReadyEvent($activity->getUuid()));
}
private function initialiseCrmService(Activity $activity, ProviderRegistry $providerRegistry): void
{
$this->team = $activity->getUser()->getTeam();
$crmProviderName = $this->team->getCrmConfiguration()->getProviderName();
$crmService = $providerRegistry->get($crmProviderName);
$crmService->setUser($this->team->getOwner());
$this->crmService = $crmService;
$this->logContext['team'] = $this->team->getSlug();
$this->logContext['team_id'] = $this->team->getId();
}
private function logMessage(string $message): void
{
$this->logger->info(sprintf('[MatchCrmData] %s', $message), $this->logContext);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – MatchCrmData.php
|
NULL
|
6332
|
|
6331
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Jiminny\Component\Utility\Service;
use Illuminate\Cache\RateLimiter;
use Jiminny\Contracts\Http\RateLimited;
use Jiminny\Contracts\Http\RateLimitInterface;
class ProviderRateLimiter
{
protected RateLimiter $rateLimiter;
public function __construct(RateLimiter $rateLimiter)
{
$this->rateLimiter = $rateLimiter;
}
public function canMakeRequest(RateLimited $provider): bool
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$key = $rateLimit->getKey();
if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {
return false;
}
}
return true;
}
public function requestAvailableIn(RateLimited $provider): int
{
return $provider->getRateLimits()->isNotEmpty()
? $provider->getRateLimits()
->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))
->max()
: 0
;
}
public function incrementRequestCount(RateLimited $provider): void
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());
}
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Activity\Import;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Events\Dispatcher;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\Queue\Constants;
use Jiminny\Component\Utility\Service\ProviderRateLimiter;
use Jiminny\Contracts\Services\Crm\ServiceInterface;
use Jiminny\DTO\ImportCall\Call;
use Jiminny\Component\TranscriptionSummary\Events\TranscriptionAiSummaryReadyEvent;
use Jiminny\Events\Activities\AiAutomation\ActivityProspectAdded;
use Jiminny\Exceptions\SocialAccountTokenInvalidException;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Participant;
use Jiminny\Models\Team;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmObjectsResolver;
use Jiminny\Services\Crm\ProspectSearchStrategyFactory;
use Jiminny\Services\Crm\ProviderRegistry;
use Psr\Log\LoggerInterface;
class MatchCrmData implements ShouldQueue
{
use Dispatchable;
use InteractsWithQueue;
use Queueable;
// AWS visibility timeout allows a maximum of 12 hours. This is 1 minute less.
private const int MAX_DELAY = 43140;
// Infrastructure allows max 3 retries (1 initial execution + 3 retries)
public int $tries = 4;
private Call $call;
private int $activityId;
private Team $team;
private ServiceInterface $crmService;
private LoggerInterface $logger;
private array $logContext;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(Call $call, int $activityId)
{
$this->call = $call;
$this->activityId = $activityId;
$this->logContext = [
'activity_id' => $activityId,
'call_id' => $call->getCallId(),
'provider' => $call->getProvider(),
];
$this->onQueue(Constants::QUEUE_DIALERS);
}
public function handle(
CrmObjectsResolver $crmObjectsResolver,
ProviderRegistry $providerRegistry,
ProviderRateLimiter $rateLimiter,
ActivityRepository $activityRepository,
LoggerInterface $logger,
Dispatcher $eventDispatcher,
): void {
$this->logger = $logger;
// Activity is already augmented with CRM data, no need to perform the same operation
if ($this->call->isActivityUpdatedWithCrm()) {
$this->logMessage('Skipping activity. Already updated with CRM data');
return;
}
/** @var Activity $activity */
$activity = $activityRepository->findById($this->activityId);
try {
$this->initialiseCrmService($activity, $providerRegistry);
} catch (SocialAccountTokenInvalidException $exception) {
$this->logMessage('Invalid token, retrying');
$this->release(self::MAX_DELAY); // Try again tomorrow
return;
}
$prospectSearchStrategy = ProspectSearchStrategyFactory::match($this->team);
if ($prospectSearchStrategy->ignoreCrmMatchData()) {
// Ignore any associated opportunity
$this->logger->info('[MatchCrmData] Ignoring crm data because of prospect strategy', [
'activity_id' => $this->activityId,
'strategy' => get_class($prospectSearchStrategy),
]);
return;
}
if (! $rateLimiter->canMakeRequest($activity->getCrm())) {
$this->logMessage('Rate limit reached, retrying');
$this->release($rateLimiter->requestAvailableIn($activity->getCrm()) + random_int(1, 60));
return;
}
$this->logMessage('Resolving CRM objects');
$rateLimiter->incrementRequestCount($activity->getCrm());
$crmObjects = $crmObjectsResolver->resolveFromCall($this->crmService, $this->call);
if (empty($crmObjects)) {
$this->logMessage('Could not resolve CRM objects, retrying');
$this->release(3600);
return;
}
[$lead, $account, $opportunity, $contact, $stage] = $crmObjects;
$activity->update([
'lead_id' => $lead->id ?? null,
'contact_id' => $contact->id ?? null,
'account_id' => $account->id ?? null,
'opportunity_id' => $opportunity->id ?? null,
'stage_id' => $stage->id ?? null,
]);
$activity->refresh();
$eventDispatcher->dispatch(new ActivityProspectAdded(
activity: $activity,
eventSource: 'match-crm-data'
));
if ($activity->getProspectName() !== null) {
$activity->setTitleFromCallData($this->call);
/** @var Participant $prospectParticipant */
$prospectParticipant = $activity
->participants()
->where(function (Builder $query) use ($activity) {
$query
->whereNull('user_id')
->orWhere('user_id', '!=', $activity->getUserId())
;
})
->first()
;
$activity->updateParticipantCrmData($crmObjects, $prospectParticipant);
}
$this->logMessage('Activity updated');
$this->triggerSummaryPushIfReady($activity, $eventDispatcher);
}
private function triggerSummaryPushIfReady(Activity $activity, Dispatcher $eventDispatcher): void
{
if (! $activity->hasTranscriptionId()) {
return;
}
if ($activity->hasProspectActivitySummaryLog()) {
$this->logMessage('Summary already sent to prospect, skipping summary push after CRM matching');
return;
}
$this->logMessage('Triggering summary push after CRM matching');
$eventDispatcher->dispatch(new TranscriptionAiSummaryReadyEvent($activity->getUuid()));
}
private function initialiseCrmService(Activity $activity, ProviderRegistry $providerRegistry): void
{
$this->team = $activity->getUser()->getTeam();
$crmProviderName = $this->team->getCrmConfiguration()->getProviderName();
$crmService = $providerRegistry->get($crmProviderName);
$crmService->setUser($this->team->getOwner());
$this->crmService = $crmService;
$this->logContext['team'] = $this->team->getSlug();
$this->logContext['team_id'] = $this->team->getId();
}
private function logMessage(string $message): void
{
$this->logger->info(sprintf('[MatchCrmData] %s', $message), $this->logContext);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – MatchCrmData.php
|
NULL
|
6331
|
|
6330
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Jiminny\Component\Utility\Service;
use Illuminate\Cache\RateLimiter;
use Jiminny\Contracts\Http\RateLimited;
use Jiminny\Contracts\Http\RateLimitInterface;
class ProviderRateLimiter
{
protected RateLimiter $rateLimiter;
public function __construct(RateLimiter $rateLimiter)
{
$this->rateLimiter = $rateLimiter;
}
public function canMakeRequest(RateLimited $provider): bool
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$key = $rateLimit->getKey();
if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {
return false;
}
}
return true;
}
public function requestAvailableIn(RateLimited $provider): int
{
return $provider->getRateLimits()->isNotEmpty()
? $provider->getRateLimits()
->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))
->max()
: 0
;
}
public function incrementRequestCount(RateLimited $provider): void
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());
}
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Activity\Import;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Events\Dispatcher;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\Queue\Constants;
use Jiminny\Component\Utility\Service\ProviderRateLimiter;
use Jiminny\Contracts\Services\Crm\ServiceInterface;
use Jiminny\DTO\ImportCall\Call;
use Jiminny\Component\TranscriptionSummary\Events\TranscriptionAiSummaryReadyEvent;
use Jiminny\Events\Activities\AiAutomation\ActivityProspectAdded;
use Jiminny\Exceptions\SocialAccountTokenInvalidException;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Participant;
use Jiminny\Models\Team;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmObjectsResolver;
use Jiminny\Services\Crm\ProspectSearchStrategyFactory;
use Jiminny\Services\Crm\ProviderRegistry;
use Psr\Log\LoggerInterface;
class MatchCrmData implements ShouldQueue
{
use Dispatchable;
use InteractsWithQueue;
use Queueable;
// AWS visibility timeout allows a maximum of 12 hours. This is 1 minute less.
private const int MAX_DELAY = 43140;
// Infrastructure allows max 3 retries (1 initial execution + 3 retries)
public int $tries = 4;
private Call $call;
private int $activityId;
private Team $team;
private ServiceInterface $crmService;
private LoggerInterface $logger;
private array $logContext;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(Call $call, int $activityId)
{
$this->call = $call;
$this->activityId = $activityId;
$this->logContext = [
'activity_id' => $activityId,
'call_id' => $call->getCallId(),
'provider' => $call->getProvider(),
];
$this->onQueue(Constants::QUEUE_DIALERS);
}
public function handle(
CrmObjectsResolver $crmObjectsResolver,
ProviderRegistry $providerRegistry,
ProviderRateLimiter $rateLimiter,
ActivityRepository $activityRepository,
LoggerInterface $logger,
Dispatcher $eventDispatcher,
): void {
$this->logger = $logger;
// Activity is already augmented with CRM data, no need to perform the same operation
if ($this->call->isActivityUpdatedWithCrm()) {
$this->logMessage('Skipping activity. Already updated with CRM data');
return;
}
/** @var Activity $activity */
$activity = $activityRepository->findById($this->activityId);
try {
$this->initialiseCrmService($activity, $providerRegistry);
} catch (SocialAccountTokenInvalidException $exception) {
$this->logMessage('Invalid token, retrying');
$this->release(self::MAX_DELAY); // Try again tomorrow
return;
}
$prospectSearchStrategy = ProspectSearchStrategyFactory::match($this->team);
if ($prospectSearchStrategy->ignoreCrmMatchData()) {
// Ignore any associated opportunity
$this->logger->info('[MatchCrmData] Ignoring crm data because of prospect strategy', [
'activity_id' => $this->activityId,
'strategy' => get_class($prospectSearchStrategy),
]);
return;
}
if (! $rateLimiter->canMakeRequest($activity->getCrm())) {
$this->logMessage('Rate limit reached, retrying');
$this->release($rateLimiter->requestAvailableIn($activity->getCrm()) + random_int(1, 60));
return;
}
$this->logMessage('Resolving CRM objects');
$rateLimiter->incrementRequestCount($activity->getCrm());
$crmObjects = $crmObjectsResolver->resolveFromCall($this->crmService, $this->call);
if (empty($crmObjects)) {
$this->logMessage('Could not resolve CRM objects, retrying');
$this->release(3600);
return;
}
[$lead, $account, $opportunity, $contact, $stage] = $crmObjects;
$activity->update([
'lead_id' => $lead->id ?? null,
'contact_id' => $contact->id ?? null,
'account_id' => $account->id ?? null,
'opportunity_id' => $opportunity->id ?? null,
'stage_id' => $stage->id ?? null,
]);
$activity->refresh();
$eventDispatcher->dispatch(new ActivityProspectAdded(
activity: $activity,
eventSource: 'match-crm-data'
));
if ($activity->getProspectName() !== null) {
$activity->setTitleFromCallData($this->call);
/** @var Participant $prospectParticipant */
$prospectParticipant = $activity
->participants()
->where(function (Builder $query) use ($activity) {
$query
->whereNull('user_id')
->orWhere('user_id', '!=', $activity->getUserId())
;
})
->first()
;
$activity->updateParticipantCrmData($crmObjects, $prospectParticipant);
}
$this->logMessage('Activity updated');
$this->triggerSummaryPushIfReady($activity, $eventDispatcher);
}
private function triggerSummaryPushIfReady(Activity $activity, Dispatcher $eventDispatcher): void
{
if (! $activity->hasTranscriptionId()) {
return;
}
if ($activity->hasProspectActivitySummaryLog()) {
$this->logMessage('Summary already sent to prospect, skipping summary push after CRM matching');
return;
}
$this->logMessage('Triggering summary push after CRM matching');
$eventDispatcher->dispatch(new TranscriptionAiSummaryReadyEvent($activity->getUuid()));
}
private function initialiseCrmService(Activity $activity, ProviderRegistry $providerRegistry): void
{
$this->team = $activity->getUser()->getTeam();
$crmProviderName = $this->team->getCrmConfiguration()->getProviderName();
$crmService = $providerRegistry->get($crmProviderName);
$crmService->setUser($this->team->getOwner());
$this->crmService = $crmService;
$this->logContext['team'] = $this->team->getSlug();
$this->logContext['team_id'] = $this->team->getId();
}
private function logMessage(string $message): void
{
$this->logger->info(sprintf('[MatchCrmData] %s', $message), $this->logContext);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – MatchCrmData.php
|
NULL
|
6330
|
|
6329
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Jiminny\Component\Utility\Service;
use Illuminate\Cache\RateLimiter;
use Jiminny\Contracts\Http\RateLimited;
use Jiminny\Contracts\Http\RateLimitInterface;
class ProviderRateLimiter
{
protected RateLimiter $rateLimiter;
public function __construct(RateLimiter $rateLimiter)
{
$this->rateLimiter = $rateLimiter;
}
public function canMakeRequest(RateLimited $provider): bool
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$key = $rateLimit->getKey();
if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {
return false;
}
}
return true;
}
public function requestAvailableIn(RateLimited $provider): int
{
return $provider->getRateLimits()->isNotEmpty()
? $provider->getRateLimits()
->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))
->max()
: 0
;
}
public function incrementRequestCount(RateLimited $provider): void
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());
}
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Activity\Import;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Events\Dispatcher;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\Queue\Constants;
use Jiminny\Component\Utility\Service\ProviderRateLimiter;
use Jiminny\Contracts\Services\Crm\ServiceInterface;
use Jiminny\DTO\ImportCall\Call;
use Jiminny\Component\TranscriptionSummary\Events\TranscriptionAiSummaryReadyEvent;
use Jiminny\Events\Activities\AiAutomation\ActivityProspectAdded;
use Jiminny\Exceptions\SocialAccountTokenInvalidException;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Participant;
use Jiminny\Models\Team;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmObjectsResolver;
use Jiminny\Services\Crm\ProspectSearchStrategyFactory;
use Jiminny\Services\Crm\ProviderRegistry;
use Psr\Log\LoggerInterface;
class MatchCrmData implements ShouldQueue
{
use Dispatchable;
use InteractsWithQueue;
use Queueable;
// AWS visibility timeout allows a maximum of 12 hours. This is 1 minute less.
private const int MAX_DELAY = 43140;
// Infrastructure allows max 3 retries (1 initial execution + 3 retries)
public int $tries = 4;
private Call $call;
private int $activityId;
private Team $team;
private ServiceInterface $crmService;
private LoggerInterface $logger;
private array $logContext;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(Call $call, int $activityId)
{
$this->call = $call;
$this->activityId = $activityId;
$this->logContext = [
'activity_id' => $activityId,
'call_id' => $call->getCallId(),
'provider' => $call->getProvider(),
];
$this->onQueue(Constants::QUEUE_DIALERS);
}
public function handle(
CrmObjectsResolver $crmObjectsResolver,
ProviderRegistry $providerRegistry,
ProviderRateLimiter $rateLimiter,
ActivityRepository $activityRepository,
LoggerInterface $logger,
Dispatcher $eventDispatcher,
): void {
$this->logger = $logger;
// Activity is already augmented with CRM data, no need to perform the same operation
if ($this->call->isActivityUpdatedWithCrm()) {
$this->logMessage('Skipping activity. Already updated with CRM data');
return;
}
/** @var Activity $activity */
$activity = $activityRepository->findById($this->activityId);
try {
$this->initialiseCrmService($activity, $providerRegistry);
} catch (SocialAccountTokenInvalidException $exception) {
$this->logMessage('Invalid token, retrying');
$this->release(self::MAX_DELAY); // Try again tomorrow
return;
}
$prospectSearchStrategy = ProspectSearchStrategyFactory::match($this->team);
if ($prospectSearchStrategy->ignoreCrmMatchData()) {
// Ignore any associated opportunity
$this->logger->info('[MatchCrmData] Ignoring crm data because of prospect strategy', [
'activity_id' => $this->activityId,
'strategy' => get_class($prospectSearchStrategy),
]);
return;
}
if (! $rateLimiter->canMakeRequest($activity->getCrm())) {
$this->logMessage('Rate limit reached, retrying');
$this->release($rateLimiter->requestAvailableIn($activity->getCrm()) + random_int(1, 60));
return;
}
$this->logMessage('Resolving CRM objects');
$rateLimiter->incrementRequestCount($activity->getCrm());
$crmObjects = $crmObjectsResolver->resolveFromCall($this->crmService, $this->call);
if (empty($crmObjects)) {
$this->logMessage('Could not resolve CRM objects, retrying');
$this->release(3600);
return;
}
[$lead, $account, $opportunity, $contact, $stage] = $crmObjects;
$activity->update([
'lead_id' => $lead->id ?? null,
'contact_id' => $contact->id ?? null,
'account_id' => $account->id ?? null,
'opportunity_id' => $opportunity->id ?? null,
'stage_id' => $stage->id ?? null,
]);
$activity->refresh();
$eventDispatcher->dispatch(new ActivityProspectAdded(
activity: $activity,
eventSource: 'match-crm-data'
));
if ($activity->getProspectName() !== null) {
$activity->setTitleFromCallData($this->call);
/** @var Participant $prospectParticipant */
$prospectParticipant = $activity
->participants()
->where(function (Builder $query) use ($activity) {
$query
->whereNull('user_id')
->orWhere('user_id', '!=', $activity->getUserId())
;
})
->first()
;
$activity->updateParticipantCrmData($crmObjects, $prospectParticipant);
}
$this->logMessage('Activity updated');
$this->triggerSummaryPushIfReady($activity, $eventDispatcher);
}
private function triggerSummaryPushIfReady(Activity $activity, Dispatcher $eventDispatcher): void
{
if (! $activity->hasTranscriptionId()) {
return;
}
if ($activity->hasProspectActivitySummaryLog()) {
$this->logMessage('Summary already sent to prospect, skipping summary push after CRM matching');
return;
}
$this->logMessage('Triggering summary push after CRM matching');
$eventDispatcher->dispatch(new TranscriptionAiSummaryReadyEvent($activity->getUuid()));
}
private function initialiseCrmService(Activity $activity, ProviderRegistry $providerRegistry): void
{
$this->team = $activity->getUser()->getTeam();
$crmProviderName = $this->team->getCrmConfiguration()->getProviderName();
$crmService = $providerRegistry->get($crmProviderName);
$crmService->setUser($this->team->getOwner());
$this->crmService = $crmService;
$this->logContext['team'] = $this->team->getSlug();
$this->logContext['team_id'] = $this->team->getId();
}
private function logMessage(string $message): void
{
$this->logger->info(sprintf('[MatchCrmData] %s', $message), $this->logContext);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – MatchCrmData.php
|
NULL
|
6329
|
|
6328
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Jiminny\Component\Utility\Service;
use Illuminate\Cache\RateLimiter;
use Jiminny\Contracts\Http\RateLimited;
use Jiminny\Contracts\Http\RateLimitInterface;
class ProviderRateLimiter
{
protected RateLimiter $rateLimiter;
public function __construct(RateLimiter $rateLimiter)
{
$this->rateLimiter = $rateLimiter;
}
public function canMakeRequest(RateLimited $provider): bool
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$key = $rateLimit->getKey();
if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {
return false;
}
}
return true;
}
public function requestAvailableIn(RateLimited $provider): int
{
return $provider->getRateLimits()->isNotEmpty()
? $provider->getRateLimits()
->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))
->max()
: 0
;
}
public function incrementRequestCount(RateLimited $provider): void
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());
}
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Activity\Import;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Events\Dispatcher;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\Queue\Constants;
use Jiminny\Component\Utility\Service\ProviderRateLimiter;
use Jiminny\Contracts\Services\Crm\ServiceInterface;
use Jiminny\DTO\ImportCall\Call;
use Jiminny\Component\TranscriptionSummary\Events\TranscriptionAiSummaryReadyEvent;
use Jiminny\Events\Activities\AiAutomation\ActivityProspectAdded;
use Jiminny\Exceptions\SocialAccountTokenInvalidException;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Participant;
use Jiminny\Models\Team;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmObjectsResolver;
use Jiminny\Services\Crm\ProspectSearchStrategyFactory;
use Jiminny\Services\Crm\ProviderRegistry;
use Psr\Log\LoggerInterface;
class MatchCrmData implements ShouldQueue
{
use Dispatchable;
use InteractsWithQueue;
use Queueable;
// AWS visibility timeout allows a maximum of 12 hours. This is 1 minute less.
private const int MAX_DELAY = 43140;
// Infrastructure allows max 3 retries (1 initial execution + 3 retries)
public int $tries = 4;
private Call $call;
private int $activityId;
private Team $team;
private ServiceInterface $crmService;
private LoggerInterface $logger;
private array $logContext;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(Call $call, int $activityId)
{
$this->call = $call;
$this->activityId = $activityId;
$this->logContext = [
'activity_id' => $activityId,
'call_id' => $call->getCallId(),
'provider' => $call->getProvider(),
];
$this->onQueue(Constants::QUEUE_DIALERS);
}
public function handle(
CrmObjectsResolver $crmObjectsResolver,
ProviderRegistry $providerRegistry,
ProviderRateLimiter $rateLimiter,
ActivityRepository $activityRepository,
LoggerInterface $logger,
Dispatcher $eventDispatcher,
): void {
$this->logger = $logger;
// Activity is already augmented with CRM data, no need to perform the same operation
if ($this->call->isActivityUpdatedWithCrm()) {
$this->logMessage('Skipping activity. Already updated with CRM data');
return;
}
/** @var Activity $activity */
$activity = $activityRepository->findById($this->activityId);
try {
$this->initialiseCrmService($activity, $providerRegistry);
} catch (SocialAccountTokenInvalidException $exception) {
$this->logMessage('Invalid token, retrying');
$this->release(self::MAX_DELAY); // Try again tomorrow
return;
}
$prospectSearchStrategy = ProspectSearchStrategyFactory::match($this->team);
if ($prospectSearchStrategy->ignoreCrmMatchData()) {
// Ignore any associated opportunity
$this->logger->info('[MatchCrmData] Ignoring crm data because of prospect strategy', [
'activity_id' => $this->activityId,
'strategy' => get_class($prospectSearchStrategy),
]);
return;
}
if (! $rateLimiter->canMakeRequest($activity->getCrm())) {
$this->logMessage('Rate limit reached, retrying');
$this->release($rateLimiter->requestAvailableIn($activity->getCrm()) + random_int(1, 60));
return;
}
$this->logMessage('Resolving CRM objects');
$rateLimiter->incrementRequestCount($activity->getCrm());
$crmObjects = $crmObjectsResolver->resolveFromCall($this->crmService, $this->call);
if (empty($crmObjects)) {
$this->logMessage('Could not resolve CRM objects, retrying');
$this->release(3600);
return;
}
[$lead, $account, $opportunity, $contact, $stage] = $crmObjects;
$activity->update([
'lead_id' => $lead->id ?? null,
'contact_id' => $contact->id ?? null,
'account_id' => $account->id ?? null,
'opportunity_id' => $opportunity->id ?? null,
'stage_id' => $stage->id ?? null,
]);
$activity->refresh();
$eventDispatcher->dispatch(new ActivityProspectAdded(
activity: $activity,
eventSource: 'match-crm-data'
));
if ($activity->getProspectName() !== null) {
$activity->setTitleFromCallData($this->call);
/** @var Participant $prospectParticipant */
$prospectParticipant = $activity
->participants()
->where(function (Builder $query) use ($activity) {
$query
->whereNull('user_id')
->orWhere('user_id', '!=', $activity->getUserId())
;
})
->first()
;
$activity->updateParticipantCrmData($crmObjects, $prospectParticipant);
}
$this->logMessage('Activity updated');
$this->triggerSummaryPushIfReady($activity, $eventDispatcher);
}
private function triggerSummaryPushIfReady(Activity $activity, Dispatcher $eventDispatcher): void
{
if (! $activity->hasTranscriptionId()) {
return;
}
if ($activity->hasProspectActivitySummaryLog()) {
$this->logMessage('Summary already sent to prospect, skipping summary push after CRM matching');
return;
}
$this->logMessage('Triggering summary push after CRM matching');
$eventDispatcher->dispatch(new TranscriptionAiSummaryReadyEvent($activity->getUuid()));
}
private function initialiseCrmService(Activity $activity, ProviderRegistry $providerRegistry): void
{
$this->team = $activity->getUser()->getTeam();
$crmProviderName = $this->team->getCrmConfiguration()->getProviderName();
$crmService = $providerRegistry->get($crmProviderName);
$crmService->setUser($this->team->getOwner());
$this->crmService = $crmService;
$this->logContext['team'] = $this->team->getSlug();
$this->logContext['team_id'] = $this->team->getId();
}
private function logMessage(string $message): void
{
$this->logger->info(sprintf('[MatchCrmData] %s', $message), $this->logContext);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – MatchCrmData.php
|
NULL
|
6328
|
|
6327
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Jiminny\Component\Utility\Service;
use Illuminate\Cache\RateLimiter;
use Jiminny\Contracts\Http\RateLimited;
use Jiminny\Contracts\Http\RateLimitInterface;
class ProviderRateLimiter
{
protected RateLimiter $rateLimiter;
public function __construct(RateLimiter $rateLimiter)
{
$this->rateLimiter = $rateLimiter;
}
public function canMakeRequest(RateLimited $provider): bool
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$key = $rateLimit->getKey();
if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {
return false;
}
}
return true;
}
public function requestAvailableIn(RateLimited $provider): int
{
return $provider->getRateLimits()->isNotEmpty()
? $provider->getRateLimits()
->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))
->max()
: 0
;
}
public function incrementRequestCount(RateLimited $provider): void
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());
}
}
}...
|
PhpStorm
|
faVsco.js – MatchCrmData.php
|
NULL
|
6327
|
|
6326
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Jiminny\Component\Utility\Service;
use Illuminate\Cache\RateLimiter;
use Jiminny\Contracts\Http\RateLimited;
use Jiminny\Contracts\Http\RateLimitInterface;
class ProviderRateLimiter
{
protected RateLimiter $rateLimiter;
public function __construct(RateLimiter $rateLimiter)
{
$this->rateLimiter = $rateLimiter;
}
public function canMakeRequest(RateLimited $provider): bool
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$key = $rateLimit->getKey();
if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {
return false;
}
}
return true;
}
public function requestAvailableIn(RateLimited $provider): int
{
return $provider->getRateLimits()->isNotEmpty()
? $provider->getRateLimits()
->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))
->max()
: 0
;
}
public function incrementRequestCount(RateLimited $provider): void
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());
}
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Activity\Import;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Events\Dispatcher;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\Queue\Constants;
use Jiminny\Component\Utility\Service\ProviderRateLimiter;
use Jiminny\Contracts\Services\Crm\ServiceInterface;
use Jiminny\DTO\ImportCall\Call;
use Jiminny\Component\TranscriptionSummary\Events\TranscriptionAiSummaryReadyEvent;
use Jiminny\Events\Activities\AiAutomation\ActivityProspectAdded;
use Jiminny\Exceptions\SocialAccountTokenInvalidException;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Participant;
use Jiminny\Models\Team;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmObjectsResolver;
use Jiminny\Services\Crm\ProspectSearchStrategyFactory;
use Jiminny\Services\Crm\ProviderRegistry;
use Psr\Log\LoggerInterface;
class MatchCrmData implements ShouldQueue
{
use Dispatchable;
use InteractsWithQueue;
use Queueable;
// AWS visibility timeout allows a maximum of 12 hours. This is 1 minute less.
private const int MAX_DELAY = 43140;
// Infrastructure allows max 3 retries (1 initial execution + 3 retries)
public int $tries = 4;
private Call $call;
private int $activityId;
private Team $team;
private ServiceInterface $crmService;
private LoggerInterface $logger;
private array $logContext;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(Call $call, int $activityId)
{
$this->call = $call;
$this->activityId = $activityId;
$this->logContext = [
'activity_id' => $activityId,
'call_id' => $call->getCallId(),
'provider' => $call->getProvider(),
];
$this->onQueue(Constants::QUEUE_DIALERS);
}
public function handle(
CrmObjectsResolver $crmObjectsResolver,
ProviderRegistry $providerRegistry,
ProviderRateLimiter $rateLimiter,
ActivityRepository $activityRepository,
LoggerInterface $logger,
Dispatcher $eventDispatcher,
): void {
$this->logger = $logger;
// Activity is already augmented with CRM data, no need to perform the same operation
if ($this->call->isActivityUpdatedWithCrm()) {
$this->logMessage('Skipping activity. Already updated with CRM data');
return;
}
/** @var Activity $activity */
$activity = $activityRepository->findById($this->activityId);
try {
$this->initialiseCrmService($activity, $providerRegistry);
} catch (SocialAccountTokenInvalidException $exception) {
$this->logMessage('Invalid token, retrying');
$this->release(self::MAX_DELAY); // Try again tomorrow
return;
}
$prospectSearchStrategy = ProspectSearchStrategyFactory::match($this->team);
if ($prospectSearchStrategy->ignoreCrmMatchData()) {
// Ignore any associated opportunity
$this->logger->info('[MatchCrmData] Ignoring crm data because of prospect strategy', [
'activity_id' => $this->activityId,
'strategy' => get_class($prospectSearchStrategy),
]);
return;
}
if (! $rateLimiter->canMakeRequest($activity->getCrm())) {
$this->logMessage('Rate limit reached, retrying');
$this->release($rateLimiter->requestAvailableIn($activity->getCrm()) + random_int(1, 60));
return;
}
$this->logMessage('Resolving CRM objects');
$rateLimiter->incrementRequestCount($activity->getCrm());
$crmObjects = $crmObjectsResolver->resolveFromCall($this->crmService, $this->call);
if (empty($crmObjects)) {
$this->logMessage('Could not resolve CRM objects, retrying');
$this->release(3600);
return;
}
[$lead, $account, $opportunity, $contact, $stage] = $crmObjects;
$activity->update([
'lead_id' => $lead->id ?? null,
'contact_id' => $contact->id ?? null,
'account_id' => $account->id ?? null,
'opportunity_id' => $opportunity->id ?? null,
'stage_id' => $stage->id ?? null,
]);
$activity->refresh();
$eventDispatcher->dispatch(new ActivityProspectAdded(
activity: $activity,
eventSource: 'match-crm-data'
));
if ($activity->getProspectName() !== null) {
$activity->setTitleFromCallData($this->call);
/** @var Participant $prospectParticipant */
$prospectParticipant = $activity
->participants()
->where(function (Builder $query) use ($activity) {
$query
->whereNull('user_id')
->orWhere('user_id', '!=', $activity->getUserId())
;
})
->first()
;
$activity->updateParticipantCrmData($crmObjects, $prospectParticipant);
}
$this->logMessage('Activity updated');
$this->triggerSummaryPushIfReady($activity, $eventDispatcher);
}
private function triggerSummaryPushIfReady(Activity $activity, Dispatcher $eventDispatcher): void
{
if (! $activity->hasTranscriptionId()) {
return;
}
if ($activity->hasProspectActivitySummaryLog()) {
$this->logMessage('Summary already sent to prospect, skipping summary push after CRM matching');
return;
}
$this->logMessage('Triggering summary push after CRM matching');
$eventDispatcher->dispatch(new TranscriptionAiSummaryReadyEvent($activity->getUuid()));
}
private function initialiseCrmService(Activity $activity, ProviderRegistry $providerRegistry): void
{
$this->team = $activity->getUser()->getTeam();
$crmProviderName = $this->team->getCrmConfiguration()->getProviderName();
$crmService = $providerRegistry->get($crmProviderName);
$crmService->setUser($this->team->getOwner());
$this->crmService = $crmService;
$this->logContext['team'] = $this->team->getSlug();
$this->logContext['team_id'] = $this->team->getId();
}
private function logMessage(string $message): void
{
$this->logger->info(sprintf('[MatchCrmData] %s', $message), $this->logContext);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – MatchCrmData.php
|
NULL
|
6326
|
|
6325
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Jiminny\Component\Utility\Service;
use Illuminate\Cache\RateLimiter;
use Jiminny\Contracts\Http\RateLimited;
use Jiminny\Contracts\Http\RateLimitInterface;
class ProviderRateLimiter
{
protected RateLimiter $rateLimiter;
public function __construct(RateLimiter $rateLimiter)
{
$this->rateLimiter = $rateLimiter;
}
public function canMakeRequest(RateLimited $provider): bool
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$key = $rateLimit->getKey();
if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {
return false;
}
}
return true;
}
public function requestAvailableIn(RateLimited $provider): int
{
return $provider->getRateLimits()->isNotEmpty()
? $provider->getRateLimits()
->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))
->max()
: 0
;
}
public function incrementRequestCount(RateLimited $provider): void
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());
}
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Activity\Import;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Events\Dispatcher;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\Queue\Constants;
use Jiminny\Component\Utility\Service\ProviderRateLimiter;
use Jiminny\Contracts\Services\Crm\ServiceInterface;
use Jiminny\DTO\ImportCall\Call;
use Jiminny\Component\TranscriptionSummary\Events\TranscriptionAiSummaryReadyEvent;
use Jiminny\Events\Activities\AiAutomation\ActivityProspectAdded;
use Jiminny\Exceptions\SocialAccountTokenInvalidException;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Participant;
use Jiminny\Models\Team;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmObjectsResolver;
use Jiminny\Services\Crm\ProspectSearchStrategyFactory;
use Jiminny\Services\Crm\ProviderRegistry;
use Psr\Log\LoggerInterface;
class MatchCrmData implements ShouldQueue
{
use Dispatchable;
use InteractsWithQueue;
use Queueable;
// AWS visibility timeout allows a maximum of 12 hours. This is 1 minute less.
private const int MAX_DELAY = 43140;
// Infrastructure allows max 3 retries (1 initial execution + 3 retries)
public int $tries = 4;
private Call $call;
private int $activityId;
private Team $team;
private ServiceInterface $crmService;
private LoggerInterface $logger;
private array $logContext;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(Call $call, int $activityId)
{
$this->call = $call;
$this->activityId = $activityId;
$this->logContext = [
'activity_id' => $activityId,
'call_id' => $call->getCallId(),
'provider' => $call->getProvider(),
];
$this->onQueue(Constants::QUEUE_DIALERS);
}
public function handle(
CrmObjectsResolver $crmObjectsResolver,
ProviderRegistry $providerRegistry,
ProviderRateLimiter $rateLimiter,
ActivityRepository $activityRepository,
LoggerInterface $logger,
Dispatcher $eventDispatcher,
): void {
$this->logger = $logger;
// Activity is already augmented with CRM data, no need to perform the same operation
if ($this->call->isActivityUpdatedWithCrm()) {
$this->logMessage('Skipping activity. Already updated with CRM data');
return;
}
/** @var Activity $activity */
$activity = $activityRepository->findById($this->activityId);
try {
$this->initialiseCrmService($activity, $providerRegistry);
} catch (SocialAccountTokenInvalidException $exception) {
$this->logMessage('Invalid token, retrying');
$this->release(self::MAX_DELAY); // Try again tomorrow
return;
}
$prospectSearchStrategy = ProspectSearchStrategyFactory::match($this->team);
if ($prospectSearchStrategy->ignoreCrmMatchData()) {
// Ignore any associated opportunity
$this->logger->info('[MatchCrmData] Ignoring crm data because of prospect strategy', [
'activity_id' => $this->activityId,
'strategy' => get_class($prospectSearchStrategy),
]);
return;
}
if (! $rateLimiter->canMakeRequest($activity->getCrm())) {
$this->logMessage('Rate limit reached, retrying');
$this->release($rateLimiter->requestAvailableIn($activity->getCrm()) + random_int(1, 60));
return;
}
$this->logMessage('Resolving CRM objects');
$rateLimiter->incrementRequestCount($activity->getCrm());
$crmObjects = $crmObjectsResolver->resolveFromCall($this->crmService, $this->call);
if (empty($crmObjects)) {
$this->logMessage('Could not resolve CRM objects, retrying');
$this->release(3600);
return;
}
[$lead, $account, $opportunity, $contact, $stage] = $crmObjects;
$activity->update([
'lead_id' => $lead->id ?? null,
'contact_id' => $contact->id ?? null,
'account_id' => $account->id ?? null,
'opportunity_id' => $opportunity->id ?? null,
'stage_id' => $stage->id ?? null,
]);
$activity->refresh();
$eventDispatcher->dispatch(new ActivityProspectAdded(
activity: $activity,
eventSource: 'match-crm-data'
));
if ($activity->getProspectName() !== null) {
$activity->setTitleFromCallData($this->call);
/** @var Participant $prospectParticipant */
$prospectParticipant = $activity
->participants()
->where(function (Builder $query) use ($activity) {
$query
->whereNull('user_id')
->orWhere('user_id', '!=', $activity->getUserId())
;
})
->first()
;
$activity->updateParticipantCrmData($crmObjects, $prospectParticipant);
}
$this->logMessage('Activity updated');
$this->triggerSummaryPushIfReady($activity, $eventDispatcher);
}
private function triggerSummaryPushIfReady(Activity $activity, Dispatcher $eventDispatcher): void
{
if (! $activity->hasTranscriptionId()) {
return;
}
if ($activity->hasProspectActivitySummaryLog()) {
$this->logMessage('Summary already sent to prospect, skipping summary push after CRM matching');
return;
}
$this->logMessage('Triggering summary push after CRM matching');
$eventDispatcher->dispatch(new TranscriptionAiSummaryReadyEvent($activity->getUuid()));
}
private function initialiseCrmService(Activity $activity, ProviderRegistry $providerRegistry): void
{
$this->team = $activity->getUser()->getTeam();
$crmProviderName = $this->team->getCrmConfiguration()->getProviderName();
$crmService = $providerRegistry->get($crmProviderName);
$crmService->setUser($this->team->getOwner());
$this->crmService = $crmService;
$this->logContext['team'] = $this->team->getSlug();
$this->logContext['team_id'] = $this->team->getId();
}
private function logMessage(string $message): void
{
$this->logger->info(sprintf('[MatchCrmData] %s', $message), $this->logContext);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – MatchCrmData.php
|
NULL
|
6325
|
|
6324
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
iTerm2ShellEditViewSessionScriptsProfilesWindowHelplahl100% <78Thu 7 May 21:15:49STAGE (ssh)*3181DOCKERDEV (-zsh)882APP (-zsh)DOCKER (-zsh)docker_lamp_1docker_lamp_12026-05-07 14:30:06 Running ['artisan'meeting-bot:schedule-bot] ….6S DONEdocker_1amp_11 '/usr/local/bin/php' 'artisan'meeting-bot: schedule-bot > */proc/1/fd/1'2>&1docker_lamp_12026-05-07 14:30:13 Running ['artisan'dialers:monitor-activities]4sDONEdocker_1amp_11 '/usr/local/bin/php' 'artisan' dialers:monitor-activities › */proc/1/fd/1'2>&1docker_lamp_12026-05-07 14:30:17 Running ['artisan' jiminny:monitor-social-accountSJ3s DONEdocker_lamp_11 '/usr/local/bin/php' 'artisan' jiminny:monitor-social-accounts > */proc/1/fd/1'2>&1docker_lamp_12026-05-07 14:30:20 Running ['artisan' mailbox:skip-lists:refresh]2sDONEdocker_lamp_11 '/usr/local/bin/php' 'artisan'mailbox: skip-lists:refresh › */proc/1/fd/1'2>&1docker_lamp_112026-05-07 14:30:23 Running ['artisan' mailbox:batch:process --max-batches=15]2s DONEdocker_Lamp_11 '/usr/local/bin/php' 'artisan'mailbox:batch:process --max-batches=15 >*/proc/1/fd/1' 2>&1docker_lamp_12026-05-07 14:30:25 Running ['artisan' conference:monitor: count]1S DONEdocker_lamp_11 '/usr/local/bin/php' 'artisan' conference:monitor: count > */proc/1/fd/1'2>&1docker_lamp_12026-05-07 14:30:27 Running ['artisan' activity:purge-stale]2S DONEdocker_1amp_11 '/usr/local/bin/php' 'artisan' activity:purge-stale › '/proc/1/fd/12>&1docker_lamp_1docker_lamp_1docker_lamp_1-2026-05-07 14:30:30 Running ['artisan' mailbox:text-relay:sync] {"error":"invalid_request""error_description": "Invalidimpersonation \u0026quot; sub\u0026quot;field: @"docker_1amp_11}docker_lamp_14sDONEdocker_lamp_11 '/usr/local/bin/php' 'artisan' mailbox:text-relay:sync › */proc/1/fd/1' 2>&1docker_1amp_12026-05-07 14:30:35 Running ['artisan'conference:pre-meeting-notification]10s DONEdocker_1amp_111 '/usr/local/bin/php' 'artisan' conference:pre-meeting-notification'/proc/1/fd/1' 2>&1unexpected EOFukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/jiminny/infrastructure/dev/docker (develop) $-zsh• 84screenpipe*XIY2PROD (ssh)New release '24.04.4 LTS' available.Run 'do-release-upgrade' to upgrade to it.• *5-zsh|*** System restart required ***Last login: Mon Apr 27 07:45:27 2026 from 212.5.153.87X L3 EU (-zsh)Last login: Thu May 7 09:29:14 on consolePoetry could not find a pyproject.toml file in /Users/lukas or its parentsPoetry could not find a pyproject.toml file in /Users/lukas or its parents@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ I|T4STAGE (ssh)Run 'do-release-upgrade' to upgrade to it.*** System restart required ***Last login: Tue Apr 28 06:25:10 2026 from 212.5.153.87in:-$XIQA (-zsh)Last login: Thu May 7 09:44:56on ttys002Poetry could not find a pyproject.toml file in /Users/lukas or its parentsPoetry could not find a pyproject.tomlfile in /Users/lukas or its parentsX 16FE (-zsh)Last login: Thu May 7 09:44:56on ttys004₴6+PRODSTAGEPoetry could not find a pyproject.toml file in /Users/lukas or its parentsFRONTENDPoetry could not find a pyproject.tomlfile in /Users/lukas or its parentslukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ I17 EXT (-zsh)Poetry could not find a pyproject.toml file in /Users/lukas or its parentsEXTENSIONPoetry could not find a pyproject.tomlfile in /Users/lukas or its parentslukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ I|...
|
PhpStorm
|
faVsco.js – MatchCrmData.php
|
NULL
|
6324
|
|
6323
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Jiminny\Component\Utility\Service;
use Illuminate\Cache\RateLimiter;
use Jiminny\Contracts\Http\RateLimited;
use Jiminny\Contracts\Http\RateLimitInterface;
class ProviderRateLimiter
{
protected RateLimiter $rateLimiter;
public function __construct(RateLimiter $rateLimiter)
{
$this->rateLimiter = $rateLimiter;
}
public function canMakeRequest(RateLimited $provider): bool
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$key = $rateLimit->getKey();
if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {
return false;
}
}
return true;
}
public function requestAvailableIn(RateLimited $provider): int
{
return $provider->getRateLimits()->isNotEmpty()
? $provider->getRateLimits()
->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))
->max()
: 0
;
}
public function incrementRequestCount(RateLimited $provider): void
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());
}
}
}
Sync Changes...
|
PhpStorm
|
faVsco.js – MatchCrmData.php
|
NULL
|
6323
|
|
6322
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Jiminny\Component\Utility\Service;
use Illuminate\Cache\RateLimiter;
use Jiminny\Contracts\Http\RateLimited;
use Jiminny\Contracts\Http\RateLimitInterface;
class ProviderRateLimiter
{
protected RateLimiter $rateLimiter;
public function __construct(RateLimiter $rateLimiter)
{
$this->rateLimiter = $rateLimiter;
}
public function canMakeRequest(RateLimited $provider): bool
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$key = $rateLimit->getKey();
if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {
return false;
}
}
return true;
}
public function requestAvailableIn(RateLimited $provider): int
{
return $provider->getRateLimits()->isNotEmpty()
? $provider->getRateLimits()
->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))
->max()
: 0
;
}
public function incrementRequestCount(RateLimited $provider): void
{
/** @var RateLimitInterface $rateLimit */
foreach ($provider->getRateLimits() as $rateLimit) {
$this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());
}
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Activity\Import;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Events\Dispatcher;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\Queue\Constants;
use Jiminny\Component\Utility\Service\ProviderRateLimiter;
use Jiminny\Contracts\Services\Crm\ServiceInterface;
use Jiminny\DTO\ImportCall\Call;
use Jiminny\Component\TranscriptionSummary\Events\TranscriptionAiSummaryReadyEvent;
use Jiminny\Events\Activities\AiAutomation\ActivityProspectAdded;
use Jiminny\Exceptions\SocialAccountTokenInvalidException;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Participant;
use Jiminny\Models\Team;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmObjectsResolver;
use Jiminny\Services\Crm\ProspectSearchStrategyFactory;
use Jiminny\Services\Crm\ProviderRegistry;
use Psr\Log\LoggerInterface;
class MatchCrmData implements ShouldQueue
{
use Dispatchable;
use InteractsWithQueue;
use Queueable;
// AWS visibility timeout allows a maximum of 12 hours. This is 1 minute less.
private const int MAX_DELAY = 43140;
// Infrastructure allows max 3 retries (1 initial execution + 3 retries)
public int $tries = 4;
private Call $call;
private int $activityId;
private Team $team;
private ServiceInterface $crmService;
private LoggerInterface $logger;
private array $logContext;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(Call $call, int $activityId)
{
$this->call = $call;
$this->activityId = $activityId;
$this->logContext = [
'activity_id' => $activityId,
'call_id' => $call->getCallId(),
'provider' => $call->getProvider(),
];
$this->onQueue(Constants::QUEUE_DIALERS);
}
public function handle(
CrmObjectsResolver $crmObjectsResolver,
ProviderRegistry $providerRegistry,
ProviderRateLimiter $rateLimiter,
ActivityRepository $activityRepository,
LoggerInterface $logger,
Dispatcher $eventDispatcher,
): void {
$this->logger = $logger;
// Activity is already augmented with CRM data, no need to perform the same operation
if ($this->call->isActivityUpdatedWithCrm()) {
$this->logMessage('Skipping activity. Already updated with CRM data');
return;
}
/** @var Activity $activity */
$activity = $activityRepository->findById($this->activityId);
try {
$this->initialiseCrmService($activity, $providerRegistry);
} catch (SocialAccountTokenInvalidException $exception) {
$this->logMessage('Invalid token, retrying');
$this->release(self::MAX_DELAY); // Try again tomorrow
return;
}
$prospectSearchStrategy = ProspectSearchStrategyFactory::match($this->team);
if ($prospectSearchStrategy->ignoreCrmMatchData()) {
// Ignore any associated opportunity
$this->logger->info('[MatchCrmData] Ignoring crm data because of prospect strategy', [
'activity_id' => $this->activityId,
'strategy' => get_class($prospectSearchStrategy),
]);
return;
}
if (! $rateLimiter->canMakeRequest($activity->getCrm())) {
$this->logMessage('Rate limit reached, retrying');
$this->release($rateLimiter->requestAvailableIn($activity->getCrm()) + random_int(1, 60));
return;
}
$this->logMessage('Resolving CRM objects');
$rateLimiter->incrementRequestCount($activity->getCrm());
$crmObjects = $crmObjectsResolver->resolveFromCall($this->crmService, $this->call);
if (empty($crmObjects)) {
$this->logMessage('Could not resolve CRM objects, retrying');
$this->release(3600);
return;
}
[$lead, $account, $opportunity, $contact, $stage] = $crmObjects;
$activity->update([
'lead_id' => $lead->id ?? null,
'contact_id' => $contact->id ?? null,
'account_id' => $account->id ?? null,
'opportunity_id' => $opportunity->id ?? null,
'stage_id' => $stage->id ?? null,
]);
$activity->refresh();
$eventDispatcher->dispatch(new ActivityProspectAdded(
activity: $activity,
eventSource: 'match-crm-data'
));
if ($activity->getProspectName() !== null) {
$activity->setTitleFromCallData($this->call);
/** @var Participant $prospectParticipant */
$prospectParticipant = $activity
->participants()
->where(function (Builder $query) use ($activity) {
$query
->whereNull('user_id')
->orWhere('user_id', '!=', $activity->getUserId())
;
})
->first()
;
$activity->updateParticipantCrmData($crmObjects, $prospectParticipant);
}
$this->logMessage('Activity updated');
$this->triggerSummaryPushIfReady($activity, $eventDispatcher);
}
private function triggerSummaryPushIfReady(Activity $activity, Dispatcher $eventDispatcher): void
{
if (! $activity->hasTranscriptionId()) {
return;
}
if ($activity->hasProspectActivitySummaryLog()) {
$this->logMessage('Summary already sent to prospect, skipping summary push after CRM matching');
return;
}
$this->logMessage('Triggering summary push after CRM matching');
$eventDispatcher->dispatch(new TranscriptionAiSummaryReadyEvent($activity->getUuid()));
}
private function initialiseCrmService(Activity $activity, ProviderRegistry $providerRegistry): void
{
$this->team = $activity->getUser()->getTeam();
$crmProviderName = $this->team->getCrmConfiguration()->getProviderName();
$crmService = $providerRegistry->get($crmProviderName);
$crmService->setUser($this->team->getOwner());
$this->crmService = $crmService;
$this->logContext['team'] = $this->team->getSlug();
$this->logContext['team_id'] = $this->team->getId();
}
private function logMessage(string $message): void
{
$this->logger->info(sprintf('[MatchCrmData] %s', $message), $this->logContext);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – MatchCrmData.php
|
NULL
|
6322
|