|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
6372
|
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
|
6372
|
|
6373
|
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
|
6373
|
|
6374
|
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
|
6374
|
|
6375
|
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
|
6375
|
|
6376
|
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
|
6376
|
|
6377
|
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
|
6377
|
|
6378
|
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
|
6378
|