|
6250
|
NULL
|
0
|
2026-05-07T18:04:43.914092+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-07/1778 /Users/lukas/.screenpipe/data/data/2026-05-07/1778177083914_m2.jpg...
|
PhpStorm
|
faVsco.js – MatchActivityCrmData.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"master, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.040226065,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: master<br/>Some incoming commits are not fetched<br/>","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.8081782,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"bounds":{"left":0.8234708,"top":0.019952115,"width":0.09208777,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AskJiminnyReportActivityServiceTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","depth":4,"bounds":{"left":0.4401596,"top":0.06624102,"width":0.31615692,"height":0.91300875},"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"8","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.007978723,"height":0.0},"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.006981383,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.011968086,"top":0.047885075,"width":0.024268618,"height":0.024740623},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
2730807366803551277
|
6666527995115800880
|
idle
|
accessibility
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
6240
|
NULL
|
NULL
|
NULL
|
|
6249
|
257
|
12
|
2026-05-07T18:04:13.612888+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-07/1778 /Users/lukas/.screenpipe/data/data/2026-05-07/1778177053612_m1.jpg...
|
PhpStorm
|
faVsco.js – MatchActivityCrmData.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"master, menu","depth":5,"on_screen":true,"help_text":"Git Branch: master<br/>Some incoming commits are not fetched<br/>","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AskJiminnyReportActivityServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"8","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.016666668,"height":0.02111111},"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.015277778,"height":0.025555555},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.014583333,"height":0.025555555},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
2730807366803551277
|
6666527995115800880
|
idle
|
accessibility
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
6241
|
NULL
|
NULL
|
NULL
|
|
6248
|
258
|
11
|
2026-05-07T18:04:13.356551+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-07/1778 /Users/lukas/.screenpipe/data/data/2026-05-07/1778177053356_m2.jpg...
|
PhpStorm
|
faVsco.js – MatchActivityCrmData.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"master, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.040226065,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: master<br/>Some incoming commits are not fetched<br/>","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.8081782,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"bounds":{"left":0.8234708,"top":0.019952115,"width":0.09208777,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AskJiminnyReportActivityServiceTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","depth":4,"bounds":{"left":0.4401596,"top":0.06624102,"width":0.31615692,"height":0.91300875},"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"8","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.007978723,"height":0.0},"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.006981383,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.011968086,"top":0.047885075,"width":0.024268618,"height":0.024740623},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
2730807366803551277
|
6666527995115800880
|
idle
|
accessibility
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
6240
|
NULL
|
NULL
|
NULL
|
|
6247
|
257
|
11
|
2026-05-07T18:03:43.036439+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-07/1778 /Users/lukas/.screenpipe/data/data/2026-05-07/1778177023036_m1.jpg...
|
PhpStorm
|
faVsco.js – MatchActivityCrmData.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"master, menu","depth":5,"on_screen":true,"help_text":"Git Branch: master<br/>Some incoming commits are not fetched<br/>","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AskJiminnyReportActivityServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"8","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.016666668,"height":0.02111111},"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.015277778,"height":0.025555555},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.014583333,"height":0.025555555},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
2730807366803551277
|
6666527995115800880
|
idle
|
accessibility
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
6241
|
NULL
|
NULL
|
NULL
|
|
6246
|
258
|
10
|
2026-05-07T18:03:42.780718+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-07/1778 /Users/lukas/.screenpipe/data/data/2026-05-07/1778177022780_m2.jpg...
|
PhpStorm
|
faVsco.js – MatchActivityCrmData.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"master, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.040226065,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: master<br/>Some incoming commits are not fetched<br/>","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.8081782,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"bounds":{"left":0.8234708,"top":0.019952115,"width":0.09208777,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AskJiminnyReportActivityServiceTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","depth":4,"bounds":{"left":0.4401596,"top":0.06624102,"width":0.31615692,"height":0.91300875},"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"8","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.007978723,"height":0.0},"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.006981383,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.011968086,"top":0.047885075,"width":0.024268618,"height":0.024740623},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
2730807366803551277
|
6666527995115800880
|
idle
|
accessibility
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
6240
|
NULL
|
NULL
|
NULL
|
|
6245
|
257
|
10
|
2026-05-07T18:03:12.408238+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-07/1778 /Users/lukas/.screenpipe/data/data/2026-05-07/1778176992408_m1.jpg...
|
PhpStorm
|
faVsco.js – MatchActivityCrmData.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"master, menu","depth":5,"on_screen":true,"help_text":"Git Branch: master<br/>Some incoming commits are not fetched<br/>","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AskJiminnyReportActivityServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"8","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.016666668,"height":0.02111111},"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.015277778,"height":0.025555555},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.014583333,"height":0.025555555},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
2730807366803551277
|
6666527995115800880
|
idle
|
accessibility
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
6241
|
NULL
|
NULL
|
NULL
|
|
6244
|
258
|
9
|
2026-05-07T18:03:12.212248+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-07/1778 /Users/lukas/.screenpipe/data/data/2026-05-07/1778176992212_m2.jpg...
|
PhpStorm
|
faVsco.js – MatchActivityCrmData.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"master, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.040226065,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: master<br/>Some incoming commits are not fetched<br/>","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.8081782,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"bounds":{"left":0.8234708,"top":0.019952115,"width":0.09208777,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AskJiminnyReportActivityServiceTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","depth":4,"bounds":{"left":0.4401596,"top":0.06624102,"width":0.31615692,"height":0.91300875},"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"8","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.007978723,"height":0.0},"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.006981383,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.011968086,"top":0.047885075,"width":0.024268618,"height":0.024740623},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
2730807366803551277
|
6666527995115800880
|
idle
|
accessibility
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
6240
|
NULL
|
NULL
|
NULL
|
|
6243
|
257
|
9
|
2026-05-07T18:02:41.842584+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-07/1778 /Users/lukas/.screenpipe/data/data/2026-05-07/1778176961842_m1.jpg...
|
PhpStorm
|
faVsco.js – MatchActivityCrmData.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"master, menu","depth":5,"on_screen":true,"help_text":"Git Branch: master<br/>Some incoming commits are not fetched<br/>","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AskJiminnyReportActivityServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"8","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.016666668,"height":0.02111111},"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.015277778,"height":0.025555555},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.014583333,"height":0.025555555},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
2730807366803551277
|
6666527995115800880
|
idle
|
accessibility
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
6241
|
NULL
|
NULL
|
NULL
|
|
6242
|
258
|
8
|
2026-05-07T18:02:41.655373+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-07/1778 /Users/lukas/.screenpipe/data/data/2026-05-07/1778176961655_m2.jpg...
|
PhpStorm
|
faVsco.js – MatchActivityCrmData.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"master, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.040226065,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: master<br/>Some incoming commits are not fetched<br/>","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.8081782,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"bounds":{"left":0.8234708,"top":0.019952115,"width":0.09208777,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AskJiminnyReportActivityServiceTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","depth":4,"bounds":{"left":0.4401596,"top":0.06624102,"width":0.31615692,"height":0.91300875},"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"8","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.007978723,"height":0.0},"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.006981383,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.011968086,"top":0.047885075,"width":0.024268618,"height":0.024740623},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
2730807366803551277
|
6666527995115800880
|
idle
|
accessibility
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
6240
|
NULL
|
NULL
|
NULL
|
|
6241
|
257
|
8
|
2026-05-07T18:02:11.282578+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-07/1778 /Users/lukas/.screenpipe/data/data/2026-05-07/1778176931282_m1.jpg...
|
PhpStorm
|
faVsco.js – MatchActivityCrmData.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"master, menu","depth":5,"on_screen":true,"help_text":"Git Branch: master<br/>Some incoming commits are not fetched<br/>","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AskJiminnyReportActivityServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"8","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.016666668,"height":0.02111111},"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.015277778,"height":0.025555555},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.014583333,"height":0.025555555},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
2730807366803551277
|
6666527995115800880
|
idle
|
accessibility
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
6240
|
258
|
7
|
2026-05-07T18:02:11.084121+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-07/1778 /Users/lukas/.screenpipe/data/data/2026-05-07/1778176931084_m2.jpg...
|
PhpStorm
|
faVsco.js – MatchActivityCrmData.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"master, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.040226065,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: master<br/>Some incoming commits are not fetched<br/>","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.8081782,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"bounds":{"left":0.8234708,"top":0.019952115,"width":0.09208777,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AskJiminnyReportActivityServiceTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","depth":4,"bounds":{"left":0.4401596,"top":0.06624102,"width":0.31615692,"height":0.91300875},"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"8","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.007978723,"height":0.0},"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.006981383,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.011968086,"top":0.047885075,"width":0.024268618,"height":0.024740623},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
2730807366803551277
|
6666527995115800880
|
idle
|
accessibility
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
6239
|
257
|
7
|
2026-05-07T18:01:40.446529+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-07/1778 /Users/lukas/.screenpipe/data/data/2026-05-07/1778176900446_m1.jpg...
|
PhpStorm
|
faVsco.js – MatchActivityCrmData.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"master, menu","depth":5,"on_screen":true,"help_text":"Git Branch: master<br/>Some incoming commits are not fetched<br/>","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AskJiminnyReportActivityServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"8","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.016666668,"height":0.02111111},"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.015277778,"height":0.025555555},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.014583333,"height":0.025555555},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
8932599970913322963
|
6666527977935931696
|
click
|
accessibility
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected...
|
6237
|
NULL
|
NULL
|
NULL
|
|
6238
|
258
|
6
|
2026-05-07T18:01:40.349381+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-07/1778 /Users/lukas/.screenpipe/data/data/2026-05-07/1778176900349_m2.jpg...
|
PhpStorm
|
faVsco.js – MatchActivityCrmData.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"master, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.040226065,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: master<br/>Some incoming commits are not fetched<br/>","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.8081782,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"bounds":{"left":0.8234708,"top":0.019952115,"width":0.09208777,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AskJiminnyReportActivityServiceTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","depth":4,"bounds":{"left":0.4401596,"top":0.06624102,"width":0.31615692,"height":0.91300875},"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"8","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.007978723,"height":0.0},"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.006981383,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.011968086,"top":0.047885075,"width":0.024268618,"height":0.024740623},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
2730807366803551277
|
6666527995115800880
|
click
|
accessibility
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
6236
|
NULL
|
NULL
|
NULL
|
|
6237
|
257
|
6
|
2026-05-07T18:01:25.297008+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-07/1778 /Users/lukas/.screenpipe/data/data/2026-05-07/1778176885297_m1.jpg...
|
PhpStorm
|
faVsco.js – MatchActivityCrmData.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
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:...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"master, menu","depth":5,"on_screen":true,"help_text":"Git Branch: master<br/>Some incoming commits are not fetched<br/>","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AskJiminnyReportActivityServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-6980843599647952031
|
-3718843084275741232
|
click
|
accessibility
|
NULL
|
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:...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
6236
|
258
|
5
|
2026-05-07T18:01:25.216358+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-07/1778 /Users/lukas/.screenpipe/data/data/2026-05-07/1778176885216_m2.jpg...
|
PhpStorm
|
faVsco.js – MatchActivityCrmData.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
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...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"master, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.040226065,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: master<br/>Some incoming commits are not fetched<br/>","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.8081782,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"bounds":{"left":0.8234708,"top":0.019952115,"width":0.09208777,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AskJiminnyReportActivityServiceTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","depth":4,"bounds":{"left":0.4401596,"top":0.06624102,"width":0.31615692,"height":0.91300875},"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
391606455048542749
|
-3718843084275216944
|
click
|
accessibility
|
NULL
|
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...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
6235
|
257
|
5
|
2026-05-07T18:01:18.884594+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-07/1778 /Users/lukas/.screenpipe/data/data/2026-05-07/1778176878884_m1.jpg...
|
PhpStorm
|
faVsco.js – MatchActivityCrmData.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"master, menu","depth":5,"on_screen":true,"help_text":"Git Branch: master<br/>Some incoming commits are not fetched<br/>","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AskJiminnyReportActivityServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"8","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.016666668,"height":0.02111111},"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.015277778,"height":0.025555555},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.014583333,"height":0.025555555},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
2730807366803551277
|
6666527995115800880
|
click
|
accessibility
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
6233
|
NULL
|
NULL
|
NULL
|
|
6234
|
258
|
4
|
2026-05-07T18:01:11.132438+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-07/1778 /Users/lukas/.screenpipe/data/data/2026-05-07/1778176871132_m2.jpg...
|
PhpStorm
|
faVsco.js – MatchActivityCrmData.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"master, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.040226065,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: master<br/>Some incoming commits are not fetched<br/>","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.8081782,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"bounds":{"left":0.8234708,"top":0.019952115,"width":0.09208777,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AskJiminnyReportActivityServiceTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","depth":4,"bounds":{"left":0.4401596,"top":0.06624102,"width":0.31615692,"height":0.91300875},"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"8","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.007978723,"height":0.0},"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.006981383,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.011968086,"top":0.047885075,"width":0.024268618,"height":0.024740623},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
2730807366803551277
|
6666527995115800880
|
click
|
accessibility
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
6232
|
NULL
|
NULL
|
NULL
|
|
6233
|
257
|
4
|
2026-05-07T18:01:11.050068+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-07/1778 /Users/lukas/.screenpipe/data/data/2026-05-07/1778176871050_m1.jpg...
|
PhpStorm
|
faVsco.js – MatchActivityCrmData.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
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...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"master, menu","depth":5,"on_screen":true,"help_text":"Git Branch: master<br/>Some incoming commits are not fetched<br/>","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AskJiminnyReportActivityServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
391606455048542749
|
-3718843084275216944
|
click
|
accessibility
|
NULL
|
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...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
6232
|
258
|
3
|
2026-05-07T18:01:00.918084+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-07/1778 /Users/lukas/.screenpipe/data/data/2026-05-07/1778176860918_m2.jpg...
|
PhpStorm
|
faVsco.js – MatchActivityCrmData.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"master, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.040226065,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: master<br/>Some incoming commits are not fetched<br/>","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.8081782,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"bounds":{"left":0.8234708,"top":0.019952115,"width":0.09208777,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AskJiminnyReportActivityServiceTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","depth":4,"bounds":{"left":0.4401596,"top":0.06624102,"width":0.31615692,"height":0.91300875},"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"8","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.007978723,"height":0.0},"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.006981383,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.011968086,"top":0.047885075,"width":0.024268618,"height":0.024740623},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
4089589782326220475
|
6684542376445413808
|
click
|
accessibility
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
6231
|
258
|
2
|
2026-05-07T18:00:54.860063+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-07/1778 /Users/lukas/.screenpipe/data/data/2026-05-07/1778176854860_m2.jpg...
|
PhpStorm
|
faVsco.js – MatchActivityCrmData.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"master, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.040226065,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: master<br/>Some incoming commits are not fetched<br/>","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.8081782,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"bounds":{"left":0.8234708,"top":0.019952115,"width":0.09208777,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AskJiminnyReportActivityServiceTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","depth":4,"bounds":{"left":0.4401596,"top":0.06624102,"width":0.31615692,"height":0.91300875},"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"8","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.007978723,"height":0.0},"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.006981383,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.011968086,"top":0.047885075,"width":0.024268618,"height":0.024740623},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-1678650113624585151
|
6666457609191754032
|
click
|
accessibility
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options...
|
6203
|
NULL
|
NULL
|
NULL
|
|
6230
|
257
|
3
|
2026-05-07T18:00:48.203252+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-07/1778 /Users/lukas/.screenpipe/data/data/2026-05-07/1778176848203_m1.jpg...
|
PhpStorm
|
faVsco.js – MatchActivityCrmData.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"master, menu","depth":5,"on_screen":true,"help_text":"Git Branch: master<br/>Some incoming commits are not fetched<br/>","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AskJiminnyReportActivityServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"8","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.016666668,"height":0.02111111},"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.015277778,"height":0.025555555},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.014583333,"height":0.025555555},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
2730807366803551277
|
6666527995115800880
|
click
|
accessibility
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
6229
|
NULL
|
NULL
|
NULL
|
|
6229
|
257
|
2
|
2026-05-07T18:00:42.328604+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-07/1778 /Users/lukas/.screenpipe/data/data/2026-05-07/1778176842328_m1.jpg...
|
PhpStorm
|
faVsco.js – MatchActivityCrmData.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
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...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"master, menu","depth":5,"on_screen":true,"help_text":"Git Branch: master<br/>Some incoming commits are not fetched<br/>","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AskJiminnyReportActivityServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
391606455048542749
|
-3718843084275216944
|
click
|
accessibility
|
NULL
|
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...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
6228
|
258
|
1
|
2026-05-07T18:00:42.236466+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-07/1778 /Users/lukas/.screenpipe/data/data/2026-05-07/1778176842236_m2.jpg...
|
PhpStorm
|
faVsco.js – MatchActivityCrmData.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"master, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.040226065,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: master<br/>Some incoming commits are not fetched<br/>","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.8081782,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"bounds":{"left":0.8234708,"top":0.019952115,"width":0.09208777,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AskJiminnyReportActivityServiceTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","depth":4,"bounds":{"left":0.4401596,"top":0.06624102,"width":0.31615692,"height":0.91300875},"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"8","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.007978723,"height":0.0},"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.006981383,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.011968086,"top":0.047885075,"width":0.024268618,"height":0.024740623},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
2730807366803551277
|
6666527995115800880
|
click
|
accessibility
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
6203
|
NULL
|
NULL
|
NULL
|
|
6227
|
257
|
1
|
2026-05-07T18:00:16.042572+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-07/1778 /Users/lukas/.screenpipe/data/data/2026-05-07/1778176816042_m1.jpg...
|
PhpStorm
|
faVsco.js – MatchActivityCrmData.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
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());
}
}
}...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"master, menu","depth":5,"on_screen":true,"help_text":"Git Branch: master<br/>Some incoming commits are not fetched<br/>","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AskJiminnyReportActivityServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-6322393822977483
|
-2563669779787567664
|
click
|
accessibility
|
NULL
|
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());
}
}
}...
|
6216
|
NULL
|
NULL
|
NULL
|
|
6226
|
258
|
0
|
2026-05-07T18:00:11.294738+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-07/1778 /Users/lukas/.screenpipe/data/data/2026-05-07/1778176811294_m2.jpg...
|
PhpStorm
|
faVsco.js – MatchActivityCrmData.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"master, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.040226065,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: master<br/>Some incoming commits are not fetched<br/>","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.8081782,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"bounds":{"left":0.8234708,"top":0.019952115,"width":0.09208777,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AskJiminnyReportActivityServiceTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","depth":4,"bounds":{"left":0.4401596,"top":0.06624102,"width":0.31615692,"height":0.91300875},"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"8","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.007978723,"height":0.0},"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.006981383,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.011968086,"top":0.047885075,"width":0.024268618,"height":0.024740623},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
2730807366803551277
|
6666527995115800880
|
idle
|
accessibility
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
6203
|
NULL
|
NULL
|
NULL
|
|
6225
|
257
|
0
|
2026-05-07T18:00:10.157472+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-07/1778 /Users/lukas/.screenpipe/data/data/2026-05-07/1778176810157_m1.jpg...
|
PhpStorm
|
faVsco.js – MatchActivityCrmData.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"master, menu","depth":5,"on_screen":true,"help_text":"Git Branch: master<br/>Some incoming commits are not fetched<br/>","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AskJiminnyReportActivityServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"8","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.016666668,"height":0.02111111},"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.015277778,"height":0.025555555},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.014583333,"height":0.025555555},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
2730807366803551277
|
6666527995115800880
|
idle
|
accessibility
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
6216
|
NULL
|
NULL
|
NULL
|
|
6224
|
NULL
|
0
|
2026-05-07T17:59:40.655566+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-07/1778 /Users/lukas/.screenpipe/data/data/2026-05-07/1778176780655_m2.jpg...
|
PhpStorm
|
faVsco.js – MatchActivityCrmData.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"master, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.040226065,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: master<br/>Some incoming commits are not fetched<br/>","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.8081782,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"bounds":{"left":0.8234708,"top":0.019952115,"width":0.09208777,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AskJiminnyReportActivityServiceTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","depth":4,"bounds":{"left":0.4401596,"top":0.06624102,"width":0.31615692,"height":0.91300875},"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"8","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.007978723,"height":0.0},"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.006981383,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.011968086,"top":0.047885075,"width":0.024268618,"height":0.024740623},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
2730807366803551277
|
6666527995115800880
|
idle
|
accessibility
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
6203
|
NULL
|
NULL
|
NULL
|
|
6223
|
NULL
|
0
|
2026-05-07T17:59:39.744318+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-07/1778 /Users/lukas/.screenpipe/data/data/2026-05-07/1778176779744_m1.jpg...
|
PhpStorm
|
faVsco.js – MatchActivityCrmData.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"master, menu","depth":5,"on_screen":true,"help_text":"Git Branch: master<br/>Some incoming commits are not fetched<br/>","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AskJiminnyReportActivityServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"8","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.016666668,"height":0.02111111},"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.015277778,"height":0.025555555},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.014583333,"height":0.025555555},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
2730807366803551277
|
6666527995115800880
|
idle
|
accessibility
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
6216
|
NULL
|
NULL
|
NULL
|
|
6222
|
256
|
7
|
2026-05-07T17:59:10.130649+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-07/1778 /Users/lukas/.screenpipe/data/data/2026-05-07/1778176750130_m2.jpg...
|
PhpStorm
|
faVsco.js – MatchActivityCrmData.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"master, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.040226065,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: master<br/>Some incoming commits are not fetched<br/>","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.8081782,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"bounds":{"left":0.8234708,"top":0.019952115,"width":0.09208777,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AskJiminnyReportActivityServiceTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","depth":4,"bounds":{"left":0.4401596,"top":0.06624102,"width":0.31615692,"height":0.91300875},"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"8","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.007978723,"height":0.0},"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.006981383,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.011968086,"top":0.047885075,"width":0.024268618,"height":0.024740623},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
2730807366803551277
|
6666527995115800880
|
idle
|
accessibility
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
6203
|
NULL
|
NULL
|
NULL
|
|
6221
|
255
|
8
|
2026-05-07T17:59:09.329057+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-07/1778 /Users/lukas/.screenpipe/data/data/2026-05-07/1778176749329_m1.jpg...
|
PhpStorm
|
faVsco.js – MatchActivityCrmData.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"master, menu","depth":5,"on_screen":true,"help_text":"Git Branch: master<br/>Some incoming commits are not fetched<br/>","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AskJiminnyReportActivityServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"8","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.016666668,"height":0.02111111},"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.015277778,"height":0.025555555},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.014583333,"height":0.025555555},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
2730807366803551277
|
6666527995115800880
|
idle
|
accessibility
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
6216
|
NULL
|
NULL
|
NULL
|
|
6220
|
256
|
6
|
2026-05-07T17:58:39.288358+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-07/1778 /Users/lukas/.screenpipe/data/data/2026-05-07/1778176719288_m2.jpg...
|
PhpStorm
|
faVsco.js – MatchActivityCrmData.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"master, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.040226065,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: master<br/>Some incoming commits are not fetched<br/>","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.8081782,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"bounds":{"left":0.8234708,"top":0.019952115,"width":0.09208777,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AskJiminnyReportActivityServiceTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","depth":4,"bounds":{"left":0.4401596,"top":0.06624102,"width":0.31615692,"height":0.91300875},"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"8","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.007978723,"height":0.0},"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.006981383,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.011968086,"top":0.047885075,"width":0.024268618,"height":0.024740623},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
2730807366803551277
|
6666527995115800880
|
idle
|
accessibility
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
6203
|
NULL
|
NULL
|
NULL
|
|
6219
|
255
|
7
|
2026-05-07T17:58:38.867877+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-07/1778 /Users/lukas/.screenpipe/data/data/2026-05-07/1778176718867_m1.jpg...
|
PhpStorm
|
faVsco.js – MatchActivityCrmData.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"master, menu","depth":5,"on_screen":true,"help_text":"Git Branch: master<br/>Some incoming commits are not fetched<br/>","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AskJiminnyReportActivityServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"8","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.016666668,"height":0.02111111},"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.015277778,"height":0.025555555},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.014583333,"height":0.025555555},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
2730807366803551277
|
6666527995115800880
|
idle
|
accessibility
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
6216
|
NULL
|
NULL
|
NULL
|
|
6218
|
255
|
6
|
2026-05-07T17:57:42.155515+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-07/1778 /Users/lukas/.screenpipe/data/data/2026-05-07/1778176662155_m1.jpg...
|
PhpStorm
|
faVsco.js – MatchActivityCrmData.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"master, menu","depth":5,"on_screen":true,"help_text":"Git Branch: master<br/>Some incoming commits are not fetched<br/>","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AskJiminnyReportActivityServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"8","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.016666668,"height":0.02111111},"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.015277778,"height":0.025555555},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.014583333,"height":0.025555555},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
2730807366803551277
|
6666527995115800880
|
idle
|
accessibility
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
6216
|
NULL
|
NULL
|
NULL
|
|
6217
|
256
|
5
|
2026-05-07T17:57:41.742504+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-07/1778 /Users/lukas/.screenpipe/data/data/2026-05-07/1778176661742_m2.jpg...
|
PhpStorm
|
faVsco.js – MatchActivityCrmData.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"master, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.040226065,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: master<br/>Some incoming commits are not fetched<br/>","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.8081782,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"bounds":{"left":0.8234708,"top":0.019952115,"width":0.09208777,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AskJiminnyReportActivityServiceTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","depth":4,"bounds":{"left":0.4401596,"top":0.06624102,"width":0.31615692,"height":0.91300875},"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"8","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.007978723,"height":0.0},"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.006981383,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.011968086,"top":0.047885075,"width":0.024268618,"height":0.024740623},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
2730807366803551277
|
6666527995115800880
|
idle
|
accessibility
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
6203
|
NULL
|
NULL
|
NULL
|
|
6216
|
255
|
5
|
2026-05-07T17:57:11.657532+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-07/1778 /Users/lukas/.screenpipe/data/data/2026-05-07/1778176631657_m1.jpg...
|
PhpStorm
|
faVsco.js – MatchActivityCrmData.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"master, menu","depth":5,"on_screen":true,"help_text":"Git Branch: master<br/>Some incoming commits are not fetched<br/>","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AskJiminnyReportActivityServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"8","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.016666668,"height":0.02111111},"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.015277778,"height":0.025555555},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.014583333,"height":0.025555555},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
2730807366803551277
|
6666527995115800880
|
idle
|
accessibility
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
6215
|
256
|
4
|
2026-05-07T17:57:11.088288+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-07/1778 /Users/lukas/.screenpipe/data/data/2026-05-07/1778176631088_m2.jpg...
|
PhpStorm
|
faVsco.js – MatchActivityCrmData.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"master, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.040226065,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: master<br/>Some incoming commits are not fetched<br/>","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.8081782,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"bounds":{"left":0.8234708,"top":0.019952115,"width":0.09208777,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AskJiminnyReportActivityServiceTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","depth":4,"bounds":{"left":0.4401596,"top":0.06624102,"width":0.31615692,"height":0.91300875},"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"8","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.007978723,"height":0.0},"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.006981383,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.011968086,"top":0.047885075,"width":0.024268618,"height":0.024740623},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
2730807366803551277
|
6666527995115800880
|
idle
|
accessibility
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
6203
|
NULL
|
NULL
|
NULL
|
|
6214
|
255
|
4
|
2026-05-07T17:56:41.041441+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-07/1778 /Users/lukas/.screenpipe/data/data/2026-05-07/1778176601041_m1.jpg...
|
PhpStorm
|
faVsco.js – MatchActivityCrmData.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
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
8
Previous Highlighted Error...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"master, menu","depth":5,"on_screen":true,"help_text":"Git Branch: master<br/>Some incoming commits are not fetched<br/>","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AskJiminnyReportActivityServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"8","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.016666668,"height":0.02111111},"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.015277778,"height":0.025555555},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-5898712366205983826
|
-3718843092865675824
|
click
|
accessibility
|
NULL
|
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
8
Previous Highlighted Error...
|
6202
|
NULL
|
NULL
|
NULL
|
|
6213
|
256
|
3
|
2026-05-07T17:56:40.517159+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-07/1778 /Users/lukas/.screenpipe/data/data/2026-05-07/1778176600517_m2.jpg...
|
PhpStorm
|
faVsco.js – MatchActivityCrmData.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"master, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.040226065,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: master<br/>Some incoming commits are not fetched<br/>","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.8081782,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"bounds":{"left":0.8234708,"top":0.019952115,"width":0.09208777,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AskJiminnyReportActivityServiceTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","depth":4,"bounds":{"left":0.4401596,"top":0.06624102,"width":0.31615692,"height":0.91300875},"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"8","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.007978723,"height":0.0},"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.006981383,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.011968086,"top":0.047885075,"width":0.024268618,"height":0.024740623},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
2730807366803551277
|
6666527995115800880
|
idle
|
accessibility
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
6203
|
NULL
|
NULL
|
NULL
|
|
6212
|
255
|
3
|
2026-05-07T17:56:39.531375+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-07/1778 /Users/lukas/.screenpipe/data/data/2026-05-07/1778176599531_m1.jpg...
|
PhpStorm
|
faVsco.js – MatchActivityCrmData.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"master, menu","depth":5,"on_screen":true,"help_text":"Git Branch: master<br/>Some incoming commits are not fetched<br/>","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AskJiminnyReportActivityServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"8","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.016666668,"height":0.02111111},"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.015277778,"height":0.025555555},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.014583333,"height":0.025555555},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
2730807366803551277
|
6666527995115800880
|
idle
|
accessibility
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
6202
|
NULL
|
NULL
|
NULL
|
|
6211
|
256
|
2
|
2026-05-07T17:56:09.991665+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-07/1778 /Users/lukas/.screenpipe/data/data/2026-05-07/1778176569991_m2.jpg...
|
PhpStorm
|
faVsco.js – MatchActivityCrmData.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"master, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.040226065,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: master<br/>Some incoming commits are not fetched<br/>","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.8081782,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"bounds":{"left":0.8234708,"top":0.019952115,"width":0.09208777,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AskJiminnyReportActivityServiceTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","depth":4,"bounds":{"left":0.4401596,"top":0.06624102,"width":0.31615692,"height":0.91300875},"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"8","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.007978723,"height":0.0},"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.006981383,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.011968086,"top":0.047885075,"width":0.024268618,"height":0.024740623},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
2730807366803551277
|
6666527995115800880
|
idle
|
accessibility
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
6203
|
NULL
|
NULL
|
NULL
|
|
6210
|
255
|
2
|
2026-05-07T17:56:09.087189+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-07/1778 /Users/lukas/.screenpipe/data/data/2026-05-07/1778176569087_m1.jpg...
|
PhpStorm
|
faVsco.js – MatchActivityCrmData.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"master, menu","depth":5,"on_screen":true,"help_text":"Git Branch: master<br/>Some incoming commits are not fetched<br/>","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AskJiminnyReportActivityServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"8","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.016666668,"height":0.02111111},"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.015277778,"height":0.025555555},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.014583333,"height":0.025555555},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
2730807366803551277
|
6666527995115800880
|
idle
|
accessibility
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
6202
|
NULL
|
NULL
|
NULL
|
|
6209
|
256
|
1
|
2026-05-07T17:55:39.252127+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-07/1778 /Users/lukas/.screenpipe/data/data/2026-05-07/1778176539252_m2.jpg...
|
PhpStorm
|
faVsco.js – MatchActivityCrmData.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"master, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.040226065,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: master<br/>Some incoming commits are not fetched<br/>","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.8081782,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"bounds":{"left":0.8234708,"top":0.019952115,"width":0.09208777,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AskJiminnyReportActivityServiceTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","depth":4,"bounds":{"left":0.4401596,"top":0.06624102,"width":0.31615692,"height":0.91300875},"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"8","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.007978723,"height":0.0},"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.006981383,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.011968086,"top":0.047885075,"width":0.024268618,"height":0.024740623},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
2730807366803551277
|
6666527995115800880
|
idle
|
accessibility
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
6203
|
NULL
|
NULL
|
NULL
|
|
6208
|
255
|
1
|
2026-05-07T17:55:38.625132+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-07/1778 /Users/lukas/.screenpipe/data/data/2026-05-07/1778176538625_m1.jpg...
|
PhpStorm
|
faVsco.js – MatchActivityCrmData.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"master, menu","depth":5,"on_screen":true,"help_text":"Git Branch: master<br/>Some incoming commits are not fetched<br/>","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AskJiminnyReportActivityServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"8","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.016666668,"height":0.02111111},"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.015277778,"height":0.025555555},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.014583333,"height":0.025555555},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
2730807366803551277
|
6666527995115800880
|
idle
|
accessibility
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
6202
|
NULL
|
NULL
|
NULL
|
|
6207
|
256
|
0
|
2026-05-07T17:55:08.740284+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-07/1778 /Users/lukas/.screenpipe/data/data/2026-05-07/1778176508740_m2.jpg...
|
PhpStorm
|
faVsco.js – MatchActivityCrmData.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"master, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.040226065,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: master<br/>Some incoming commits are not fetched<br/>","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.8081782,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"bounds":{"left":0.8234708,"top":0.019952115,"width":0.09208777,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AskJiminnyReportActivityServiceTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","depth":4,"bounds":{"left":0.4401596,"top":0.06624102,"width":0.31615692,"height":0.91300875},"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"8","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.007978723,"height":0.0},"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.006981383,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.011968086,"top":0.047885075,"width":0.024268618,"height":0.024740623},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
2730807366803551277
|
6666527995115800880
|
idle
|
accessibility
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
6203
|
NULL
|
NULL
|
NULL
|
|
6206
|
255
|
0
|
2026-05-07T17:55:08.108288+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-07/1778 /Users/lukas/.screenpipe/data/data/2026-05-07/1778176508108_m1.jpg...
|
PhpStorm
|
faVsco.js – MatchActivityCrmData.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"master, menu","depth":5,"on_screen":true,"help_text":"Git Branch: master<br/>Some incoming commits are not fetched<br/>","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AskJiminnyReportActivityServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"8","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.016666668,"height":0.02111111},"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.015277778,"height":0.025555555},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.014583333,"height":0.025555555},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
2730807366803551277
|
6666527995115800880
|
idle
|
accessibility
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
6202
|
NULL
|
NULL
|
NULL
|
|
6205
|
NULL
|
0
|
2026-05-07T17:54:38.155203+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-07/1778 /Users/lukas/.screenpipe/data/data/2026-05-07/1778176478155_m2.jpg...
|
PhpStorm
|
faVsco.js – MatchActivityCrmData.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"master, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.040226065,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: master<br/>Some incoming commits are not fetched<br/>","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.8081782,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"bounds":{"left":0.8234708,"top":0.019952115,"width":0.09208777,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AskJiminnyReportActivityServiceTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","depth":4,"bounds":{"left":0.4401596,"top":0.06624102,"width":0.31615692,"height":0.91300875},"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"8","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.007978723,"height":0.0},"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.006981383,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.011968086,"top":0.047885075,"width":0.024268618,"height":0.024740623},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
2730807366803551277
|
6666527995115800880
|
idle
|
accessibility
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
6203
|
NULL
|
NULL
|
NULL
|
|
6204
|
NULL
|
0
|
2026-05-07T17:54:37.614026+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-07/1778 /Users/lukas/.screenpipe/data/data/2026-05-07/1778176477614_m1.jpg...
|
PhpStorm
|
faVsco.js – MatchActivityCrmData.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"master, menu","depth":5,"on_screen":true,"help_text":"Git Branch: master<br/>Some incoming commits are not fetched<br/>","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AskJiminnyReportActivityServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"8","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.016666668,"height":0.02111111},"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.015277778,"height":0.025555555},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.014583333,"height":0.025555555},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
2730807366803551277
|
6666527995115800880
|
idle
|
accessibility
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
6202
|
NULL
|
NULL
|
NULL
|
|
6203
|
254
|
9
|
2026-05-07T17:54:07.641217+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-07/1778 /Users/lukas/.screenpipe/data/data/2026-05-07/1778176447641_m2.jpg...
|
PhpStorm
|
faVsco.js – MatchActivityCrmData.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"master, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.040226065,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: master<br/>Some incoming commits are not fetched<br/>","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.8081782,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"bounds":{"left":0.8234708,"top":0.019952115,"width":0.09208777,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AskJiminnyReportActivityServiceTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","depth":4,"bounds":{"left":0.4401596,"top":0.06624102,"width":0.31615692,"height":0.91300875},"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"8","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.007978723,"height":0.0},"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.006981383,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.011968086,"top":0.047885075,"width":0.024268618,"height":0.024740623},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
2730807366803551277
|
6666527995115800880
|
idle
|
accessibility
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
6202
|
253
|
9
|
2026-05-07T17:54:07.227369+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-07/1778 /Users/lukas/.screenpipe/data/data/2026-05-07/1778176447227_m1.jpg...
|
PhpStorm
|
faVsco.js – MatchActivityCrmData.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"master, menu","depth":5,"on_screen":true,"help_text":"Git Branch: master<br/>Some incoming commits are not fetched<br/>","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AskJiminnyReportActivityServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"8","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.016666668,"height":0.02111111},"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.015277778,"height":0.025555555},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.014583333,"height":0.025555555},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\Crm;\n\nuse Exception;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Database\\Connection;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Jobs\\Middleware\\HandleHubspotRateLimit;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Repositories\\ActivityRepository;\nuse Jiminny\\Services\\Crm\\CrmActivityService;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Throwable;\n\nclass MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use SerializesModels;\n\n public int $tries = 3;\n\n private int $activityId;\n private ?Configuration $fromConfiguration;\n private bool $remoteSearch;\n\n public function middleware(): array\n {\n return [new HandleHubspotRateLimit()];\n }\n\n public function __construct(\n int $activityId,\n ?Configuration $fromConfiguration = null,\n bool $remoteSearch = false,\n ) {\n $this->activityId = $activityId;\n $this->fromConfiguration = $fromConfiguration;\n $this->remoteSearch = $remoteSearch;\n\n $this->onQueue(Constants::QUEUE_ANALYTICS_LOW);\n }\n\n public function uniqueId(): string\n {\n $configId = $this->fromConfiguration?->getId() ?? 0;\n $remote = $this->remoteSearch ? 'remote' : 'local';\n\n return \"$this->activityId:$configId:$remote\";\n }\n\n public function timeout(): int\n {\n return 300; // 5 minutes max execution time\n }\n\n public function uniqueFor(): int\n {\n return $this->timeout() + 60; // timeout + 1 minute buffer\n }\n\n public function backoff(): array\n {\n return [30, 90, 180];\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception|Throwable\n */\n public function handle(\n ActivityRepository $activityRepository,\n CrmActivityService $crmActivityService,\n Connection $connection,\n ): void {\n $activity = $activityRepository->findById($this->activityId);\n if ($activity === null) {\n throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');\n }\n\n try {\n $connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {\n Log::info('[MatchActivityCrmData] Starting CRM data matching', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'set_configuration' => $this->fromConfiguration?->getId(),\n 'old_state' => [\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ],\n ]);\n\n $this->resetCrmMappings($activity, $activityRepository);\n\n $this->switchCrmConfigurationIfNeeded($activity);\n\n $activity->refresh();\n\n $crmActivityService->updateCrmData(\n activity: $activity,\n remoteSearch: $this->remoteSearch,\n );\n\n $hasMatch = $activity->getLead() !== null\n || $activity->getContact() !== null\n || $activity->getAccount() !== null\n || $activity->getOpportunity() !== null;\n\n if ($hasMatch) {\n Log::info('[MatchActivityCrmData] Successfully matched CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'lead_id' => $activity->getLead()?->getId(),\n 'contact_id' => $activity->getContact()?->getId(),\n 'account_id' => $activity->getAccount()?->getId(),\n 'opportunity_id' => $activity->getOpportunity()?->getId(),\n 'stage_id' => $activity->getStage()?->getId(),\n ]);\n } else {\n Log::info('[MatchActivityCrmData] No CRM match found', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n ]);\n }\n });\n } catch (Throwable $e) {\n Log::error('[MatchActivityCrmData] Failed to match CRM data', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'exception' => $e->getMessage(),\n 'trace' => $e->getTraceAsString(),\n ]);\n\n throw $e;\n }\n }\n\n public function failed(Throwable $exception): void\n {\n Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [\n 'activity' => $this->activityId,\n 'remote_search' => $this->remoteSearch,\n 'from_configuration' => $this->fromConfiguration?->getId(),\n 'exception' => $exception->getMessage(),\n 'attempts' => $this->attempts(),\n ]);\n }\n\n private function resetCrmMappings(\n Activity $activity,\n ActivityRepository $activityRepository\n ): void {\n $activity->update([\n 'lead_id' => null,\n 'contact_id' => null,\n 'account_id' => null,\n 'opportunity_id' => null,\n 'stage_id' => null,\n ]);\n\n $participantsOldState = $activityRepository->getActivityParticipants($activity)\n ->map(function ($participant) {\n return [\n 'id' => $participant->id,\n 'user_id' => $participant->user_id,\n 'contact_id' => $participant->contact_id,\n 'lead_id' => $participant->lead_id,\n ];\n });\n\n if ($participantsOldState->isNotEmpty()) {\n Log::info('[MatchActivityCrmData] Participants old state', [\n 'activity' => $this->activityId,\n 'participants' => $participantsOldState->toArray(),\n ]);\n }\n\n $activity->participants()->update([\n 'user_id' => null,\n 'contact_id' => null,\n 'lead_id' => null,\n ]);\n }\n\n private function switchCrmConfigurationIfNeeded(Activity $activity): void\n {\n if ($this->fromConfiguration === null) {\n return;\n }\n\n if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {\n return;\n }\n\n Log::info('[MatchActivityCrmData] Switching CRM configuration', [\n 'activity' => $this->activityId,\n 'old_configuration' => $activity->getCrm()?->getId(),\n 'new_configuration' => $this->fromConfiguration->getId(),\n ]);\n\n $activity->update([\n 'crm_configuration_id' => $this->fromConfiguration->getId(),\n 'crm_provider_id' => null,\n ]);\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
2730807366803551277
|
6666527995115800880
|
idle
|
accessibility
|
NULL
|
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
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\Crm;
use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Connection;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Jobs\Job;
use Jiminny\Jobs\Middleware\HandleHubspotRateLimit;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Repositories\ActivityRepository;
use Jiminny\Services\Crm\CrmActivityService;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
class MatchActivityCrmData extends Job implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use SerializesModels;
public int $tries = 3;
private int $activityId;
private ?Configuration $fromConfiguration;
private bool $remoteSearch;
public function middleware(): array
{
return [new HandleHubspotRateLimit()];
}
public function __construct(
int $activityId,
?Configuration $fromConfiguration = null,
bool $remoteSearch = false,
) {
$this->activityId = $activityId;
$this->fromConfiguration = $fromConfiguration;
$this->remoteSearch = $remoteSearch;
$this->onQueue(Constants::QUEUE_ANALYTICS_LOW);
}
public function uniqueId(): string
{
$configId = $this->fromConfiguration?->getId() ?? 0;
$remote = $this->remoteSearch ? 'remote' : 'local';
return "$this->activityId:$configId:$remote";
}
public function timeout(): int
{
return 300; // 5 minutes max execution time
}
public function uniqueFor(): int
{
return $this->timeout() + 60; // timeout + 1 minute buffer
}
public function backoff(): array
{
return [30, 90, 180];
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception|Throwable
*/
public function handle(
ActivityRepository $activityRepository,
CrmActivityService $crmActivityService,
Connection $connection,
): void {
$activity = $activityRepository->findById($this->activityId);
if ($activity === null) {
throw new InvalidArgumentException('[MatchActivityCrmData] Cannot find activity.');
}
try {
$connection->transaction(function () use ($activity, $crmActivityService, $activityRepository) {
Log::info('[MatchActivityCrmData] Starting CRM data matching', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'set_configuration' => $this->fromConfiguration?->getId(),
'old_state' => [
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
],
]);
$this->resetCrmMappings($activity, $activityRepository);
$this->switchCrmConfigurationIfNeeded($activity);
$activity->refresh();
$crmActivityService->updateCrmData(
activity: $activity,
remoteSearch: $this->remoteSearch,
);
$hasMatch = $activity->getLead() !== null
|| $activity->getContact() !== null
|| $activity->getAccount() !== null
|| $activity->getOpportunity() !== null;
if ($hasMatch) {
Log::info('[MatchActivityCrmData] Successfully matched CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'lead_id' => $activity->getLead()?->getId(),
'contact_id' => $activity->getContact()?->getId(),
'account_id' => $activity->getAccount()?->getId(),
'opportunity_id' => $activity->getOpportunity()?->getId(),
'stage_id' => $activity->getStage()?->getId(),
]);
} else {
Log::info('[MatchActivityCrmData] No CRM match found', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
]);
}
});
} catch (Throwable $e) {
Log::error('[MatchActivityCrmData] Failed to match CRM data', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
public function failed(Throwable $exception): void
{
Log::error('[MatchActivityCrmData] Job permanently failed after all retries', [
'activity' => $this->activityId,
'remote_search' => $this->remoteSearch,
'from_configuration' => $this->fromConfiguration?->getId(),
'exception' => $exception->getMessage(),
'attempts' => $this->attempts(),
]);
}
private function resetCrmMappings(
Activity $activity,
ActivityRepository $activityRepository
): void {
$activity->update([
'lead_id' => null,
'contact_id' => null,
'account_id' => null,
'opportunity_id' => null,
'stage_id' => null,
]);
$participantsOldState = $activityRepository->getActivityParticipants($activity)
->map(function ($participant) {
return [
'id' => $participant->id,
'user_id' => $participant->user_id,
'contact_id' => $participant->contact_id,
'lead_id' => $participant->lead_id,
];
});
if ($participantsOldState->isNotEmpty()) {
Log::info('[MatchActivityCrmData] Participants old state', [
'activity' => $this->activityId,
'participants' => $participantsOldState->toArray(),
]);
}
$activity->participants()->update([
'user_id' => null,
'contact_id' => null,
'lead_id' => null,
]);
}
private function switchCrmConfigurationIfNeeded(Activity $activity): void
{
if ($this->fromConfiguration === null) {
return;
}
if ($activity->getCrm()?->getId() === $this->fromConfiguration->getId()) {
return;
}
Log::info('[MatchActivityCrmData] Switching CRM configuration', [
'activity' => $this->activityId,
'old_configuration' => $activity->getCrm()?->getId(),
'new_configuration' => $this->fromConfiguration->getId(),
]);
$activity->update([
'crm_configuration_id' => $this->fromConfiguration->getId(),
'crm_provider_id' => null,
]);
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
6201
|
254
|
8
|
2026-05-07T17:53:36.709481+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-07/1778 /Users/lukas/.screenpipe/data/data/2026-05-07/1778176416709_m2.jpg...
|
PhpStorm
|
faVsco.js – MatchActivityCrmData.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
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:...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"master, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.040226065,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: master<br/>Some incoming commits are not fetched<br/>","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.8081782,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"bounds":{"left":0.8234708,"top":0.019952115,"width":0.09208777,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AskJiminnyReportActivityServiceTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","depth":4,"bounds":{"left":0.4401596,"top":0.06624102,"width":0.31615692,"height":0.91300875},"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Utility\\Service;\n\nuse Illuminate\\Cache\\RateLimiter;\nuse Jiminny\\Contracts\\Http\\RateLimited;\nuse Jiminny\\Contracts\\Http\\RateLimitInterface;\n\nclass ProviderRateLimiter\n{\n protected RateLimiter $rateLimiter;\n\n public function __construct(RateLimiter $rateLimiter)\n {\n $this->rateLimiter = $rateLimiter;\n }\n\n public function canMakeRequest(RateLimited $provider): bool\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $key = $rateLimit->getKey();\n\n if ($this->rateLimiter->tooManyAttempts($key, $rateLimit->getQuota())) {\n return false;\n }\n }\n\n return true;\n }\n\n public function requestAvailableIn(RateLimited $provider): int\n {\n return $provider->getRateLimits()->isNotEmpty()\n ? $provider->getRateLimits()\n ->map(fn (RateLimitInterface $rateLimit): int => $this->rateLimiter->availableIn($rateLimit->getKey()))\n ->max()\n : 0\n ;\n }\n\n public function incrementRequestCount(RateLimited $provider): void\n {\n /** @var RateLimitInterface $rateLimit */\n foreach ($provider->getRateLimits() as $rateLimit) {\n $this->rateLimiter->hit($rateLimit->getKey(), $rateLimit->getWindow());\n }\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-6980843599647952031
|
-3718843084275741232
|
click
|
accessibility
|
NULL
|
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:...
|
6181
|
NULL
|
NULL
|
NULL
|