|
49663
|
1766
|
59
|
2026-05-18T06:51:28.197115+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779087088197_m1.jpg...
|
PhpStorm
|
faVsco.js – TestPipedriveOfficialSdkCommand.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Notion CalendarEditViewWindowHelpDaily - Platform Notion CalendarEditViewWindowHelpDaily - Platform • now100% L2meet.google.com/agt-teir-cwt?authuser=lukas.kovalik%40jiminny.comNikolay Yankov (Presenting)+18X JimE3 Prom3 Atteni83 мCP - x(a Sche x• Curso x|* Ciauo xCostXoS[URL_WITH_CREDENTIALS] Mon 18 May 9:51L Al BookmarxsSteliyan GeorgievNikolay IvanovISSUE SOOJS9:51 AM | Daily - Platform...Lukas Kovalik5:02Mon 18 May 9:51:27Nikolay YankovAneliya Angelova...
|
NULL
|
1223267510637859777
|
NULL
|
visual_change
|
ocr
|
NULL
|
Notion CalendarEditViewWindowHelpDaily - Platform Notion CalendarEditViewWindowHelpDaily - Platform • now100% L2meet.google.com/agt-teir-cwt?authuser=lukas.kovalik%40jiminny.comNikolay Yankov (Presenting)+18X JimE3 Prom3 Atteni83 мCP - x(a Sche x• Curso x|* Ciauo xCostXoS[URL_WITH_CREDENTIALS] Mon 18 May 9:51L Al BookmarxsSteliyan GeorgievNikolay IvanovISSUE SOOJS9:51 AM | Daily - Platform...Lukas Kovalik5:02Mon 18 May 9:51:27Nikolay YankovAneliya Angelova...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
49662
|
1766
|
58
|
2026-05-18T06:51:22.153526+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779087082153_m1.jpg...
|
PhpStorm
|
faVsco.js – TestPipedriveOfficialSdkCommand.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Notion CalendarEditViewWindowHelpDaily - Platform Notion CalendarEditViewWindowHelpDaily - Platform • now100% L8• Mon 18 May 9:51:21meet.google.com/agt-teir-cwt?authuser=lukas.kovalik%40jiminny.comNikolay Yankov (Presenting)+Cr our18X JiminE83 Prom3 AtteniE3 MCP(a Sche x© Curso x|* Cauo x*04Q0CostXouminxQ мсP3 Pipel© S[URL_WITH_CREDENTIALS] Mon 18 May 9:51L Al BookmarxsSteliyan GeorgievNikolay YankovNikolay vanovAneliya Angelova9:51 AM | Daily - Platform...Lukas Kovalik4:56...
|
NULL
|
4343548740857816594
|
NULL
|
visual_change
|
ocr
|
NULL
|
Notion CalendarEditViewWindowHelpDaily - Platform Notion CalendarEditViewWindowHelpDaily - Platform • now100% L8• Mon 18 May 9:51:21meet.google.com/agt-teir-cwt?authuser=lukas.kovalik%40jiminny.comNikolay Yankov (Presenting)+Cr our18X JiminE83 Prom3 AtteniE3 MCP(a Sche x© Curso x|* Cauo x*04Q0CostXouminxQ мсP3 Pipel© S[URL_WITH_CREDENTIALS] Mon 18 May 9:51L Al BookmarxsSteliyan GeorgievNikolay YankovNikolay vanovAneliya Angelova9:51 AM | Daily - Platform...Lukas Kovalik4:56...
|
49661
|
NULL
|
NULL
|
NULL
|
|
49661
|
1766
|
57
|
2026-05-18T06:51:19.136592+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779087079136_m1.jpg...
|
PhpStorm
|
faVsco.js – TestPipedriveOfficialSdkCommand.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Notion CalendarEditViewWindowHelpmeet.google.com/a Notion CalendarEditViewWindowHelpmeet.google.com/agt-teir-cwt?authuser=lukas.kovalik%40jiminny.com+18Nikolay Yankov (Presenting)Flex JimE3 Prom0 AtteniE3 MCP(a Sche xo Curso X * Cauo XCostXhttps://fiminny.atlassian.net/jra/software/c/projectsJJY/boards/37?selectedissue=JY-2083523 SSHE Datadog* Claudel3 CircieClA SentryPlatform Team %.Q Search boardS00P J-20S8 W JT-298SSMAOTOKONote: here you can find example prompts for testing - ® Prompts and SkllsNotify the user d a~ Subtasksdeleted but is used in AJ…..AJ REPORTSBacklogД Jy-200763 .* =0WorkPriorityStor...Assignee% JY-20859 tool for get.deal= Medium8 Unassign...KATDURUEYNotify the user if a SS isdeieted but is used in AJReportAJREPORTSBacklogA JY-206152.5 a4+0 =% JY-20860 tool for search,deals= MediumUnassign...% JY-20861 tool for lisk_deal_options= Medium% Unassign...READY FOR DEVS* JY-20862 tool for get.deal activities= MediumUnassign..READY FOR DEVS= MediumUnassign...READY FOR DEVS9, JY-20884 add tools to tooks lisgl= MediumUpgrade BE lbraries - MayMAINTENANCE8Unassign...READY FOR DEVS"o JY-20805 manualtesung= MediumUnassign...READY FOR DEVS% JY-20874 describe tools data structures= MediumNxoiay T.E JY-19958Linked work itemsAnerowenevilsuggestionsAolinkeewewneAUTO-DETEGTED ACTIVITY TYРОcontuence contontBacklogI Jy-204702 a0=Prompts and Skills"0AGOQ мсР3 PipelTasks7insights & Coachin…C Dev@ 2In DevI Improve StoryDetailsAssigneeNkolay IvanowAssign to meHeoot& Gatya DimitrovaDevelopment@ Open with VS Code1 branch3 commits1 pull request3 bullds3 days agoWeXolDwomponentPlatformSub-ProductAdd optionsLabetsStory point estimate9:51 AM | Daily - PlatformDaily - Platform • now100% L28•Mon 18 May 9:51:18• CaleMon 18 May 9:51L Al BockmarxsSteliyan GeorgievNikolay YankovAJ Panorama for CallScoring m o0(AUTOMATED AT ECORINGR J-20301 05 0 •*=(Deadiine 25 May) Migratedepricated Gemini 3.1 FlashLhe Fietew moocDeployedД л-20880 (00 11 •**=Setup test coverage forProphet in SonarE JY-19951 1Е Jy-202720 •=*=Sidekick SMS issueDeployedCЕ P4-20091n • = @...Nikolay lvanovAneliya AngelovaLukas Kovalik4:52...
|
NULL
|
8397054432965923408
|
NULL
|
visual_change
|
ocr
|
NULL
|
Notion CalendarEditViewWindowHelpmeet.google.com/a Notion CalendarEditViewWindowHelpmeet.google.com/agt-teir-cwt?authuser=lukas.kovalik%40jiminny.com+18Nikolay Yankov (Presenting)Flex JimE3 Prom0 AtteniE3 MCP(a Sche xo Curso X * Cauo XCostXhttps://fiminny.atlassian.net/jra/software/c/projectsJJY/boards/37?selectedissue=JY-2083523 SSHE Datadog* Claudel3 CircieClA SentryPlatform Team %.Q Search boardS00P J-20S8 W JT-298SSMAOTOKONote: here you can find example prompts for testing - ® Prompts and SkllsNotify the user d a~ Subtasksdeleted but is used in AJ…..AJ REPORTSBacklogД Jy-200763 .* =0WorkPriorityStor...Assignee% JY-20859 tool for get.deal= Medium8 Unassign...KATDURUEYNotify the user if a SS isdeieted but is used in AJReportAJREPORTSBacklogA JY-206152.5 a4+0 =% JY-20860 tool for search,deals= MediumUnassign...% JY-20861 tool for lisk_deal_options= Medium% Unassign...READY FOR DEVS* JY-20862 tool for get.deal activities= MediumUnassign..READY FOR DEVS= MediumUnassign...READY FOR DEVS9, JY-20884 add tools to tooks lisgl= MediumUpgrade BE lbraries - MayMAINTENANCE8Unassign...READY FOR DEVS"o JY-20805 manualtesung= MediumUnassign...READY FOR DEVS% JY-20874 describe tools data structures= MediumNxoiay T.E JY-19958Linked work itemsAnerowenevilsuggestionsAolinkeewewneAUTO-DETEGTED ACTIVITY TYРОcontuence contontBacklogI Jy-204702 a0=Prompts and Skills"0AGOQ мсР3 PipelTasks7insights & Coachin…C Dev@ 2In DevI Improve StoryDetailsAssigneeNkolay IvanowAssign to meHeoot& Gatya DimitrovaDevelopment@ Open with VS Code1 branch3 commits1 pull request3 bullds3 days agoWeXolDwomponentPlatformSub-ProductAdd optionsLabetsStory point estimate9:51 AM | Daily - PlatformDaily - Platform • now100% L28•Mon 18 May 9:51:18• CaleMon 18 May 9:51L Al BockmarxsSteliyan GeorgievNikolay YankovAJ Panorama for CallScoring m o0(AUTOMATED AT ECORINGR J-20301 05 0 •*=(Deadiine 25 May) Migratedepricated Gemini 3.1 FlashLhe Fietew moocDeployedД л-20880 (00 11 •**=Setup test coverage forProphet in SonarE JY-19951 1Е Jy-202720 •=*=Sidekick SMS issueDeployedCЕ P4-20091n • = @...Nikolay lvanovAneliya AngelovaLukas Kovalik4:52...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
49660
|
1766
|
56
|
2026-05-18T06:51:14.870859+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779087074870_m1.jpg...
|
PhpStorm
|
faVsco.js – TestPipedriveOfficialSdkCommand.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Notion CalendarEditViewWindowHelpDaily - Platform Notion CalendarEditViewWindowHelpDaily - Platform • now100% 12Mon 18 May 9:51:14meet.google.com/agt-teir-cwt?authuser=lukas.kovalik%40jiminny.comNikolay Yankov (Presenting)+18M InboxE3 JiminE3 Prom0 Atten:E3 MCP - X[Q ScheX0) Curso X * Cauo X Cost X 0Jmr x1@ Moo Poe X→ Jmrhttps:/fiminny.atlassian.net/jra/software/c/projects/JJY/boards/37?selectedissue=JY-20835Platform Team 8.Q Search board-81MADTOKONotify the user d adeleted but is used in AJ…..AJ REPORTSBacklogД Jy-200763 .* =0Notify the user it a SS isdeleted but is used in AJReportAJREPORTSBackiogR JY-2061525 а000 =Upgrade BE ibraries - MayMAINTENANCSE JY-199581 •=©Amprove Acuvity lypesuggestionsAUTO-DETEGTED ASTIVITY TYPBacklogД Jy-204702 as=C Projectsu0 Daaosd00e caudeASoozy 2X D Lasnsigits & CoachnC Dw Cux9 J-2W758 1 J7-4085SNote: here you can find example prompts for testing - ® Prompts and Sklls~ SubtasksWork% JY-20859 tool for get.deal% JY-20860 tool for search,deais% JY-20861 tool for Iisf_deal_options* JY-20862 tool for get.deal activities9, JY-20884 add tools to tools, lisgl"o JY-20805 manualtesung% JY-20874 describe tools data structuresLinked work itemsAoainxodweneConfluence contentPrompts and SkillsPriorityStor...Assignee= Medium8 Unassign...KATURUSY= MediumUnassign...KAURWY= Medium% Unassign...READY FOR DEVS= Medium= Medium= Medium|Unassign...8 Unassion...8Unassign...READY FOR DEVSREADY FOR DEVSREADY FOR DEV= MediumUnassign.READY FOR DEV= MediumNxolay T.@ 2In DevI Improve Story~ DetailsAssignee• Nkclay IvanouAssign to me2 Galya DimitrovaDevelopmentQ Open with VS Code1 branch3 commits1 pull request3 bullds3 days agoWeXolDuomponentPlatformSub-ProductAdd optionsLabetsStory point estimate© Calertọn 18 May 9:51L1 Al BookmarxsSteliyan GeorgievNikolay YankovAJ Panorama for CallScoring in ODAUTOMATED AT SCORINGR Jy-20301 05(Deadiine 25 May) Migratedepricated Gemini 3.1 FlashDeployedД л7-20880 0 0 ***=Setup test coverage forProphet in SonarВ4Р-T9TЕ Jy-202720 •=0=Sidekick SMS issueDeployed(. P7-20091n • = @Nikolay lIvanovAneliya Angelova9:51 AM | Daily - Platform...Lukas Kovalik4:48...
|
NULL
|
413501360037312494
|
NULL
|
visual_change
|
ocr
|
NULL
|
Notion CalendarEditViewWindowHelpDaily - Platform Notion CalendarEditViewWindowHelpDaily - Platform • now100% 12Mon 18 May 9:51:14meet.google.com/agt-teir-cwt?authuser=lukas.kovalik%40jiminny.comNikolay Yankov (Presenting)+18M InboxE3 JiminE3 Prom0 Atten:E3 MCP - X[Q ScheX0) Curso X * Cauo X Cost X 0Jmr x1@ Moo Poe X→ Jmrhttps:/fiminny.atlassian.net/jra/software/c/projects/JJY/boards/37?selectedissue=JY-20835Platform Team 8.Q Search board-81MADTOKONotify the user d adeleted but is used in AJ…..AJ REPORTSBacklogД Jy-200763 .* =0Notify the user it a SS isdeleted but is used in AJReportAJREPORTSBackiogR JY-2061525 а000 =Upgrade BE ibraries - MayMAINTENANCSE JY-199581 •=©Amprove Acuvity lypesuggestionsAUTO-DETEGTED ASTIVITY TYPBacklogД Jy-204702 as=C Projectsu0 Daaosd00e caudeASoozy 2X D Lasnsigits & CoachnC Dw Cux9 J-2W758 1 J7-4085SNote: here you can find example prompts for testing - ® Prompts and Sklls~ SubtasksWork% JY-20859 tool for get.deal% JY-20860 tool for search,deais% JY-20861 tool for Iisf_deal_options* JY-20862 tool for get.deal activities9, JY-20884 add tools to tools, lisgl"o JY-20805 manualtesung% JY-20874 describe tools data structuresLinked work itemsAoainxodweneConfluence contentPrompts and SkillsPriorityStor...Assignee= Medium8 Unassign...KATURUSY= MediumUnassign...KAURWY= Medium% Unassign...READY FOR DEVS= Medium= Medium= Medium|Unassign...8 Unassion...8Unassign...READY FOR DEVSREADY FOR DEVSREADY FOR DEV= MediumUnassign.READY FOR DEV= MediumNxolay T.@ 2In DevI Improve Story~ DetailsAssignee• Nkclay IvanouAssign to me2 Galya DimitrovaDevelopmentQ Open with VS Code1 branch3 commits1 pull request3 bullds3 days agoWeXolDuomponentPlatformSub-ProductAdd optionsLabetsStory point estimate© Calertọn 18 May 9:51L1 Al BookmarxsSteliyan GeorgievNikolay YankovAJ Panorama for CallScoring in ODAUTOMATED AT SCORINGR Jy-20301 05(Deadiine 25 May) Migratedepricated Gemini 3.1 FlashDeployedД л7-20880 0 0 ***=Setup test coverage forProphet in SonarВ4Р-T9TЕ Jy-202720 •=0=Sidekick SMS issueDeployed(. P7-20091n • = @Nikolay lIvanovAneliya Angelova9:51 AM | Daily - Platform...Lukas Kovalik4:48...
|
49658
|
NULL
|
NULL
|
NULL
|
|
49659
|
1768
|
33
|
2026-05-18T06:51:13.050260+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779087073050_m2.jpg...
|
PhpStorm
|
faVsco.js – TestPipedriveOfficialSdkCommand.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
PhostormProiectVIewINavicareCodeLaravelKeractor• D PhostormProiectVIewINavicareCodeLaravelKeractor• Dally - Platrorm • now100% C• • Mon 18 May 9:51:12FV faVsco.js?° pipedrive-sdk-poc vAskJiminnyReportActivityServiceTest vC ActivityController.ong=custom.loglaravel.log4 SF [jiminny@localhost]& console (STAGINGI© SyncProfileMetadata.php©syncleammetadata.ong1estPlpeariveomricialsakcommand.orCConvertLeadActivities.ongOPurgeLookupcache.pnp© SyncPlanhat.phpA HS_local [jiminny@localhost]A console [PROD]© TestPipedriveOfficialSdkCommand.php X © Kernel.php• Debugging Pipedriv+0 ..# console EUSapiinstance = new PersonsApi(null, $config);Upoateopponunityspecrications.ono>@ Dealinsights• … Dev>C DialersD DTOSC ElasticsearchEnqagementstatsш GeckoExport• LivestreamMallboxesa MidratePlavbackithemesM Plavbooks141143D Playlists> M Postmark> M PronhetAv D Reports© AutomatedReportsCommand.php© AutomatedReportSRetentionPollcycor 161© AutomatedReportsSendCommand.pht(C) CroateMockAck.liminnvRenortResult@© DeleteReportCommand.php© GenerateMarketingReport.php© Team.php© Usage.php>07 Slack›_ TeamscreatePlaybookcreatedevent.ongclass lestripeuraveurricralsakcommano excenas commanaprivate function testBasicCrudOperations(SocialAccount SsocialAccount): voidsthls->intol su. (Sexpires && $expires ‹ timeo ? yc(r'limiti = 101)c) IntearationApp/Service.php(C) LeadConverted.php© CreateSelfCoachedEvent.php1 caten tescepe on sa) y et Personel), ro, netrtleved per soni 1n (elopseoes', selopsed) :C) CreateCommentedEvent.phpC) CreateSmsSentEvent.ohpC) PlanhatActivityListener.pho(C)AskAnvthinaPromotService.oho(C)AutomatedReportsRepositorv.ohd// Test with the DB token directlySthis->info( string: "\n--- Testing with DB token ---"):Scont1a = new confiqurationorSconfig->setAccessToken(SrawDbToken) :d: :testGetPersons(Obiect (Pioedrive versions v1 ConfiqurationC) AutomatedRenortsCommand.ohophp api y2.ohoC) RequestGenerateReport.Job.oho/eertvectaisda SdKCod.phd: :testBasicCrudOperations (Object(Jiminny (Mode15|SocialAccount))(C) AutomatedReportkesulconp(C) AutomatedRenort nhn X// Test 2a: Get DealsAccept Rejectclass AutomatedRenont extends Mode]li46 y1Y6AY137// Test 2a: Get Deals SummarySthis->tes+Getleals(Sconfjol*• Tereturn Sthis->getType === AutomatedReportsService::TYPE_ASK_JIMINNY:public function isExpired: boolf...}- 139140141— 142// Test 2b: Get PersonsSthis-stes+Ge+Pencons(Sconfia)O docker exec docker lamp 1 pho artisan timinnv:test-pinedrive-official-sdk 19public function canExecute@: boolf...;public function getActivitvSearchido: ?int-...=// Test 2c: Get ActivitiesSthis->testGetActivities($config)} catch (Exception $e) {$this->recordResult( testName: 'Basic CRUD Operations'.success: false, Se->getpublic function getAskAnvthingPromptIdO: 2intf...}Sthis->newLineO24les 190-12>2) Reject allAccept allAsk anvthina (*4L)nublic function aetExoiresAtO: 2Carhons...no usaget 1 of 10 edits +Accept File & X Reject File 0%€+ 2 of 2 files →+ & codeSWE.1Gprivate tunction testlurrentsystemApproachSoc1alAccount ssoc1aLAccount: vO1d ...ServicesToC exv M DatabasevAEU#consolev&jiminny@localhost# HS localA SFA PROD« consoleA STAGING# console 2 s 241 msy, NoshorOutputGid jiminny.social_accounts xdid w 1 rowvCSu| 1 + 0, sộ3e idsociable 1d1116241.19555731|W provider_user_id! provider_user_token(• provider refresh tokenI expiresM refresh token expiresU oroviden!O state1 auth_scopeI retry afterI created atV1U:AQLBAHS -L2 TNK2yuuuaLq142hWb9crUNKTpk4=-109r5nXap_6AE0hDhDQVa1nvWCHEVnpVSEAAAAFB8BqKahk:G9WOBBwagbzBtAgEAMGgGCSqGS1D3D0EHATAeB9LghkgBZ0MEAS4wEQQMnG8KNCZLI5EnLRPXA9EQgDsGP1CKfI5MU/0eL36BtM5FCQa56mYUy24_AAoqdh2y;V=ka6egLqSOinp-5G4JE/frJURMV8VIW-FY149HGHSO8DKUSBq30JM398MH84Cb10mzCrXaVo2GbZEd6nn5034113:[TELEGRAM_TOKEN]b2bfc1779088398<null)oinedriveconnectedlbase.deals:full.activities:full.contacts:full.search:readundated at2023-09-08 09:44:292026-05-15 15:44•311 row retrieved ctartina from 1 in 740 mc (eyecution: 186 mc fetchina- 554 mclW Windsurf Toams 127-1/25 charc 1 line hreak) UTF.8ih 4 spaces...
|
NULL
|
6272835950382003353
|
NULL
|
click
|
ocr
|
NULL
|
PhostormProiectVIewINavicareCodeLaravelKeractor• D PhostormProiectVIewINavicareCodeLaravelKeractor• Dally - Platrorm • now100% C• • Mon 18 May 9:51:12FV faVsco.js?° pipedrive-sdk-poc vAskJiminnyReportActivityServiceTest vC ActivityController.ong=custom.loglaravel.log4 SF [jiminny@localhost]& console (STAGINGI© SyncProfileMetadata.php©syncleammetadata.ong1estPlpeariveomricialsakcommand.orCConvertLeadActivities.ongOPurgeLookupcache.pnp© SyncPlanhat.phpA HS_local [jiminny@localhost]A console [PROD]© TestPipedriveOfficialSdkCommand.php X © Kernel.php• Debugging Pipedriv+0 ..# console EUSapiinstance = new PersonsApi(null, $config);Upoateopponunityspecrications.ono>@ Dealinsights• … Dev>C DialersD DTOSC ElasticsearchEnqagementstatsш GeckoExport• LivestreamMallboxesa MidratePlavbackithemesM Plavbooks141143D Playlists> M Postmark> M PronhetAv D Reports© AutomatedReportsCommand.php© AutomatedReportSRetentionPollcycor 161© AutomatedReportsSendCommand.pht(C) CroateMockAck.liminnvRenortResult@© DeleteReportCommand.php© GenerateMarketingReport.php© Team.php© Usage.php>07 Slack›_ TeamscreatePlaybookcreatedevent.ongclass lestripeuraveurricralsakcommano excenas commanaprivate function testBasicCrudOperations(SocialAccount SsocialAccount): voidsthls->intol su. (Sexpires && $expires ‹ timeo ? yc(r'limiti = 101)c) IntearationApp/Service.php(C) LeadConverted.php© CreateSelfCoachedEvent.php1 caten tescepe on sa) y et Personel), ro, netrtleved per soni 1n (elopseoes', selopsed) :C) CreateCommentedEvent.phpC) CreateSmsSentEvent.ohpC) PlanhatActivityListener.pho(C)AskAnvthinaPromotService.oho(C)AutomatedReportsRepositorv.ohd// Test with the DB token directlySthis->info( string: "\n--- Testing with DB token ---"):Scont1a = new confiqurationorSconfig->setAccessToken(SrawDbToken) :d: :testGetPersons(Obiect (Pioedrive versions v1 ConfiqurationC) AutomatedRenortsCommand.ohophp api y2.ohoC) RequestGenerateReport.Job.oho/eertvectaisda SdKCod.phd: :testBasicCrudOperations (Object(Jiminny (Mode15|SocialAccount))(C) AutomatedReportkesulconp(C) AutomatedRenort nhn X// Test 2a: Get DealsAccept Rejectclass AutomatedRenont extends Mode]li46 y1Y6AY137// Test 2a: Get Deals SummarySthis->tes+Getleals(Sconfjol*• Tereturn Sthis->getType === AutomatedReportsService::TYPE_ASK_JIMINNY:public function isExpired: boolf...}- 139140141— 142// Test 2b: Get PersonsSthis-stes+Ge+Pencons(Sconfia)O docker exec docker lamp 1 pho artisan timinnv:test-pinedrive-official-sdk 19public function canExecute@: boolf...;public function getActivitvSearchido: ?int-...=// Test 2c: Get ActivitiesSthis->testGetActivities($config)} catch (Exception $e) {$this->recordResult( testName: 'Basic CRUD Operations'.success: false, Se->getpublic function getAskAnvthingPromptIdO: 2intf...}Sthis->newLineO24les 190-12>2) Reject allAccept allAsk anvthina (*4L)nublic function aetExoiresAtO: 2Carhons...no usaget 1 of 10 edits +Accept File & X Reject File 0%€+ 2 of 2 files →+ & codeSWE.1Gprivate tunction testlurrentsystemApproachSoc1alAccount ssoc1aLAccount: vO1d ...ServicesToC exv M DatabasevAEU#consolev&jiminny@localhost# HS localA SFA PROD« consoleA STAGING# console 2 s 241 msy, NoshorOutputGid jiminny.social_accounts xdid w 1 rowvCSu| 1 + 0, sộ3e idsociable 1d1116241.19555731|W provider_user_id! provider_user_token(• provider refresh tokenI expiresM refresh token expiresU oroviden!O state1 auth_scopeI retry afterI created atV1U:AQLBAHS -L2 TNK2yuuuaLq142hWb9crUNKTpk4=-109r5nXap_6AE0hDhDQVa1nvWCHEVnpVSEAAAAFB8BqKahk:G9WOBBwagbzBtAgEAMGgGCSqGS1D3D0EHATAeB9LghkgBZ0MEAS4wEQQMnG8KNCZLI5EnLRPXA9EQgDsGP1CKfI5MU/0eL36BtM5FCQa56mYUy24_AAoqdh2y;V=ka6egLqSOinp-5G4JE/frJURMV8VIW-FY149HGHSO8DKUSBq30JM398MH84Cb10mzCrXaVo2GbZEd6nn5034113:[TELEGRAM_TOKEN]b2bfc1779088398<null)oinedriveconnectedlbase.deals:full.activities:full.contacts:full.search:readundated at2023-09-08 09:44:292026-05-15 15:44•311 row retrieved ctartina from 1 in 740 mc (eyecution: 186 mc fetchina- 554 mclW Windsurf Toams 127-1/25 charc 1 line hreak) UTF.8ih 4 spaces...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
49658
|
1766
|
55
|
2026-05-18T06:51:11.780468+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779087071780_m1.jpg...
|
PhpStorm
|
faVsco.js – TestPipedriveOfficialSdkCommand.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Notion CalendarEditViewWindowHelpDaily - Platform Notion CalendarEditViewWindowHelpDaily - Platform • now100% 12Mon 18 May 9:51:11meet.google.com/agt-teir-cwt?authuser=lukas.kovalik%40jiminny.comNikolay Yankov (Presenting)+M Inbo|E3 3minE3 Prom0 Ate xE3 MCP- x( Sche x© Curx x* Owo x• cost x Jmin x Q MCP xo Pipe xhttps:/fiminny.atlassian.net/jra/software/c/projects/JJY/boards/37?selectedissue=JY-208350 идестиDacaooyb1 cabeO Crore85037 07 DLLa Insights & Coschin…C Dw CukPlatform Team %Q Sesron coardS009 J1-207591 1 J7-4983STAOTOKONotify the user d adeined but is used in AJ...XI maarEsBaciiogO.Jy2067e3 sas =Notity che user it a 5S ideieted but is used in AJReportBacidogД Jy 20E15Upgrade DE ibranas - MayLAA DATER MIKEDBасто9E Jy-19a581. =QsuggestonsAUTO DITECTED AOTTVTTY TУЛОBacidagД 3y-20010Note: here you can find example prompts for testing - ® Prompts and SkultsSubtasksWork% JY-20859 tool for get_deal|%, JY-20860 tool for search.deais% JY-20861 tool for ist_deal,options4a JY-20862 tooi for get deal activities% JY-20863 rate limits%, JY-20884 add toois to tools list"t JY*20805 maniusitesung% JY-20874 describe toois data structuresLinked work itemsAoelinsoowrendcConfluence contentPrompts and Skils-•+PrlocityStor..AssignesSuna= Medium8 Unassign...CATORSY= Medium% Unassign..= Medium% Unassign...READY FOR DEVS= MediumUnassign...READY FORDEVS= MediumUnassign..READY FORDEVS= Medium|8Unassign...READY FOR DEVS= Medium= MediumUnassign..Nikclay Y....READY FOR DEVS@ 2In DevI Improve StoryDetailsAssigneeNkolsy IvanouAssign to meReporter& Gatya DintrovaDevelopmentQ Open with VS Code1 branch3 commits1 pull request3 bullds3 aays ag0ENGLDLompononPlatformAdd optionsLatetsstory poit esonat• Caletọn 18 May 9:51L1 Ai bockmarxsSteliyan Georgiev.Nikolay YankovAJ Panorama for CallScoring m o0Qл-2л0 08(Deadina 25 Mayl Migratedepnicared Gemin 3.1 FlashQ.J1-22800 3 [1 *** =Sesup test coverage forNikolay vanovAneliya Angelova9:51 AM | Daily - Platform50 11-20172Sidekick SMS issueDepioyedТЕ Рнеи11 »*=→•..Lukas Kovalik4:45...
|
NULL
|
-7195903002697802741
|
NULL
|
visual_change
|
ocr
|
NULL
|
Notion CalendarEditViewWindowHelpDaily - Platform Notion CalendarEditViewWindowHelpDaily - Platform • now100% 12Mon 18 May 9:51:11meet.google.com/agt-teir-cwt?authuser=lukas.kovalik%40jiminny.comNikolay Yankov (Presenting)+M Inbo|E3 3minE3 Prom0 Ate xE3 MCP- x( Sche x© Curx x* Owo x• cost x Jmin x Q MCP xo Pipe xhttps:/fiminny.atlassian.net/jra/software/c/projects/JJY/boards/37?selectedissue=JY-208350 идестиDacaooyb1 cabeO Crore85037 07 DLLa Insights & Coschin…C Dw CukPlatform Team %Q Sesron coardS009 J1-207591 1 J7-4983STAOTOKONotify the user d adeined but is used in AJ...XI maarEsBaciiogO.Jy2067e3 sas =Notity che user it a 5S ideieted but is used in AJReportBacidogД Jy 20E15Upgrade DE ibranas - MayLAA DATER MIKEDBасто9E Jy-19a581. =QsuggestonsAUTO DITECTED AOTTVTTY TУЛОBacidagД 3y-20010Note: here you can find example prompts for testing - ® Prompts and SkultsSubtasksWork% JY-20859 tool for get_deal|%, JY-20860 tool for search.deais% JY-20861 tool for ist_deal,options4a JY-20862 tooi for get deal activities% JY-20863 rate limits%, JY-20884 add toois to tools list"t JY*20805 maniusitesung% JY-20874 describe toois data structuresLinked work itemsAoelinsoowrendcConfluence contentPrompts and Skils-•+PrlocityStor..AssignesSuna= Medium8 Unassign...CATORSY= Medium% Unassign..= Medium% Unassign...READY FOR DEVS= MediumUnassign...READY FORDEVS= MediumUnassign..READY FORDEVS= Medium|8Unassign...READY FOR DEVS= Medium= MediumUnassign..Nikclay Y....READY FOR DEVS@ 2In DevI Improve StoryDetailsAssigneeNkolsy IvanouAssign to meReporter& Gatya DintrovaDevelopmentQ Open with VS Code1 branch3 commits1 pull request3 bullds3 aays ag0ENGLDLompononPlatformAdd optionsLatetsstory poit esonat• Caletọn 18 May 9:51L1 Ai bockmarxsSteliyan Georgiev.Nikolay YankovAJ Panorama for CallScoring m o0Qл-2л0 08(Deadina 25 Mayl Migratedepnicared Gemin 3.1 FlashQ.J1-22800 3 [1 *** =Sesup test coverage forNikolay vanovAneliya Angelova9:51 AM | Daily - Platform50 11-20172Sidekick SMS issueDepioyedТЕ Рнеи11 »*=→•..Lukas Kovalik4:45...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
49657
|
1766
|
54
|
2026-05-18T06:51:08.751898+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779087068751_m1.jpg...
|
PhpStorm
|
faVsco.js – TestPipedriveOfficialSdkCommand.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Notion CalendarEditViewWindowHelpDaily - Platform Notion CalendarEditViewWindowHelpDaily - Platform • now100% L2meet.google.com/agt-teir-cwt?authuser=lukas.kovalik%40jiminny.comNikolay Yankov (Presenting)+M Inbo|E3 JmicE3 Prom0 Ater xx MoP: X( Sche x© Curs x* Owo x• Cost x Jmi x Q MP xo Pipe x® Snh xO Calehttps:/fiminny.atlassian.net/jra/software/c/projects/JJY/boards/37?selectedissue=JY-208350 идестиE caao 0 e1 cadeL Insights & Coschin…C Dw C UKPlatform Team %Q Sesron coardS009 J1-207591 1 J7-4985STAOTOKONotify the user d adeined but is used in AJ...XI maarEsBaciiogO.Jy2067e3 9as =Notity the user it a $S isdeieted but is used in AJReportBacidogД Jy 20E15Upgrade DE ibranas - MayLAA DATEN MIKEDBасто9E Jy-198581. =QsuggestonsAUTO DITECTED ASTTVTTY TYЛOBacidagД 3y-20010Note: here you can find example prompts for testing - ® Prompts and SultsSubtasksWork% JY-20859 tool for get_deal|%, JY-20860 tool for search.deais% JY-20861 tool for ist_deal,options4a JY-20862 tooi for get deal activities% JY-20863 rate limits%, JY-20884 add toois to tools list"tJY*20805 maniusitesung% JY-20874 describe toois data structuresLinked work itemsAoelinsoowrendcConfluence contentPrompts and SkilsPrlocityStor..Assignes= Medium8 Unassign...= Medium% Unassign..= Medium% Unassign...= MediumUnassign...= MediumUnassign..= Medium|8Unassign...= Medium= MediumUnassign..Nikclay Y....O Crore85037 07 DL-•+SunaCATORSYREADY FORDEVSREADY FORDEVSREADY FORDEVSREADY FOR DEVSREADY FOR DEVSIn DevI Improve StoryDetailsAssigneeNkolsy IvanouAssign to me& Gatya DintrovaDevelopmentQ Open with VS Code1 branch3 commits1 pull request3 bullds3 aays ag0MEROEDLomoononPlatformAdd optionsLatetsstory poitesoratAJ Panorama for CallScoring m o0Qл-2л0 08(Deadina 25 Mayl Migratedepnicared Gemin 3.1 FlashQ.J1-220800 3 [1 *** =Setup test coverage fortọn 18 May 9:51L1 Ai bockmarxsSteliyan GeorgievNikolay Vanov"SSUL SOgIS9:51 AM | Daily - Platform50 1-20172Sidekick SMS issueDepioyedТЕ Рнеи11 »*=→•..Lukas Kovalik•4:42Mon 18 May 9:51:08Nikolay YankovAneliya Angelova...
|
NULL
|
2428079084665594274
|
NULL
|
visual_change
|
ocr
|
NULL
|
Notion CalendarEditViewWindowHelpDaily - Platform Notion CalendarEditViewWindowHelpDaily - Platform • now100% L2meet.google.com/agt-teir-cwt?authuser=lukas.kovalik%40jiminny.comNikolay Yankov (Presenting)+M Inbo|E3 JmicE3 Prom0 Ater xx MoP: X( Sche x© Curs x* Owo x• Cost x Jmi x Q MP xo Pipe x® Snh xO Calehttps:/fiminny.atlassian.net/jra/software/c/projects/JJY/boards/37?selectedissue=JY-208350 идестиE caao 0 e1 cadeL Insights & Coschin…C Dw C UKPlatform Team %Q Sesron coardS009 J1-207591 1 J7-4985STAOTOKONotify the user d adeined but is used in AJ...XI maarEsBaciiogO.Jy2067e3 9as =Notity the user it a $S isdeieted but is used in AJReportBacidogД Jy 20E15Upgrade DE ibranas - MayLAA DATEN MIKEDBасто9E Jy-198581. =QsuggestonsAUTO DITECTED ASTTVTTY TYЛOBacidagД 3y-20010Note: here you can find example prompts for testing - ® Prompts and SultsSubtasksWork% JY-20859 tool for get_deal|%, JY-20860 tool for search.deais% JY-20861 tool for ist_deal,options4a JY-20862 tooi for get deal activities% JY-20863 rate limits%, JY-20884 add toois to tools list"tJY*20805 maniusitesung% JY-20874 describe toois data structuresLinked work itemsAoelinsoowrendcConfluence contentPrompts and SkilsPrlocityStor..Assignes= Medium8 Unassign...= Medium% Unassign..= Medium% Unassign...= MediumUnassign...= MediumUnassign..= Medium|8Unassign...= Medium= MediumUnassign..Nikclay Y....O Crore85037 07 DL-•+SunaCATORSYREADY FORDEVSREADY FORDEVSREADY FORDEVSREADY FOR DEVSREADY FOR DEVSIn DevI Improve StoryDetailsAssigneeNkolsy IvanouAssign to me& Gatya DintrovaDevelopmentQ Open with VS Code1 branch3 commits1 pull request3 bullds3 aays ag0MEROEDLomoononPlatformAdd optionsLatetsstory poitesoratAJ Panorama for CallScoring m o0Qл-2л0 08(Deadina 25 Mayl Migratedepnicared Gemin 3.1 FlashQ.J1-220800 3 [1 *** =Setup test coverage fortọn 18 May 9:51L1 Ai bockmarxsSteliyan GeorgievNikolay Vanov"SSUL SOgIS9:51 AM | Daily - Platform50 1-20172Sidekick SMS issueDepioyedТЕ Рнеи11 »*=→•..Lukas Kovalik•4:42Mon 18 May 9:51:08Nikolay YankovAneliya Angelova...
|
49656
|
NULL
|
NULL
|
NULL
|
|
49656
|
1766
|
53
|
2026-05-18T06:51:05.718460+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779087065718_m1.jpg...
|
PhpStorm
|
faVsco.js – TestPipedriveOfficialSdkCommand.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Notion CalendarEditViewWindowHelpDaily - Platform Notion CalendarEditViewWindowHelpDaily - Platform • now100% 12Mon 18 May 9:51:05meet.google.com/agt-teir-cwt?authuser=lukas.kovalik%40jiminny.comNikolay Yankov (Presenting)+i8M Inbo|E3 JmicE3 Prom0 Ater xE3 MCP- x( Sche x© Curs x* Owo x• cost x Jmin x Q MCP xo Pipe x aть xI• Calehttps:/fiminny.atlassian.net/jra/software/c/projects/JJY/boards/37?selectedissue=JY-208350 идестиDacaowyb1 cadeL Insights & Coschin…C Dw C UKPlatform Team %Q Sesron coardS009 J1-207591 1 J7-4985STAOTOKONotify the user d adeined but is used in AJ...XI maarEsBaciiogO.Jy2067e3 9as =Notity the user it a $S isdeieted but is used in AJReportBacidogД Jy 20E15Upgrade DE ibranas - MayLAA DATEN MIKEDBасто9E Jy-198581. =QsuggestonsAUTO DITECTED ASTIVITY TYNGBacidagД 3y-20010Note: here you can find example prompts for testing - ® Prompts and SultsSubtasksWork% JY-20859 tool for get_deal|%, JY-20860 tool for search.deais% JY-20861 tool for ist_deal,options4a JY-20862 tooi for get deal activities% JY-20863 rate limits%, JY-20884 add toois to tools list"tJY*20805 maniusitesung% JY-20874 describe toois data structuresLinked work itemsAoelinsoowrendcConfluence contentPrompts and SkilsPrlocityStor..Assignes= Medium8 Unassign...= Medium% Unassign..= Medium% Unassign...= MediumUnassign...= MediumUnassign..= Medium|8Unassign...= Medium= MediumUnassign..Nikclay Y....O Crore85031 0X DL-•+SunaCATORSYREADY FORDEVSREADY FORDEVSREADY FORDEVSREADY FOR DEVSREADY FOR DEVSIn DevI Improve StoryDetailsAssigneeNkolsy IvanouAssign to me& Gatya DintrovaDevelopmentQ Open with VS Code1 branch3 commits1 pull request3 bullds3 aays ag0WENOLDLomoononPlatformAdd optionsLatetsstory poit esonatAJ Panorama for CallScoring m o0Qл-2л0 08(Deadina 25 Mayl Migratedepnicared Gemin 3.1 FlashQ.J1-220800 3 [1 *** =Setup test coverage forL1 Ai bockmarxsSteliyan GeorgievNikolay YankovNikolay vanovSSUE SNOiSAneliya Angelova9:51 AM | Daily - Platform50 1-20172Sidekick SMS issueDepioyedТЕ Рнеи11 »*=→•..Lukas Kovalik4:39...
|
NULL
|
-2551918996894445971
|
NULL
|
visual_change
|
ocr
|
NULL
|
Notion CalendarEditViewWindowHelpDaily - Platform Notion CalendarEditViewWindowHelpDaily - Platform • now100% 12Mon 18 May 9:51:05meet.google.com/agt-teir-cwt?authuser=lukas.kovalik%40jiminny.comNikolay Yankov (Presenting)+i8M Inbo|E3 JmicE3 Prom0 Ater xE3 MCP- x( Sche x© Curs x* Owo x• cost x Jmin x Q MCP xo Pipe x aть xI• Calehttps:/fiminny.atlassian.net/jra/software/c/projects/JJY/boards/37?selectedissue=JY-208350 идестиDacaowyb1 cadeL Insights & Coschin…C Dw C UKPlatform Team %Q Sesron coardS009 J1-207591 1 J7-4985STAOTOKONotify the user d adeined but is used in AJ...XI maarEsBaciiogO.Jy2067e3 9as =Notity the user it a $S isdeieted but is used in AJReportBacidogД Jy 20E15Upgrade DE ibranas - MayLAA DATEN MIKEDBасто9E Jy-198581. =QsuggestonsAUTO DITECTED ASTIVITY TYNGBacidagД 3y-20010Note: here you can find example prompts for testing - ® Prompts and SultsSubtasksWork% JY-20859 tool for get_deal|%, JY-20860 tool for search.deais% JY-20861 tool for ist_deal,options4a JY-20862 tooi for get deal activities% JY-20863 rate limits%, JY-20884 add toois to tools list"tJY*20805 maniusitesung% JY-20874 describe toois data structuresLinked work itemsAoelinsoowrendcConfluence contentPrompts and SkilsPrlocityStor..Assignes= Medium8 Unassign...= Medium% Unassign..= Medium% Unassign...= MediumUnassign...= MediumUnassign..= Medium|8Unassign...= Medium= MediumUnassign..Nikclay Y....O Crore85031 0X DL-•+SunaCATORSYREADY FORDEVSREADY FORDEVSREADY FORDEVSREADY FOR DEVSREADY FOR DEVSIn DevI Improve StoryDetailsAssigneeNkolsy IvanouAssign to me& Gatya DintrovaDevelopmentQ Open with VS Code1 branch3 commits1 pull request3 bullds3 aays ag0WENOLDLomoononPlatformAdd optionsLatetsstory poit esonatAJ Panorama for CallScoring m o0Qл-2л0 08(Deadina 25 Mayl Migratedepnicared Gemin 3.1 FlashQ.J1-220800 3 [1 *** =Setup test coverage forL1 Ai bockmarxsSteliyan GeorgievNikolay YankovNikolay vanovSSUE SNOiSAneliya Angelova9:51 AM | Daily - Platform50 1-20172Sidekick SMS issueDepioyedТЕ Рнеи11 »*=→•..Lukas Kovalik4:39...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
49655
|
1766
|
52
|
2026-05-18T06:51:02.703949+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779087062703_m1.jpg...
|
PhpStorm
|
faVsco.js – TestPipedriveOfficialSdkCommand.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
pipedrive-sdk-poc, menu
S Project: faVsco.js, menu
pipedrive-sdk-poc, 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
6
1
6...
|
[{"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":"pipedrive-sdk-poc, menu","depth":5,"on_screen":true,"help_text":"Git Branch: pipedrive-sdk-poc","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":"AXStaticText","text":"6","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"1","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"6","depth":4,"role_description":"text"}]...
|
-233160542334707940
|
-8132368178556458034
|
visual_change
|
hybrid
|
NULL
|
Project: faVsco.js, menu
pipedrive-sdk-poc, menu
S Project: faVsco.js, menu
pipedrive-sdk-poc, 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
6
1
6
Notion CalendarEditViewWindowHelpDaily - Platform • now100% L28•Mon 18 May 9:51:02meet.google.com/agt-teir-cwt?authuser=lukas.kovalik%40jiminny.comNikolay Yankov (Presenting)+18E3 Jimin:E3 Prom:Attent X83 мCP - x(a Scher x• Curso x |* Cauc xCosti XoUmn X@ MoP[URL_WITH_CREDENTIALS] Georgiev®Nikolay YankovNikolay vanovSUL SЛO5Aneliya AngelovaLukas Kovalik:4:36...
|
49653
|
NULL
|
NULL
|
NULL
|
|
49654
|
1768
|
32
|
2026-05-18T06:50:51.803921+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779087051803_m2.jpg...
|
PhpStorm
|
faVsco.js – TestPipedriveOfficialSdkCommand.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
VIewINavicareCodeLaravelPhostormFV faVsco.js?° pip VIewINavicareCodeLaravelPhostormFV faVsco.js?° pipedrive-sdk-poc vroledeyso sonar-oroiect oroperties= test.ov<> Untitled Diagram.xmlus vetur.confio.ism. WEBHOOK FILTERING_MPLEMENTATION.mo>ib External Librariesv =° Scratches and Consolesv M Database ConsolesV AEUA console (EU]A DEAL RISKS [EU]A DI (EU]A EU [EU)v &llminny@localnostA console [jiminny@localhost]# Di [liminny@localhostlA HS local [iiminny@localhostl# SF ['iiminny@localhost]& zoho dev liminny@localhostV & PROD& console PRODI¿ console_1 [PROD14DPROD> ДOA> APAILPAIPRODSTAGING& console [STAGINGconsole " STAGNGduranus STAGINGIDally - Platrorm • now100% S2. Mon 18 May 9:50:51C ActivityController.ong=custom.loglaravel.log4 SF [jiminny@localhost]& console (STAGINGICascadeCConvertLeadActivities.ong© SyncPlanhat.phpA HS_local [jiminny@localhost]A console [PROD]© TestPipedriveOfficialSdkCommand.php X © Kernel.php• Debugging Pipedrive+0 ..# console EU© CreatePlaybookCreatedEvent.phpclass lestripeuraveurticralsakcommano excenas commanaprivate function testBasicCrudOperations(SocialAccount SsocialAccount): voidsthls->intol su. (Sexpires && $expires ‹ timeo ? ywServices+,o,cv M DatabasevAEU#consolev&jiminny@localhost# HS localA SFA PROD« consoleA STAGING# console 2 s 241 msy, Noshorc) IntearationApp/Service.php(C) LeadConverted.php© CreateSelfCoachedEvent.phpC) CreateCommentedEvent.phpC) CreateSmsSentEvent.ohpC) PlanhatActivityListener.pho(C)AskAnvthinaPromotService.ohoC) AutomatedReportsCommand.ohophp api y2.ohoC) RequestGenerateReport.Job.oho/// Test with the DB token directlySthis->info( string: "\n--- Testing with DB token ---")Scont1a = new confiqurationorsconf10->setaccesstoken(Srawdbloken):(C) AutomatedReportkesulconp(C) AutomatedRenort nhn X// Test 2a: Get DealsAccept Rejectclass AutomatedRenont extends ModellBAY11141143// Test 2a: Get Deals SummarySthis->tes+Getleals(Sconfioalreturn Sthis->getType === AutomatedReportsService::TYPE_ASK_JIMINNY:public function isExpired: boolf...}- 139140141// Test 2b: Get PersonsSthis-stes+Ge+Pencons(Sconfia)public function canExecute@: boolf...;public function getActivitvSearchido: ?int-...=// Test 2c: Get ActivitiesSthis->testGetActivities($config)} catch (Exception $e) {$this->recordResult( testName: 'Basic CRUD Operations'.success: false, $e->getpublic function getAskAnvthingPromptIdO: 2intf...}Sthis->newLineO172nublic function aetExoiresAtO: 2Carhons...no usaget 1 of 10 edits +Accept File & X Reject File 0%€+ 2 of 2 files →private tunction testlurrentsystemApproachSoc1alAccount ssoc1aLAccount: vO1d ...OutputGid jiminny.social_accounts xdid w 1 rowv1116241.19555731|docker exec docker lamp 1 php artisan jiminny:test-pipedrive-official-sdk 19ot a onso e oy anes/mr/este pert veotiCla Sor omane, рp 206tr ippoirieant soкnew PersonsApa null, scontag);A official Sok uses getAllPersons methodandt, . 101)., caran tesde on sut e cet ersone) rorr e per old n telopscohes, solbpecel):ghmirnys Conso1el Commandis/CrmtPestP2pedrtveott1c2aCSckcomnandt: 1testGetPersons (Object(Pipedrivel versions| vz1 Configuration))grer contednd te oteo es ea esre re te 41 u Seakeoe- oha e tes Crudopertions oi e (Jeimy Voe 3 5ocso 1ccoune))Thouahts• TestPipedriveOfficialSdkCommand.php+8-10l2) Reject allAccept allAsk anvthina (*4L)+ & codeSWE.1Ge idsociable 1dW provider_user_id! provider_user_token(• provider refresh tokenI expiresM refresh token expiresU oroviden!O state1 auth_scopeI retry afterI created atundated atV1U:AQLBAHS -L2 TNK2yuuuaLq142hWb9crUNKTpk4=-109r5nXap_6AE0hDhDQVa1nvWCHEVnpVSEAAAAFB8BqKahk:G9WOBBwagbzBtAgEAMGgGCSqGS1D3D0EHATAeB9LghkgBZ0MEAS4wEQQMnG8KNCZLI5EnLRPXA9EQgDsGP1CKfI5MU/0eL36BtM5FCQa56mYUy24_AAoqdh2y;V=ka6egLqSOinp-5G4JE/frJURMV8VIW-FY149HGHSO8DKUSBq30JM398MH84Cb10mzCrXaVo2GbZEd6nn5034113:[TELEGRAM_TOKEN]b2bfc1779088398<null)oinedriveconnectedibase.deals:full.activities:full.contacts:full.search:read2023-09-08 09:44:292026-05-15 15:44•311 row retrieved ctartina from 1 in 740 mc (eyecution: 186 mc fetchina- 554 mclW Windsurf Toamc137:1 (35 chars, 1 line break) UTF-8 # 4 spaces...
|
NULL
|
6348686074786956141
|
NULL
|
visual_change
|
ocr
|
NULL
|
VIewINavicareCodeLaravelPhostormFV faVsco.js?° pip VIewINavicareCodeLaravelPhostormFV faVsco.js?° pipedrive-sdk-poc vroledeyso sonar-oroiect oroperties= test.ov<> Untitled Diagram.xmlus vetur.confio.ism. WEBHOOK FILTERING_MPLEMENTATION.mo>ib External Librariesv =° Scratches and Consolesv M Database ConsolesV AEUA console (EU]A DEAL RISKS [EU]A DI (EU]A EU [EU)v &llminny@localnostA console [jiminny@localhost]# Di [liminny@localhostlA HS local [iiminny@localhostl# SF ['iiminny@localhost]& zoho dev liminny@localhostV & PROD& console PRODI¿ console_1 [PROD14DPROD> ДOA> APAILPAIPRODSTAGING& console [STAGINGconsole " STAGNGduranus STAGINGIDally - Platrorm • now100% S2. Mon 18 May 9:50:51C ActivityController.ong=custom.loglaravel.log4 SF [jiminny@localhost]& console (STAGINGICascadeCConvertLeadActivities.ong© SyncPlanhat.phpA HS_local [jiminny@localhost]A console [PROD]© TestPipedriveOfficialSdkCommand.php X © Kernel.php• Debugging Pipedrive+0 ..# console EU© CreatePlaybookCreatedEvent.phpclass lestripeuraveurticralsakcommano excenas commanaprivate function testBasicCrudOperations(SocialAccount SsocialAccount): voidsthls->intol su. (Sexpires && $expires ‹ timeo ? ywServices+,o,cv M DatabasevAEU#consolev&jiminny@localhost# HS localA SFA PROD« consoleA STAGING# console 2 s 241 msy, Noshorc) IntearationApp/Service.php(C) LeadConverted.php© CreateSelfCoachedEvent.phpC) CreateCommentedEvent.phpC) CreateSmsSentEvent.ohpC) PlanhatActivityListener.pho(C)AskAnvthinaPromotService.ohoC) AutomatedReportsCommand.ohophp api y2.ohoC) RequestGenerateReport.Job.oho/// Test with the DB token directlySthis->info( string: "\n--- Testing with DB token ---")Scont1a = new confiqurationorsconf10->setaccesstoken(Srawdbloken):(C) AutomatedReportkesulconp(C) AutomatedRenort nhn X// Test 2a: Get DealsAccept Rejectclass AutomatedRenont extends ModellBAY11141143// Test 2a: Get Deals SummarySthis->tes+Getleals(Sconfioalreturn Sthis->getType === AutomatedReportsService::TYPE_ASK_JIMINNY:public function isExpired: boolf...}- 139140141// Test 2b: Get PersonsSthis-stes+Ge+Pencons(Sconfia)public function canExecute@: boolf...;public function getActivitvSearchido: ?int-...=// Test 2c: Get ActivitiesSthis->testGetActivities($config)} catch (Exception $e) {$this->recordResult( testName: 'Basic CRUD Operations'.success: false, $e->getpublic function getAskAnvthingPromptIdO: 2intf...}Sthis->newLineO172nublic function aetExoiresAtO: 2Carhons...no usaget 1 of 10 edits +Accept File & X Reject File 0%€+ 2 of 2 files →private tunction testlurrentsystemApproachSoc1alAccount ssoc1aLAccount: vO1d ...OutputGid jiminny.social_accounts xdid w 1 rowv1116241.19555731|docker exec docker lamp 1 php artisan jiminny:test-pipedrive-official-sdk 19ot a onso e oy anes/mr/este pert veotiCla Sor omane, рp 206tr ippoirieant soкnew PersonsApa null, scontag);A official Sok uses getAllPersons methodandt, . 101)., caran tesde on sut e cet ersone) rorr e per old n telopscohes, solbpecel):ghmirnys Conso1el Commandis/CrmtPestP2pedrtveott1c2aCSckcomnandt: 1testGetPersons (Object(Pipedrivel versions| vz1 Configuration))grer contednd te oteo es ea esre re te 41 u Seakeoe- oha e tes Crudopertions oi e (Jeimy Voe 3 5ocso 1ccoune))Thouahts• TestPipedriveOfficialSdkCommand.php+8-10l2) Reject allAccept allAsk anvthina (*4L)+ & codeSWE.1Ge idsociable 1dW provider_user_id! provider_user_token(• provider refresh tokenI expiresM refresh token expiresU oroviden!O state1 auth_scopeI retry afterI created atundated atV1U:AQLBAHS -L2 TNK2yuuuaLq142hWb9crUNKTpk4=-109r5nXap_6AE0hDhDQVa1nvWCHEVnpVSEAAAAFB8BqKahk:G9WOBBwagbzBtAgEAMGgGCSqGS1D3D0EHATAeB9LghkgBZ0MEAS4wEQQMnG8KNCZLI5EnLRPXA9EQgDsGP1CKfI5MU/0eL36BtM5FCQa56mYUy24_AAoqdh2y;V=ka6egLqSOinp-5G4JE/frJURMV8VIW-FY149HGHSO8DKUSBq30JM398MH84Cb10mzCrXaVo2GbZEd6nn5034113:[TELEGRAM_TOKEN]b2bfc1779088398<null)oinedriveconnectedibase.deals:full.activities:full.contacts:full.search:read2023-09-08 09:44:292026-05-15 15:44•311 row retrieved ctartina from 1 in 740 mc (eyecution: 186 mc fetchina- 554 mclW Windsurf Toamc137:1 (35 chars, 1 line break) UTF-8 # 4 spaces...
|
49652
|
NULL
|
NULL
|
NULL
|
|
49653
|
1766
|
51
|
2026-05-18T06:50:49.433257+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779087049433_m1.jpg...
|
PhpStorm
|
faVsco.js – TestPipedriveOfficialSdkCommand.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
pipedrive-sdk-poc, menu
S Project: faVsco.js, menu
pipedrive-sdk-poc, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'...
|
[{"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":"pipedrive-sdk-poc, menu","depth":5,"on_screen":true,"help_text":"Git Branch: pipedrive-sdk-poc","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}]...
|
7266624862028544203
|
577591577001045378
|
visual_change
|
hybrid
|
NULL
|
Project: faVsco.js, menu
pipedrive-sdk-poc, menu
S Project: faVsco.js, menu
pipedrive-sdk-poc, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
Notion CalendarEditViewWindowHelpDaily - Platform • now100% 12Mon 18 May 9:50:49meet.google.com/agt-teir-cwt?authuser=lukas.kovalik%40jiminny.comNikolay Yankov (Presenting)+S18E3 JiminE3 Prom0 Atten:83 MCP(a Sche x• Curso x | * Cavo xCostX Jmn xQ мсP3 Pipel© S[URL_WITH_CREDENTIALS] 18 May 9:50L Al bookmarxsSteliyan GeorgievNikolay YankovNikolay VanovAneliya AngelovaLukas Kovalik4:23...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
49444
|
1765
|
16
|
2026-05-18T06:44:44.591604+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779086684591_m2.jpg...
|
PhpStorm
|
faVsco.js – TestPipedriveOfficialSdkCommand.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
pipedrive-sdk-poc, menu
S Project: faVsco.js, menu
pipedrive-sdk-poc, 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\Console\Commands\Crm;
use Jiminny\Console\Commands\Command;
use Jiminny\Console\Commands\Crm\Traits\SyncObjectsCommandTrait;
use Jiminny\Jobs\Crm\SyncObjects as SyncObjectsJob;
use Jiminny\Jobs\Job;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Models\Team;
class SyncObjects extends Command
{
use SyncObjectsCommandTrait;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'crm:sync-objects {team?} {--sync}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Sync remote CRM objects.';
/**
* Execute the console command.
*/
public function handle(): void
{
$teamId = $this->argument('team');
$teams = [];
if ($teamId) {
$team = Team::idOrUuId($teamId);
if ($team && $team->crm?->provider === Configuration::PROVIDER_HUBSPOT) {
$this->error(sprintf(
'Team %s (%s) uses HubSpot. Use crm:sync-hubspot-objects instead.',
$team->getName(),
$team->getUuid()
));
return;
}
if ($team) {
$teams[] = $team;
}
} else {
// Exclude HubSpot teams - handled by crm:sync-hubspot-objects
$teams = Team::where('status', Team::STATUS_ACTIVE)
->whereHas(
'crm',
fn ($q) => $q
->where('provider', '!=', Configuration::PROVIDER_HUBSPOT)
->where('sync_objects', true)
)
->get();
}
$this->dispatchSyncJobsForTeams($teams);
}
protected function getStaggerDelaySeconds(): float
{
return 2;
}
protected function getMaxDelaySeconds(): int
{
// Cap delay at 15 minutes (SQS max delay limit)
// capacity of 450 teams before reach
return 900;
}
protected function getLogPrefix(): string
{
return '';
}
protected function createSyncJob(Team $team): Job
{
return new SyncObjectsJob($team);
}
}
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":"pipedrive-sdk-poc, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.05618351,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: pipedrive-sdk-poc","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\\Console\\Commands\\Crm;\n\nuse Jiminny\\Console\\Commands\\Command;\nuse Jiminny\\Console\\Commands\\Crm\\Traits\\SyncObjectsCommandTrait;\nuse Jiminny\\Jobs\\Crm\\SyncObjects as SyncObjectsJob;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Models\\Team;\n\nclass SyncObjects extends Command\n{\n use SyncObjectsCommandTrait;\n\n /**\n * The name and signature of the console command.\n *\n * @var string\n */\n protected $signature = 'crm:sync-objects {team?} {--sync}';\n\n /**\n * The console command description.\n *\n * @var string\n */\n protected $description = 'Sync remote CRM objects.';\n\n /**\n * Execute the console command.\n */\n public function handle(): void\n {\n $teamId = $this->argument('team');\n $teams = [];\n\n if ($teamId) {\n $team = Team::idOrUuId($teamId);\n if ($team && $team->crm?->provider === Configuration::PROVIDER_HUBSPOT) {\n $this->error(sprintf(\n 'Team %s (%s) uses HubSpot. Use crm:sync-hubspot-objects instead.',\n $team->getName(),\n $team->getUuid()\n ));\n\n return;\n }\n if ($team) {\n $teams[] = $team;\n }\n } else {\n // Exclude HubSpot teams - handled by crm:sync-hubspot-objects\n $teams = Team::where('status', Team::STATUS_ACTIVE)\n ->whereHas(\n 'crm',\n fn ($q) => $q\n ->where('provider', '!=', Configuration::PROVIDER_HUBSPOT)\n ->where('sync_objects', true)\n )\n ->get();\n }\n\n $this->dispatchSyncJobsForTeams($teams);\n }\n\n protected function getStaggerDelaySeconds(): float\n {\n return 2;\n }\n\n protected function getMaxDelaySeconds(): int\n {\n // Cap delay at 15 minutes (SQS max delay limit)\n // capacity of 450 teams before reach\n return 900;\n }\n\n protected function getLogPrefix(): string\n {\n return '';\n }\n\n protected function createSyncJob(Team $team): Job\n {\n return new SyncObjectsJob($team);\n }\n}","depth":4,"bounds":{"left":0.15724733,"top":0.1963288,"width":0.390625,"height":0.8036712},"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Console\\Commands\\Crm;\n\nuse Jiminny\\Console\\Commands\\Command;\nuse Jiminny\\Console\\Commands\\Crm\\Traits\\SyncObjectsCommandTrait;\nuse Jiminny\\Jobs\\Crm\\SyncObjects as SyncObjectsJob;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Models\\Team;\n\nclass SyncObjects extends Command\n{\n use SyncObjectsCommandTrait;\n\n /**\n * The name and signature of the console command.\n *\n * @var string\n */\n protected $signature = 'crm:sync-objects {team?} {--sync}';\n\n /**\n * The console command description.\n *\n * @var string\n */\n protected $description = 'Sync remote CRM objects.';\n\n /**\n * Execute the console command.\n */\n public function handle(): void\n {\n $teamId = $this->argument('team');\n $teams = [];\n\n if ($teamId) {\n $team = Team::idOrUuId($teamId);\n if ($team && $team->crm?->provider === Configuration::PROVIDER_HUBSPOT) {\n $this->error(sprintf(\n 'Team %s (%s) uses HubSpot. Use crm:sync-hubspot-objects instead.',\n $team->getName(),\n $team->getUuid()\n ));\n\n return;\n }\n if ($team) {\n $teams[] = $team;\n }\n } else {\n // Exclude HubSpot teams - handled by crm:sync-hubspot-objects\n $teams = Team::where('status', Team::STATUS_ACTIVE)\n ->whereHas(\n 'crm',\n fn ($q) => $q\n ->where('provider', '!=', Configuration::PROVIDER_HUBSPOT)\n ->where('sync_objects', true)\n )\n ->get();\n }\n\n $this->dispatchSyncJobsForTeams($teams);\n }\n\n protected function getStaggerDelaySeconds(): float\n {\n return 2;\n }\n\n protected function getMaxDelaySeconds(): int\n {\n // Cap delay at 15 minutes (SQS max delay limit)\n // capacity of 450 teams before reach\n return 900;\n }\n\n protected function getLogPrefix(): string\n {\n return '';\n }\n\n protected function createSyncJob(Team $team): Job\n {\n return new SyncObjectsJob($team);\n }\n}","role_description":"text entry area","is_enabled":true,"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}]...
|
7656656192700481823
|
-8678315669563599932
|
visual_change
|
accessibility
|
NULL
|
Project: faVsco.js, menu
pipedrive-sdk-poc, menu
S Project: faVsco.js, menu
pipedrive-sdk-poc, 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\Console\Commands\Crm;
use Jiminny\Console\Commands\Command;
use Jiminny\Console\Commands\Crm\Traits\SyncObjectsCommandTrait;
use Jiminny\Jobs\Crm\SyncObjects as SyncObjectsJob;
use Jiminny\Jobs\Job;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Models\Team;
class SyncObjects extends Command
{
use SyncObjectsCommandTrait;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'crm:sync-objects {team?} {--sync}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Sync remote CRM objects.';
/**
* Execute the console command.
*/
public function handle(): void
{
$teamId = $this->argument('team');
$teams = [];
if ($teamId) {
$team = Team::idOrUuId($teamId);
if ($team && $team->crm?->provider === Configuration::PROVIDER_HUBSPOT) {
$this->error(sprintf(
'Team %s (%s) uses HubSpot. Use crm:sync-hubspot-objects instead.',
$team->getName(),
$team->getUuid()
));
return;
}
if ($team) {
$teams[] = $team;
}
} else {
// Exclude HubSpot teams - handled by crm:sync-hubspot-objects
$teams = Team::where('status', Team::STATUS_ACTIVE)
->whereHas(
'crm',
fn ($q) => $q
->where('provider', '!=', Configuration::PROVIDER_HUBSPOT)
->where('sync_objects', true)
)
->get();
}
$this->dispatchSyncJobsForTeams($teams);
}
protected function getStaggerDelaySeconds(): float
{
return 2;
}
protected function getMaxDelaySeconds(): int
{
// Cap delay at 15 minutes (SQS max delay limit)
// capacity of 450 teams before reach
return 900;
}
protected function getLogPrefix(): string
{
return '';
}
protected function createSyncJob(Team $team): Job
{
return new SyncObjectsJob($team);
}
}
Code changed:
Hide...
|
49443
|
NULL
|
NULL
|
NULL
|
|
49443
|
1765
|
15
|
2026-05-18T06:44:43.704177+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779086683704_m2.jpg...
|
PhpStorm
|
faVsco.js – TestPipedriveOfficialSdkCommand.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
pipedrive-sdk-poc, menu
S Project: faVsco.js, menu
pipedrive-sdk-poc, 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\Console\Commands\Crm;
use Jiminny\Console\Commands\Command;
use Jiminny\Console\Commands\Crm\Traits\SyncObjectsCommandTrait;
use Jiminny\Jobs\Crm\SyncObjects as SyncObjectsJob;
use Jiminny\Jobs\Job;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Models\Team;
class SyncObjects extends Command
{
use SyncObjectsCommandTrait;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'crm:sync-objects {team?} {--sync}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Sync remote CRM objects.';
/**
* Execute the console command.
*/
public function handle(): void
{
$teamId = $this->argument('team');
$teams = [];
if ($teamId) {
$team = Team::idOrUuId($teamId);
if ($team && $team->crm?->provider === Configuration::PROVIDER_HUBSPOT) {
$this->error(sprintf(
'Team %s (%s) uses HubSpot. Use crm:sync-hubspot-objects instead.',
$team->getName(),
$team->getUuid()
));
return;
}
if ($team) {
$teams[] = $team;
}
} else {
// Exclude HubSpot teams - handled by crm:sync-hubspot-objects
$teams = Team::where('status', Team::STATUS_ACTIVE)
->whereHas(
'crm',
fn ($q) => $q
->where('provider', '!=', Configuration::PROVIDER_HUBSPOT)
->where('sync_objects', true)
)
->get();
}
$this->dispatchSyncJobsForTeams($teams);
}
protected function getStaggerDelaySeconds(): float
{
return 2;
}
protected function getMaxDelaySeconds(): int
{
// Cap delay at 15 minutes (SQS max delay limit)
// capacity of 450 teams before reach
return 900;
}
protected function getLogPrefix(): string
{
return '';
}
protected function createSyncJob(Team $team): Job
{
return new SyncObjectsJob($team);
}
}...
|
[{"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":"pipedrive-sdk-poc, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.05618351,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: pipedrive-sdk-poc","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\\Console\\Commands\\Crm;\n\nuse Jiminny\\Console\\Commands\\Command;\nuse Jiminny\\Console\\Commands\\Crm\\Traits\\SyncObjectsCommandTrait;\nuse Jiminny\\Jobs\\Crm\\SyncObjects as SyncObjectsJob;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Models\\Team;\n\nclass SyncObjects extends Command\n{\n use SyncObjectsCommandTrait;\n\n /**\n * The name and signature of the console command.\n *\n * @var string\n */\n protected $signature = 'crm:sync-objects {team?} {--sync}';\n\n /**\n * The console command description.\n *\n * @var string\n */\n protected $description = 'Sync remote CRM objects.';\n\n /**\n * Execute the console command.\n */\n public function handle(): void\n {\n $teamId = $this->argument('team');\n $teams = [];\n\n if ($teamId) {\n $team = Team::idOrUuId($teamId);\n if ($team && $team->crm?->provider === Configuration::PROVIDER_HUBSPOT) {\n $this->error(sprintf(\n 'Team %s (%s) uses HubSpot. Use crm:sync-hubspot-objects instead.',\n $team->getName(),\n $team->getUuid()\n ));\n\n return;\n }\n if ($team) {\n $teams[] = $team;\n }\n } else {\n // Exclude HubSpot teams - handled by crm:sync-hubspot-objects\n $teams = Team::where('status', Team::STATUS_ACTIVE)\n ->whereHas(\n 'crm',\n fn ($q) => $q\n ->where('provider', '!=', Configuration::PROVIDER_HUBSPOT)\n ->where('sync_objects', true)\n )\n ->get();\n }\n\n $this->dispatchSyncJobsForTeams($teams);\n }\n\n protected function getStaggerDelaySeconds(): float\n {\n return 2;\n }\n\n protected function getMaxDelaySeconds(): int\n {\n // Cap delay at 15 minutes (SQS max delay limit)\n // capacity of 450 teams before reach\n return 900;\n }\n\n protected function getLogPrefix(): string\n {\n return '';\n }\n\n protected function createSyncJob(Team $team): Job\n {\n return new SyncObjectsJob($team);\n }\n}","depth":4,"bounds":{"left":0.15724733,"top":0.2952913,"width":0.21476063,"height":0.7047087},"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Console\\Commands\\Crm;\n\nuse Jiminny\\Console\\Commands\\Command;\nuse Jiminny\\Console\\Commands\\Crm\\Traits\\SyncObjectsCommandTrait;\nuse Jiminny\\Jobs\\Crm\\SyncObjects as SyncObjectsJob;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Models\\Team;\n\nclass SyncObjects extends Command\n{\n use SyncObjectsCommandTrait;\n\n /**\n * The name and signature of the console command.\n *\n * @var string\n */\n protected $signature = 'crm:sync-objects {team?} {--sync}';\n\n /**\n * The console command description.\n *\n * @var string\n */\n protected $description = 'Sync remote CRM objects.';\n\n /**\n * Execute the console command.\n */\n public function handle(): void\n {\n $teamId = $this->argument('team');\n $teams = [];\n\n if ($teamId) {\n $team = Team::idOrUuId($teamId);\n if ($team && $team->crm?->provider === Configuration::PROVIDER_HUBSPOT) {\n $this->error(sprintf(\n 'Team %s (%s) uses HubSpot. Use crm:sync-hubspot-objects instead.',\n $team->getName(),\n $team->getUuid()\n ));\n\n return;\n }\n if ($team) {\n $teams[] = $team;\n }\n } else {\n // Exclude HubSpot teams - handled by crm:sync-hubspot-objects\n $teams = Team::where('status', Team::STATUS_ACTIVE)\n ->whereHas(\n 'crm',\n fn ($q) => $q\n ->where('provider', '!=', Configuration::PROVIDER_HUBSPOT)\n ->where('sync_objects', true)\n )\n ->get();\n }\n\n $this->dispatchSyncJobsForTeams($teams);\n }\n\n protected function getStaggerDelaySeconds(): float\n {\n return 2;\n }\n\n protected function getMaxDelaySeconds(): int\n {\n // Cap delay at 15 minutes (SQS max delay limit)\n // capacity of 450 teams before reach\n return 900;\n }\n\n protected function getLogPrefix(): string\n {\n return '';\n }\n\n protected function createSyncJob(Team $team): Job\n {\n return new SyncObjectsJob($team);\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-7615602772621210147
|
-8678315669563599930
|
click
|
accessibility
|
NULL
|
Project: faVsco.js, menu
pipedrive-sdk-poc, menu
S Project: faVsco.js, menu
pipedrive-sdk-poc, 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\Console\Commands\Crm;
use Jiminny\Console\Commands\Command;
use Jiminny\Console\Commands\Crm\Traits\SyncObjectsCommandTrait;
use Jiminny\Jobs\Crm\SyncObjects as SyncObjectsJob;
use Jiminny\Jobs\Job;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Models\Team;
class SyncObjects extends Command
{
use SyncObjectsCommandTrait;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'crm:sync-objects {team?} {--sync}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Sync remote CRM objects.';
/**
* Execute the console command.
*/
public function handle(): void
{
$teamId = $this->argument('team');
$teams = [];
if ($teamId) {
$team = Team::idOrUuId($teamId);
if ($team && $team->crm?->provider === Configuration::PROVIDER_HUBSPOT) {
$this->error(sprintf(
'Team %s (%s) uses HubSpot. Use crm:sync-hubspot-objects instead.',
$team->getName(),
$team->getUuid()
));
return;
}
if ($team) {
$teams[] = $team;
}
} else {
// Exclude HubSpot teams - handled by crm:sync-hubspot-objects
$teams = Team::where('status', Team::STATUS_ACTIVE)
->whereHas(
'crm',
fn ($q) => $q
->where('provider', '!=', Configuration::PROVIDER_HUBSPOT)
->where('sync_objects', true)
)
->get();
}
$this->dispatchSyncJobsForTeams($teams);
}
protected function getStaggerDelaySeconds(): float
{
return 2;
}
protected function getMaxDelaySeconds(): int
{
// Cap delay at 15 minutes (SQS max delay limit)
// capacity of 450 teams before reach
return 900;
}
protected function getLogPrefix(): string
{
return '';
}
protected function createSyncJob(Team $team): Job
{
return new SyncObjectsJob($team);
}
}...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
49442
|
1764
|
17
|
2026-05-18T06:44:43.704168+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779086683704_m1.jpg...
|
PhpStorm
|
faVsco.js – TestPipedriveOfficialSdkCommand.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
pipedrive-sdk-poc, menu
S Project: faVsco.js, menu
pipedrive-sdk-poc, 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\Console\Commands\Crm;
use Jiminny\Console\Commands\Command;
use Jiminny\Console\Commands\Crm\Traits\SyncObjectsCommandTrait;
use Jiminny\Jobs\Crm\SyncObjects as SyncObjectsJob;
use Jiminny\Jobs\Job;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Models\Team;
class SyncObjects extends Command
{
use SyncObjectsCommandTrait;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'crm:sync-objects {team?} {--sync}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Sync remote CRM objects.';
/**
* Execute the console command.
*/
public function handle(): void
{
$teamId = $this->argument('team');
$teams = [];
if ($teamId) {
$team = Team::idOrUuId($teamId);
if ($team && $team->crm?->provider === Configuration::PROVIDER_HUBSPOT) {
$this->error(sprintf(
'Team %s (%s) uses HubSpot. Use crm:sync-hubspot-objects instead.',
$team->getName(),
$team->getUuid()
));
return;
}
if ($team) {
$teams[] = $team;
}
} else {
// Exclude HubSpot teams - handled by crm:sync-hubspot-objects
$teams = Team::where('status', Team::STATUS_ACTIVE)
->whereHas(
'crm',
fn ($q) => $q
->where('provider', '!=', Configuration::PROVIDER_HUBSPOT)
->where('sync_objects', true)
)
->get();
}
$this->dispatchSyncJobsForTeams($teams);
}
protected function getStaggerDelaySeconds(): float
{
return 2;
}
protected function getMaxDelaySeconds(): int
{
// Cap delay at 15 minutes (SQS max delay limit)
// capacity of 450 teams before reach
return 900;
}
protected function getLogPrefix(): string
{
return '';
}
protected function createSyncJob(Team $team): Job
{
return new SyncObjectsJob($team);
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
1
51
1
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Console\Commands\Crm;
use Carbon\Carbon;
use Exception;
use Illuminate\Console\Command;
use Jiminny\Component\Encryption\EncryptedTokenManager;
use Jiminny\Models\SocialAccount;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Pipedrive\versions\v1\Api\ActivitiesApi;
use Pipedrive\versions\v1\Api\ActivityFieldsApi;
use Pipedrive\versions\v1\Api\DealFieldsApi;
use Pipedrive\versions\v1\Api\DealsApi;
use Pipedrive\versions\v1\Api\PersonFieldsApi;
use Pipedrive\versions\v1\Api\PersonsApi;
use Pipedrive\versions\v1\Configuration;
class TestPipedriveOfficialSdkCommand extends Command
{
protected $signature = 'jiminny:test-pipedrive-official-sdk {teamId}';
protected $description = 'Test official Pipedrive SDK (pipedrive/pipedrive) with existing OAuth tokens';
private array $results = [];
private EncryptedTokenManager $tokenManager;
public function __construct(EncryptedTokenManager $tokenManager)
{
parent::__construct();
$this->tokenManager = $tokenManager;
}
public function handle(): int
{
$teamId = (int) $this->argument('teamId');
$this->info("Starting Pipedrive Official SDK POC for Team ID: {$teamId}");
$this->newLine();
try {
$this->runTests($teamId);
} catch (Exception $e) {
$this->error("POC failed with error: {$e->getMessage()}");
$this->error($e->getTraceAsString());
return 1;
}
$this->displayResults();
return 0;
}
private function runTests(int $teamId): void
{
$team = Team::find($teamId);
if (! $team) {
throw new Exception("Team with ID {$teamId} not found");
}
$socialAccount = SocialAccount::where('provider', SocialAccount::PROVIDER_PIPEDRIVE)
->whereHas('sociable', function ($query) use ($team) {
$query->where('team_id', $team->id);
})
->orderByDesc('expires') // Get the most recently expiring token
->first();
if (! $socialAccount) {
throw new Exception("No Pipedrive social account found for team {$teamId}");
}
$this->info("Found Pipedrive account for team: {$team->name}");
$this->newLine();
// Test 1: OAuth Initialization
$this->testOAuthInitialization($socialAccount);
// Test 2: Basic CRUD Operations
$this->testBasicCrudOperations($socialAccount);
// Test 3: Field Metadata
$this->testFieldMetadata($socialAccount);
// Test 4: v2 API Support
$this->testV2ApiSupport($socialAccount);
// Test 5: Error Handling
$this->testErrorHandling($socialAccount);
}
private function testOAuthInitialization(SocialAccount $socialAccount): void
{
$this->info('Test 1: OAuth Initialization');
$startTime = microtime(true);
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
$client = new DealsApi(null, $config);
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('OAuth Initialization', true, "Client initialized successfully in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('OAuth Initialization', false, $e->getMessage(), $elapsed);
}
$this->newLine();
}
private function testBasicCrudOperations(SocialAccount $socialAccount): void
{
$this->info('Test 2: Basic CRUD Operations');
try {
// Get the raw token from database
$rawDbToken = $socialAccount->getProviderUserToken();
$this->info("DB token (length: " . strlen($rawDbToken) . ")");
$this->info("DB token preview: " . substr($rawDbToken, 0, 20) . "...");
// Check token expiry
$expires = $socialAccount->expires;
$this->info("Token expires at: " . ($expires ? Carbon::createFromTimestamp($expires)->toDateTimeString() : 'null'));
$this->info("Token is expired: " . ($expires && $expires < time() ? 'YES' : 'NO'));
// Test with the DB token directly
$this->info("\n--- Testing with DB token ---");
$config = new Configuration();
$config->setAccessToken($rawDbToken);
// Test 2a: Get Deals Summary
$this->testGetDeals($config);
// Test 2b: Get Persons
$this->testGetPersons($config);
// Test 2c: Get Activities
$this->testGetActivities($config);
} catch (Exception $e) {
$this->recordResult('Basic CRUD Operations', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testCurrentSystemApproach(SocialAccount $socialAccount): void
{
$startTime = microtime(true);
try {
// Simulate how current system makes requests
$token = $socialAccount->getProviderUserToken();
$url = 'https://api.pipedrive.com/api/v2/deals';
$headers = [
'Authorization' => 'Bearer ' . $token,
'Accept' => 'application/json',
];
$client = new \GuzzleHttp\Client();
$response = $client->get($url, [
'headers' => $headers,
'timeout' => 30,
]);
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$statusCode = $response->getStatusCode();
$this->recordResult('Current System v2 API', $statusCode === 200, "Status: {$statusCode} in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Current System v2 API', false, $e->getMessage(), $elapsed);
}
}
private function testGetDeals(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new DealsApi(null, $config);
// Official SDK uses getDealsSummary instead of getDeals
$result = $apiInstance->getDealsSummary();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Deals Summary', true, "Retrieved deals summary in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Deals Summary', false, $e->getMessage(), $elapsed);
}
}
private function testGetPersons(Configuration $config): void
{
$this->info('Test 2b: Get Persons');
$startTime = microtime(true);
try {
$apiInstance = new PersonsApi(null, $config);
// Official SDK uses getAllPersons method
$result = $apiInstance->getAllPersons(['limit' => 10]);
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Persons', true, "Retrieved persons in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Persons', false, $e->getMessage(), $elapsed);
}
}
private function testGetActivities(Configuration $config): void
{
$this->info('Test 2c: Get Activities');
$startTime = microtime(true);
try {
$apiInstance = new ActivitiesApi(null, $config);
// Official SDK uses getAllActivities method
$result = $apiInstance->getAllActivities(['limit' => 10]);
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Activities', true, "Retrieved activities in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Activities', false, $e->getMessage(), $elapsed);
}
}
private function testFieldMetadata(SocialAccount $socialAccount): void
{
$this->info('Test 3: Field Metadata');
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
// Test 3a: Get Deal Fields
$this->testGetDealFields($config);
// Test 3b: Get Person Fields
$this->testGetPersonFields($config);
// Test 3c: Get Activity Fields
$this->testGetActivityFields($config);
} catch (Exception $e) {
$this->recordResult('Field Metadata', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testGetDealFields(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new DealFieldsApi(null, $config);
$result = $apiInstance->getDealFields();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$count = $result->getData() ? count($result->getData()) : 0;
$this->recordResult('Get Deal Fields', true, "Retrieved {$count} deal fields in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Deal Fields', false, $e->getMessage(), $elapsed);
}
}
private function testGetPersonFields(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new PersonFieldsApi(null, $config);
$result = $apiInstance->getPersonFields();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$count = $result->getData() ? count($result->getData()) : 0;
$this->recordResult('Get Person Fields', true, "Retrieved {$count} person fields in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Person Fields', false, $e->getMessage(), $elapsed);
}
}
private function testGetActivityFields(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new ActivityFieldsApi(null, $config);
$result = $apiInstance->getActivityFields();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$count = $result->getData() ? count($result->getData()) : 0;
$this->recordResult('Get Activity Fields', true, "Retrieved {$count} activity fields in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Activity Fields', false, $e->getMessage(), $elapsed);
}
}
private function testV2ApiSupport(SocialAccount $socialAccount): void
{
$this->info('Test 4: v2 API Support (Cursor-based Pagination)');
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
$startTime = microtime(true);
// Test v2 deals endpoint with cursor pagination
// Note: The official SDK may or may not have v2 API support
// This test will determine if v2 is available
try {
$apiInstance = new DealsApi(null, $config);
$updatedSince = Carbon::now()->subDays(30)->toRfc3339String();
$result = $apiInstance->getDealsSummary();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
// Check if response has cursor information (v2 indicator)
// The response object doesn't have getAdditionalData method
// Just test if the call works - v2 API support is limited in official SDK v1
$this->recordResult('v2 API Support', false, "v1 API works but v2 cursor pagination not available in official SDK v1. Retrieved summary in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('v2 API Support', false, $e->getMessage(), $elapsed);
}
} catch (Exception $e) {
$this->recordResult('v2 API Support', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testErrorHandling(SocialAccount $socialAccount): void
{
$this->info('Test 5: Error Handling');
try {
// Test 5a: Invalid Token
$this->testInvalidToken();
// Test 5b: Non-existent Resource
$this->testNonExistentResource($socialAccount);
} catch (Exception $e) {
$this->recordResult('Error Handling', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testInvalidToken(): void
{
$this->info('Test 5a: Invalid Token');
$startTime = microtime(true);
try {
$config = new Configuration();
$config->setAccessToken('invalid_token');
$client = new DealsApi(null, $config);
$client->getDealsSummary();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Invalid Token Error', false, "Expected error but request succeeded in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Invalid Token Error', true, "Correctly threw exception: {$e->getMessage()} in {$elapsed}ms", $elapsed);
}
}
private function testNonExistentResource(SocialAccount $socialAccount): void
{
$this->info('Test 5b: Non-existent Resource');
$startTime = microtime(true);
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
$client = new DealsApi(null, $config);
// Try to get a deal with a very high ID that likely doesn't exist
// Official SDK has getDealsSummary which works without specific ID
// Instead, let's test with an invalid field ID
try {
$fieldClient = new DealFieldsApi(null, $config);
$fieldClient->getDealField(999999999); // Very high ID that likely doesn't exist
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Non-existent Resource Error', false, "Expected error but request succeeded in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Non-existent Resource Error', true, "Correctly threw exception: {$e->getMessage()} in {$elapsed}ms", $elapsed);
}
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Non-existent Resource Error', false, $e->getMessage(), $elapsed);
}
}
private function recordResult(string $testName, bool $success, string $message, float $elapsed): void
{
$this->results[] = [
'test' => $testName,
'success' => $success,
'message' => $message,
'elapsed' => $elapsed,
];
$status = $success ? '✅ PASS' : '❌ FAIL';
$this->line(" {$status} - {$testName}: {$message}");
}
private function displayResults(): void
{
$this->newLine();
$this->info('=== POC Test Results Summary ===');
$this->newLine();
$total = count($this->results);
$passed = count(array_filter($this->results, fn ($r) => $r['success']));
$failed = $total - $passed;
$this->line("Total Tests: {$total}");
$this->line("Passed: {$passed}");
$this->line("Failed: {$failed}");
$this->newLine();
$totalTime = array_sum(array_column($this->results, 'elapsed'));
$this->line("Total Execution Time: {$totalTime}ms");
$this->newLine();
if ($failed > 0) {
$this->warn('Failed Tests:');
foreach ($this->results as $result) {
if (! $result['success']) {
$this->line(" - {$result['test']}: {$result['message']}");
}
}
$this->newLine();
}
$this->info('=== Detailed Results ===');
foreach ($this->results as $result) {
$status = $result['success'] ? '✅' : '❌';
$this->line("{$status} {$result['test']} ({$result['elapsed']}ms): {$result['message']}");
}
$this->newLine();
$this->info('=== Recommendations ===');
if ($passed === $total) {
$this->info('✅ All tests passed! The official SDK is viable for migration.');
$this->info('Next steps: Proceed with full migration planning.');
} else {
$this->warn('⚠️ Some tests failed. Review the failures above before proceeding.');
$this->warn('Consider hybrid approach or investigate alternatives if critical operations fail.');
}
}
}
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":"pipedrive-sdk-poc, menu","depth":5,"on_screen":true,"help_text":"Git Branch: pipedrive-sdk-poc","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\\Console\\Commands\\Crm;\n\nuse Jiminny\\Console\\Commands\\Command;\nuse Jiminny\\Console\\Commands\\Crm\\Traits\\SyncObjectsCommandTrait;\nuse Jiminny\\Jobs\\Crm\\SyncObjects as SyncObjectsJob;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Models\\Team;\n\nclass SyncObjects extends Command\n{\n use SyncObjectsCommandTrait;\n\n /**\n * The name and signature of the console command.\n *\n * @var string\n */\n protected $signature = 'crm:sync-objects {team?} {--sync}';\n\n /**\n * The console command description.\n *\n * @var string\n */\n protected $description = 'Sync remote CRM objects.';\n\n /**\n * Execute the console command.\n */\n public function handle(): void\n {\n $teamId = $this->argument('team');\n $teams = [];\n\n if ($teamId) {\n $team = Team::idOrUuId($teamId);\n if ($team && $team->crm?->provider === Configuration::PROVIDER_HUBSPOT) {\n $this->error(sprintf(\n 'Team %s (%s) uses HubSpot. Use crm:sync-hubspot-objects instead.',\n $team->getName(),\n $team->getUuid()\n ));\n\n return;\n }\n if ($team) {\n $teams[] = $team;\n }\n } else {\n // Exclude HubSpot teams - handled by crm:sync-hubspot-objects\n $teams = Team::where('status', Team::STATUS_ACTIVE)\n ->whereHas(\n 'crm',\n fn ($q) => $q\n ->where('provider', '!=', Configuration::PROVIDER_HUBSPOT)\n ->where('sync_objects', true)\n )\n ->get();\n }\n\n $this->dispatchSyncJobsForTeams($teams);\n }\n\n protected function getStaggerDelaySeconds(): float\n {\n return 2;\n }\n\n protected function getMaxDelaySeconds(): int\n {\n // Cap delay at 15 minutes (SQS max delay limit)\n // capacity of 450 teams before reach\n return 900;\n }\n\n protected function getLogPrefix(): string\n {\n return '';\n }\n\n protected function createSyncJob(Team $team): Job\n {\n return new SyncObjectsJob($team);\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Console\\Commands\\Crm;\n\nuse Jiminny\\Console\\Commands\\Command;\nuse Jiminny\\Console\\Commands\\Crm\\Traits\\SyncObjectsCommandTrait;\nuse Jiminny\\Jobs\\Crm\\SyncObjects as SyncObjectsJob;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Models\\Team;\n\nclass SyncObjects extends Command\n{\n use SyncObjectsCommandTrait;\n\n /**\n * The name and signature of the console command.\n *\n * @var string\n */\n protected $signature = 'crm:sync-objects {team?} {--sync}';\n\n /**\n * The console command description.\n *\n * @var string\n */\n protected $description = 'Sync remote CRM objects.';\n\n /**\n * Execute the console command.\n */\n public function handle(): void\n {\n $teamId = $this->argument('team');\n $teams = [];\n\n if ($teamId) {\n $team = Team::idOrUuId($teamId);\n if ($team && $team->crm?->provider === Configuration::PROVIDER_HUBSPOT) {\n $this->error(sprintf(\n 'Team %s (%s) uses HubSpot. Use crm:sync-hubspot-objects instead.',\n $team->getName(),\n $team->getUuid()\n ));\n\n return;\n }\n if ($team) {\n $teams[] = $team;\n }\n } else {\n // Exclude HubSpot teams - handled by crm:sync-hubspot-objects\n $teams = Team::where('status', Team::STATUS_ACTIVE)\n ->whereHas(\n 'crm',\n fn ($q) => $q\n ->where('provider', '!=', Configuration::PROVIDER_HUBSPOT)\n ->where('sync_objects', true)\n )\n ->get();\n }\n\n $this->dispatchSyncJobsForTeams($teams);\n }\n\n protected function getStaggerDelaySeconds(): float\n {\n return 2;\n }\n\n protected function getMaxDelaySeconds(): int\n {\n // Cap delay at 15 minutes (SQS max delay limit)\n // capacity of 450 teams before reach\n return 900;\n }\n\n protected function getLogPrefix(): string\n {\n return '';\n }\n\n protected function createSyncJob(Team $team): Job\n {\n return new SyncObjectsJob($team);\n }\n}","role_description":"text entry area","is_enabled":true,"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":"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":"AXStaticText","text":"1","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.015277778,"height":0.02111111},"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"51","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.02013889,"height":0.02111111},"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"1","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.015277778,"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\\Console\\Commands\\Crm;\n\nuse Carbon\\Carbon;\nuse Exception;\nuse Illuminate\\Console\\Command;\nuse Jiminny\\Component\\Encryption\\EncryptedTokenManager;\nuse Jiminny\\Models\\SocialAccount;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Pipedrive\\versions\\v1\\Api\\ActivitiesApi;\nuse Pipedrive\\versions\\v1\\Api\\ActivityFieldsApi;\nuse Pipedrive\\versions\\v1\\Api\\DealFieldsApi;\nuse Pipedrive\\versions\\v1\\Api\\DealsApi;\nuse Pipedrive\\versions\\v1\\Api\\PersonFieldsApi;\nuse Pipedrive\\versions\\v1\\Api\\PersonsApi;\nuse Pipedrive\\versions\\v1\\Configuration;\n\nclass TestPipedriveOfficialSdkCommand extends Command\n{\n protected $signature = 'jiminny:test-pipedrive-official-sdk {teamId}';\n\n protected $description = 'Test official Pipedrive SDK (pipedrive/pipedrive) with existing OAuth tokens';\n\n private array $results = [];\n\n private EncryptedTokenManager $tokenManager;\n\n public function __construct(EncryptedTokenManager $tokenManager)\n {\n parent::__construct();\n $this->tokenManager = $tokenManager;\n }\n\n public function handle(): int\n {\n $teamId = (int) $this->argument('teamId');\n\n $this->info(\"Starting Pipedrive Official SDK POC for Team ID: {$teamId}\");\n $this->newLine();\n\n try {\n $this->runTests($teamId);\n } catch (Exception $e) {\n $this->error(\"POC failed with error: {$e->getMessage()}\");\n $this->error($e->getTraceAsString());\n\n return 1;\n }\n\n $this->displayResults();\n\n return 0;\n }\n\n private function runTests(int $teamId): void\n {\n $team = Team::find($teamId);\n if (! $team) {\n throw new Exception(\"Team with ID {$teamId} not found\");\n }\n\n $socialAccount = SocialAccount::where('provider', SocialAccount::PROVIDER_PIPEDRIVE)\n ->whereHas('sociable', function ($query) use ($team) {\n $query->where('team_id', $team->id);\n })\n ->orderByDesc('expires') // Get the most recently expiring token\n ->first();\n\n if (! $socialAccount) {\n throw new Exception(\"No Pipedrive social account found for team {$teamId}\");\n }\n\n $this->info(\"Found Pipedrive account for team: {$team->name}\");\n $this->newLine();\n\n // Test 1: OAuth Initialization\n $this->testOAuthInitialization($socialAccount);\n\n // Test 2: Basic CRUD Operations\n $this->testBasicCrudOperations($socialAccount);\n\n // Test 3: Field Metadata\n $this->testFieldMetadata($socialAccount);\n\n // Test 4: v2 API Support\n $this->testV2ApiSupport($socialAccount);\n\n // Test 5: Error Handling\n $this->testErrorHandling($socialAccount);\n }\n\n private function testOAuthInitialization(SocialAccount $socialAccount): void\n {\n $this->info('Test 1: OAuth Initialization');\n $startTime = microtime(true);\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n\n $client = new DealsApi(null, $config);\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('OAuth Initialization', true, \"Client initialized successfully in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('OAuth Initialization', false, $e->getMessage(), $elapsed);\n }\n\n $this->newLine();\n }\n\n private function testBasicCrudOperations(SocialAccount $socialAccount): void\n {\n $this->info('Test 2: Basic CRUD Operations');\n\n try {\n // Get the raw token from database\n $rawDbToken = $socialAccount->getProviderUserToken();\n $this->info(\"DB token (length: \" . strlen($rawDbToken) . \")\");\n $this->info(\"DB token preview: \" . substr($rawDbToken, 0, 20) . \"...\");\n\n // Check token expiry\n $expires = $socialAccount->expires;\n $this->info(\"Token expires at: \" . ($expires ? Carbon::createFromTimestamp($expires)->toDateTimeString() : 'null'));\n $this->info(\"Token is expired: \" . ($expires && $expires < time() ? 'YES' : 'NO'));\n\n // Test with the DB token directly\n $this->info(\"\\n--- Testing with DB token ---\");\n $config = new Configuration();\n $config->setAccessToken($rawDbToken);\n\n // Test 2a: Get Deals Summary\n $this->testGetDeals($config);\n\n // Test 2b: Get Persons\n $this->testGetPersons($config);\n\n // Test 2c: Get Activities\n $this->testGetActivities($config);\n } catch (Exception $e) {\n $this->recordResult('Basic CRUD Operations', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testCurrentSystemApproach(SocialAccount $socialAccount): void\n {\n $startTime = microtime(true);\n\n try {\n // Simulate how current system makes requests\n $token = $socialAccount->getProviderUserToken();\n $url = 'https://api.pipedrive.com/api/v2/deals';\n\n $headers = [\n 'Authorization' => 'Bearer ' . $token,\n 'Accept' => 'application/json',\n ];\n\n $client = new \\GuzzleHttp\\Client();\n $response = $client->get($url, [\n 'headers' => $headers,\n 'timeout' => 30,\n ]);\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $statusCode = $response->getStatusCode();\n $this->recordResult('Current System v2 API', $statusCode === 200, \"Status: {$statusCode} in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Current System v2 API', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetDeals(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new DealsApi(null, $config);\n // Official SDK uses getDealsSummary instead of getDeals\n $result = $apiInstance->getDealsSummary();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Deals Summary', true, \"Retrieved deals summary in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Deals Summary', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetPersons(Configuration $config): void\n {\n $this->info('Test 2b: Get Persons');\n $startTime = microtime(true);\n\n try {\n $apiInstance = new PersonsApi(null, $config);\n // Official SDK uses getAllPersons method\n $result = $apiInstance->getAllPersons(['limit' => 10]);\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Persons', true, \"Retrieved persons in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Persons', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetActivities(Configuration $config): void\n {\n $this->info('Test 2c: Get Activities');\n $startTime = microtime(true);\n\n try {\n $apiInstance = new ActivitiesApi(null, $config);\n // Official SDK uses getAllActivities method\n $result = $apiInstance->getAllActivities(['limit' => 10]);\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Activities', true, \"Retrieved activities in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Activities', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testFieldMetadata(SocialAccount $socialAccount): void\n {\n $this->info('Test 3: Field Metadata');\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n\n // Test 3a: Get Deal Fields\n $this->testGetDealFields($config);\n\n // Test 3b: Get Person Fields\n $this->testGetPersonFields($config);\n\n // Test 3c: Get Activity Fields\n $this->testGetActivityFields($config);\n } catch (Exception $e) {\n $this->recordResult('Field Metadata', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testGetDealFields(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new DealFieldsApi(null, $config);\n $result = $apiInstance->getDealFields();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $count = $result->getData() ? count($result->getData()) : 0;\n $this->recordResult('Get Deal Fields', true, \"Retrieved {$count} deal fields in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Deal Fields', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetPersonFields(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new PersonFieldsApi(null, $config);\n $result = $apiInstance->getPersonFields();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $count = $result->getData() ? count($result->getData()) : 0;\n $this->recordResult('Get Person Fields', true, \"Retrieved {$count} person fields in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Person Fields', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetActivityFields(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new ActivityFieldsApi(null, $config);\n $result = $apiInstance->getActivityFields();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $count = $result->getData() ? count($result->getData()) : 0;\n $this->recordResult('Get Activity Fields', true, \"Retrieved {$count} activity fields in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Activity Fields', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testV2ApiSupport(SocialAccount $socialAccount): void\n {\n $this->info('Test 4: v2 API Support (Cursor-based Pagination)');\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n\n $startTime = microtime(true);\n\n // Test v2 deals endpoint with cursor pagination\n // Note: The official SDK may or may not have v2 API support\n // This test will determine if v2 is available\n try {\n $apiInstance = new DealsApi(null, $config);\n $updatedSince = Carbon::now()->subDays(30)->toRfc3339String();\n $result = $apiInstance->getDealsSummary();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n\n // Check if response has cursor information (v2 indicator)\n // The response object doesn't have getAdditionalData method\n // Just test if the call works - v2 API support is limited in official SDK v1\n $this->recordResult('v2 API Support', false, \"v1 API works but v2 cursor pagination not available in official SDK v1. Retrieved summary in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('v2 API Support', false, $e->getMessage(), $elapsed);\n }\n } catch (Exception $e) {\n $this->recordResult('v2 API Support', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testErrorHandling(SocialAccount $socialAccount): void\n {\n $this->info('Test 5: Error Handling');\n\n try {\n // Test 5a: Invalid Token\n $this->testInvalidToken();\n\n // Test 5b: Non-existent Resource\n $this->testNonExistentResource($socialAccount);\n } catch (Exception $e) {\n $this->recordResult('Error Handling', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testInvalidToken(): void\n {\n $this->info('Test 5a: Invalid Token');\n $startTime = microtime(true);\n\n try {\n $config = new Configuration();\n $config->setAccessToken('invalid_token');\n $client = new DealsApi(null, $config);\n\n $client->getDealsSummary();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Invalid Token Error', false, \"Expected error but request succeeded in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Invalid Token Error', true, \"Correctly threw exception: {$e->getMessage()} in {$elapsed}ms\", $elapsed);\n }\n }\n\n private function testNonExistentResource(SocialAccount $socialAccount): void\n {\n $this->info('Test 5b: Non-existent Resource');\n $startTime = microtime(true);\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n $client = new DealsApi(null, $config);\n\n // Try to get a deal with a very high ID that likely doesn't exist\n // Official SDK has getDealsSummary which works without specific ID\n // Instead, let's test with an invalid field ID\n try {\n $fieldClient = new DealFieldsApi(null, $config);\n $fieldClient->getDealField(999999999); // Very high ID that likely doesn't exist\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Non-existent Resource Error', false, \"Expected error but request succeeded in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Non-existent Resource Error', true, \"Correctly threw exception: {$e->getMessage()} in {$elapsed}ms\", $elapsed);\n }\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Non-existent Resource Error', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function recordResult(string $testName, bool $success, string $message, float $elapsed): void\n {\n $this->results[] = [\n 'test' => $testName,\n 'success' => $success,\n 'message' => $message,\n 'elapsed' => $elapsed,\n ];\n\n $status = $success ? '✅ PASS' : '❌ FAIL';\n $this->line(\" {$status} - {$testName}: {$message}\");\n }\n\n private function displayResults(): void\n {\n $this->newLine();\n $this->info('=== POC Test Results Summary ===');\n $this->newLine();\n\n $total = count($this->results);\n $passed = count(array_filter($this->results, fn ($r) => $r['success']));\n $failed = $total - $passed;\n\n $this->line(\"Total Tests: {$total}\");\n $this->line(\"Passed: {$passed}\");\n $this->line(\"Failed: {$failed}\");\n $this->newLine();\n\n $totalTime = array_sum(array_column($this->results, 'elapsed'));\n $this->line(\"Total Execution Time: {$totalTime}ms\");\n $this->newLine();\n\n if ($failed > 0) {\n $this->warn('Failed Tests:');\n foreach ($this->results as $result) {\n if (! $result['success']) {\n $this->line(\" - {$result['test']}: {$result['message']}\");\n }\n }\n $this->newLine();\n }\n\n $this->info('=== Detailed Results ===');\n foreach ($this->results as $result) {\n $status = $result['success'] ? '✅' : '❌';\n $this->line(\"{$status} {$result['test']} ({$result['elapsed']}ms): {$result['message']}\");\n }\n\n $this->newLine();\n $this->info('=== Recommendations ===');\n\n if ($passed === $total) {\n $this->info('✅ All tests passed! The official SDK is viable for migration.');\n $this->info('Next steps: Proceed with full migration planning.');\n } else {\n $this->warn('⚠️ Some tests failed. Review the failures above before proceeding.');\n $this->warn('Consider hybrid approach or investigate alternatives if critical operations fail.');\n }\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Console\\Commands\\Crm;\n\nuse Carbon\\Carbon;\nuse Exception;\nuse Illuminate\\Console\\Command;\nuse Jiminny\\Component\\Encryption\\EncryptedTokenManager;\nuse Jiminny\\Models\\SocialAccount;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Pipedrive\\versions\\v1\\Api\\ActivitiesApi;\nuse Pipedrive\\versions\\v1\\Api\\ActivityFieldsApi;\nuse Pipedrive\\versions\\v1\\Api\\DealFieldsApi;\nuse Pipedrive\\versions\\v1\\Api\\DealsApi;\nuse Pipedrive\\versions\\v1\\Api\\PersonFieldsApi;\nuse Pipedrive\\versions\\v1\\Api\\PersonsApi;\nuse Pipedrive\\versions\\v1\\Configuration;\n\nclass TestPipedriveOfficialSdkCommand extends Command\n{\n protected $signature = 'jiminny:test-pipedrive-official-sdk {teamId}';\n\n protected $description = 'Test official Pipedrive SDK (pipedrive/pipedrive) with existing OAuth tokens';\n\n private array $results = [];\n\n private EncryptedTokenManager $tokenManager;\n\n public function __construct(EncryptedTokenManager $tokenManager)\n {\n parent::__construct();\n $this->tokenManager = $tokenManager;\n }\n\n public function handle(): int\n {\n $teamId = (int) $this->argument('teamId');\n\n $this->info(\"Starting Pipedrive Official SDK POC for Team ID: {$teamId}\");\n $this->newLine();\n\n try {\n $this->runTests($teamId);\n } catch (Exception $e) {\n $this->error(\"POC failed with error: {$e->getMessage()}\");\n $this->error($e->getTraceAsString());\n\n return 1;\n }\n\n $this->displayResults();\n\n return 0;\n }\n\n private function runTests(int $teamId): void\n {\n $team = Team::find($teamId);\n if (! $team) {\n throw new Exception(\"Team with ID {$teamId} not found\");\n }\n\n $socialAccount = SocialAccount::where('provider', SocialAccount::PROVIDER_PIPEDRIVE)\n ->whereHas('sociable', function ($query) use ($team) {\n $query->where('team_id', $team->id);\n })\n ->orderByDesc('expires') // Get the most recently expiring token\n ->first();\n\n if (! $socialAccount) {\n throw new Exception(\"No Pipedrive social account found for team {$teamId}\");\n }\n\n $this->info(\"Found Pipedrive account for team: {$team->name}\");\n $this->newLine();\n\n // Test 1: OAuth Initialization\n $this->testOAuthInitialization($socialAccount);\n\n // Test 2: Basic CRUD Operations\n $this->testBasicCrudOperations($socialAccount);\n\n // Test 3: Field Metadata\n $this->testFieldMetadata($socialAccount);\n\n // Test 4: v2 API Support\n $this->testV2ApiSupport($socialAccount);\n\n // Test 5: Error Handling\n $this->testErrorHandling($socialAccount);\n }\n\n private function testOAuthInitialization(SocialAccount $socialAccount): void\n {\n $this->info('Test 1: OAuth Initialization');\n $startTime = microtime(true);\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n\n $client = new DealsApi(null, $config);\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('OAuth Initialization', true, \"Client initialized successfully in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('OAuth Initialization', false, $e->getMessage(), $elapsed);\n }\n\n $this->newLine();\n }\n\n private function testBasicCrudOperations(SocialAccount $socialAccount): void\n {\n $this->info('Test 2: Basic CRUD Operations');\n\n try {\n // Get the raw token from database\n $rawDbToken = $socialAccount->getProviderUserToken();\n $this->info(\"DB token (length: \" . strlen($rawDbToken) . \")\");\n $this->info(\"DB token preview: \" . substr($rawDbToken, 0, 20) . \"...\");\n\n // Check token expiry\n $expires = $socialAccount->expires;\n $this->info(\"Token expires at: \" . ($expires ? Carbon::createFromTimestamp($expires)->toDateTimeString() : 'null'));\n $this->info(\"Token is expired: \" . ($expires && $expires < time() ? 'YES' : 'NO'));\n\n // Test with the DB token directly\n $this->info(\"\\n--- Testing with DB token ---\");\n $config = new Configuration();\n $config->setAccessToken($rawDbToken);\n\n // Test 2a: Get Deals Summary\n $this->testGetDeals($config);\n\n // Test 2b: Get Persons\n $this->testGetPersons($config);\n\n // Test 2c: Get Activities\n $this->testGetActivities($config);\n } catch (Exception $e) {\n $this->recordResult('Basic CRUD Operations', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testCurrentSystemApproach(SocialAccount $socialAccount): void\n {\n $startTime = microtime(true);\n\n try {\n // Simulate how current system makes requests\n $token = $socialAccount->getProviderUserToken();\n $url = 'https://api.pipedrive.com/api/v2/deals';\n\n $headers = [\n 'Authorization' => 'Bearer ' . $token,\n 'Accept' => 'application/json',\n ];\n\n $client = new \\GuzzleHttp\\Client();\n $response = $client->get($url, [\n 'headers' => $headers,\n 'timeout' => 30,\n ]);\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $statusCode = $response->getStatusCode();\n $this->recordResult('Current System v2 API', $statusCode === 200, \"Status: {$statusCode} in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Current System v2 API', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetDeals(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new DealsApi(null, $config);\n // Official SDK uses getDealsSummary instead of getDeals\n $result = $apiInstance->getDealsSummary();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Deals Summary', true, \"Retrieved deals summary in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Deals Summary', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetPersons(Configuration $config): void\n {\n $this->info('Test 2b: Get Persons');\n $startTime = microtime(true);\n\n try {\n $apiInstance = new PersonsApi(null, $config);\n // Official SDK uses getAllPersons method\n $result = $apiInstance->getAllPersons(['limit' => 10]);\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Persons', true, \"Retrieved persons in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Persons', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetActivities(Configuration $config): void\n {\n $this->info('Test 2c: Get Activities');\n $startTime = microtime(true);\n\n try {\n $apiInstance = new ActivitiesApi(null, $config);\n // Official SDK uses getAllActivities method\n $result = $apiInstance->getAllActivities(['limit' => 10]);\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Activities', true, \"Retrieved activities in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Activities', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testFieldMetadata(SocialAccount $socialAccount): void\n {\n $this->info('Test 3: Field Metadata');\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n\n // Test 3a: Get Deal Fields\n $this->testGetDealFields($config);\n\n // Test 3b: Get Person Fields\n $this->testGetPersonFields($config);\n\n // Test 3c: Get Activity Fields\n $this->testGetActivityFields($config);\n } catch (Exception $e) {\n $this->recordResult('Field Metadata', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testGetDealFields(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new DealFieldsApi(null, $config);\n $result = $apiInstance->getDealFields();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $count = $result->getData() ? count($result->getData()) : 0;\n $this->recordResult('Get Deal Fields', true, \"Retrieved {$count} deal fields in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Deal Fields', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetPersonFields(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new PersonFieldsApi(null, $config);\n $result = $apiInstance->getPersonFields();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $count = $result->getData() ? count($result->getData()) : 0;\n $this->recordResult('Get Person Fields', true, \"Retrieved {$count} person fields in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Person Fields', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetActivityFields(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new ActivityFieldsApi(null, $config);\n $result = $apiInstance->getActivityFields();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $count = $result->getData() ? count($result->getData()) : 0;\n $this->recordResult('Get Activity Fields', true, \"Retrieved {$count} activity fields in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Activity Fields', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testV2ApiSupport(SocialAccount $socialAccount): void\n {\n $this->info('Test 4: v2 API Support (Cursor-based Pagination)');\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n\n $startTime = microtime(true);\n\n // Test v2 deals endpoint with cursor pagination\n // Note: The official SDK may or may not have v2 API support\n // This test will determine if v2 is available\n try {\n $apiInstance = new DealsApi(null, $config);\n $updatedSince = Carbon::now()->subDays(30)->toRfc3339String();\n $result = $apiInstance->getDealsSummary();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n\n // Check if response has cursor information (v2 indicator)\n // The response object doesn't have getAdditionalData method\n // Just test if the call works - v2 API support is limited in official SDK v1\n $this->recordResult('v2 API Support', false, \"v1 API works but v2 cursor pagination not available in official SDK v1. Retrieved summary in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('v2 API Support', false, $e->getMessage(), $elapsed);\n }\n } catch (Exception $e) {\n $this->recordResult('v2 API Support', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testErrorHandling(SocialAccount $socialAccount): void\n {\n $this->info('Test 5: Error Handling');\n\n try {\n // Test 5a: Invalid Token\n $this->testInvalidToken();\n\n // Test 5b: Non-existent Resource\n $this->testNonExistentResource($socialAccount);\n } catch (Exception $e) {\n $this->recordResult('Error Handling', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testInvalidToken(): void\n {\n $this->info('Test 5a: Invalid Token');\n $startTime = microtime(true);\n\n try {\n $config = new Configuration();\n $config->setAccessToken('invalid_token');\n $client = new DealsApi(null, $config);\n\n $client->getDealsSummary();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Invalid Token Error', false, \"Expected error but request succeeded in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Invalid Token Error', true, \"Correctly threw exception: {$e->getMessage()} in {$elapsed}ms\", $elapsed);\n }\n }\n\n private function testNonExistentResource(SocialAccount $socialAccount): void\n {\n $this->info('Test 5b: Non-existent Resource');\n $startTime = microtime(true);\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n $client = new DealsApi(null, $config);\n\n // Try to get a deal with a very high ID that likely doesn't exist\n // Official SDK has getDealsSummary which works without specific ID\n // Instead, let's test with an invalid field ID\n try {\n $fieldClient = new DealFieldsApi(null, $config);\n $fieldClient->getDealField(999999999); // Very high ID that likely doesn't exist\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Non-existent Resource Error', false, \"Expected error but request succeeded in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Non-existent Resource Error', true, \"Correctly threw exception: {$e->getMessage()} in {$elapsed}ms\", $elapsed);\n }\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Non-existent Resource Error', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function recordResult(string $testName, bool $success, string $message, float $elapsed): void\n {\n $this->results[] = [\n 'test' => $testName,\n 'success' => $success,\n 'message' => $message,\n 'elapsed' => $elapsed,\n ];\n\n $status = $success ? '✅ PASS' : '❌ FAIL';\n $this->line(\" {$status} - {$testName}: {$message}\");\n }\n\n private function displayResults(): void\n {\n $this->newLine();\n $this->info('=== POC Test Results Summary ===');\n $this->newLine();\n\n $total = count($this->results);\n $passed = count(array_filter($this->results, fn ($r) => $r['success']));\n $failed = $total - $passed;\n\n $this->line(\"Total Tests: {$total}\");\n $this->line(\"Passed: {$passed}\");\n $this->line(\"Failed: {$failed}\");\n $this->newLine();\n\n $totalTime = array_sum(array_column($this->results, 'elapsed'));\n $this->line(\"Total Execution Time: {$totalTime}ms\");\n $this->newLine();\n\n if ($failed > 0) {\n $this->warn('Failed Tests:');\n foreach ($this->results as $result) {\n if (! $result['success']) {\n $this->line(\" - {$result['test']}: {$result['message']}\");\n }\n }\n $this->newLine();\n }\n\n $this->info('=== Detailed Results ===');\n foreach ($this->results as $result) {\n $status = $result['success'] ? '✅' : '❌';\n $this->line(\"{$status} {$result['test']} ({$result['elapsed']}ms): {$result['message']}\");\n }\n\n $this->newLine();\n $this->info('=== Recommendations ===');\n\n if ($passed === $total) {\n $this->info('✅ All tests passed! The official SDK is viable for migration.');\n $this->info('Next steps: Proceed with full migration planning.');\n } else {\n $this->warn('⚠️ Some tests failed. Review the failures above before proceeding.');\n $this->warn('Consider hybrid approach or investigate alternatives if critical operations fail.');\n }\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":true,"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}]...
|
-7411996384006884351
|
34631079629834884
|
click
|
accessibility
|
NULL
|
Project: faVsco.js, menu
pipedrive-sdk-poc, menu
S Project: faVsco.js, menu
pipedrive-sdk-poc, 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\Console\Commands\Crm;
use Jiminny\Console\Commands\Command;
use Jiminny\Console\Commands\Crm\Traits\SyncObjectsCommandTrait;
use Jiminny\Jobs\Crm\SyncObjects as SyncObjectsJob;
use Jiminny\Jobs\Job;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Models\Team;
class SyncObjects extends Command
{
use SyncObjectsCommandTrait;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'crm:sync-objects {team?} {--sync}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Sync remote CRM objects.';
/**
* Execute the console command.
*/
public function handle(): void
{
$teamId = $this->argument('team');
$teams = [];
if ($teamId) {
$team = Team::idOrUuId($teamId);
if ($team && $team->crm?->provider === Configuration::PROVIDER_HUBSPOT) {
$this->error(sprintf(
'Team %s (%s) uses HubSpot. Use crm:sync-hubspot-objects instead.',
$team->getName(),
$team->getUuid()
));
return;
}
if ($team) {
$teams[] = $team;
}
} else {
// Exclude HubSpot teams - handled by crm:sync-hubspot-objects
$teams = Team::where('status', Team::STATUS_ACTIVE)
->whereHas(
'crm',
fn ($q) => $q
->where('provider', '!=', Configuration::PROVIDER_HUBSPOT)
->where('sync_objects', true)
)
->get();
}
$this->dispatchSyncJobsForTeams($teams);
}
protected function getStaggerDelaySeconds(): float
{
return 2;
}
protected function getMaxDelaySeconds(): int
{
// Cap delay at 15 minutes (SQS max delay limit)
// capacity of 450 teams before reach
return 900;
}
protected function getLogPrefix(): string
{
return '';
}
protected function createSyncJob(Team $team): Job
{
return new SyncObjectsJob($team);
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
1
51
1
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Console\Commands\Crm;
use Carbon\Carbon;
use Exception;
use Illuminate\Console\Command;
use Jiminny\Component\Encryption\EncryptedTokenManager;
use Jiminny\Models\SocialAccount;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Pipedrive\versions\v1\Api\ActivitiesApi;
use Pipedrive\versions\v1\Api\ActivityFieldsApi;
use Pipedrive\versions\v1\Api\DealFieldsApi;
use Pipedrive\versions\v1\Api\DealsApi;
use Pipedrive\versions\v1\Api\PersonFieldsApi;
use Pipedrive\versions\v1\Api\PersonsApi;
use Pipedrive\versions\v1\Configuration;
class TestPipedriveOfficialSdkCommand extends Command
{
protected $signature = 'jiminny:test-pipedrive-official-sdk {teamId}';
protected $description = 'Test official Pipedrive SDK (pipedrive/pipedrive) with existing OAuth tokens';
private array $results = [];
private EncryptedTokenManager $tokenManager;
public function __construct(EncryptedTokenManager $tokenManager)
{
parent::__construct();
$this->tokenManager = $tokenManager;
}
public function handle(): int
{
$teamId = (int) $this->argument('teamId');
$this->info("Starting Pipedrive Official SDK POC for Team ID: {$teamId}");
$this->newLine();
try {
$this->runTests($teamId);
} catch (Exception $e) {
$this->error("POC failed with error: {$e->getMessage()}");
$this->error($e->getTraceAsString());
return 1;
}
$this->displayResults();
return 0;
}
private function runTests(int $teamId): void
{
$team = Team::find($teamId);
if (! $team) {
throw new Exception("Team with ID {$teamId} not found");
}
$socialAccount = SocialAccount::where('provider', SocialAccount::PROVIDER_PIPEDRIVE)
->whereHas('sociable', function ($query) use ($team) {
$query->where('team_id', $team->id);
})
->orderByDesc('expires') // Get the most recently expiring token
->first();
if (! $socialAccount) {
throw new Exception("No Pipedrive social account found for team {$teamId}");
}
$this->info("Found Pipedrive account for team: {$team->name}");
$this->newLine();
// Test 1: OAuth Initialization
$this->testOAuthInitialization($socialAccount);
// Test 2: Basic CRUD Operations
$this->testBasicCrudOperations($socialAccount);
// Test 3: Field Metadata
$this->testFieldMetadata($socialAccount);
// Test 4: v2 API Support
$this->testV2ApiSupport($socialAccount);
// Test 5: Error Handling
$this->testErrorHandling($socialAccount);
}
private function testOAuthInitialization(SocialAccount $socialAccount): void
{
$this->info('Test 1: OAuth Initialization');
$startTime = microtime(true);
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
$client = new DealsApi(null, $config);
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('OAuth Initialization', true, "Client initialized successfully in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('OAuth Initialization', false, $e->getMessage(), $elapsed);
}
$this->newLine();
}
private function testBasicCrudOperations(SocialAccount $socialAccount): void
{
$this->info('Test 2: Basic CRUD Operations');
try {
// Get the raw token from database
$rawDbToken = $socialAccount->getProviderUserToken();
$this->info("DB token (length: " . strlen($rawDbToken) . ")");
$this->info("DB token preview: " . substr($rawDbToken, 0, 20) . "...");
// Check token expiry
$expires = $socialAccount->expires;
$this->info("Token expires at: " . ($expires ? Carbon::createFromTimestamp($expires)->toDateTimeString() : 'null'));
$this->info("Token is expired: " . ($expires && $expires < time() ? 'YES' : 'NO'));
// Test with the DB token directly
$this->info("\n--- Testing with DB token ---");
$config = new Configuration();
$config->setAccessToken($rawDbToken);
// Test 2a: Get Deals Summary
$this->testGetDeals($config);
// Test 2b: Get Persons
$this->testGetPersons($config);
// Test 2c: Get Activities
$this->testGetActivities($config);
} catch (Exception $e) {
$this->recordResult('Basic CRUD Operations', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testCurrentSystemApproach(SocialAccount $socialAccount): void
{
$startTime = microtime(true);
try {
// Simulate how current system makes requests
$token = $socialAccount->getProviderUserToken();
$url = 'https://api.pipedrive.com/api/v2/deals';
$headers = [
'Authorization' => 'Bearer ' . $token,
'Accept' => 'application/json',
];
$client = new \GuzzleHttp\Client();
$response = $client->get($url, [
'headers' => $headers,
'timeout' => 30,
]);
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$statusCode = $response->getStatusCode();
$this->recordResult('Current System v2 API', $statusCode === 200, "Status: {$statusCode} in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Current System v2 API', false, $e->getMessage(), $elapsed);
}
}
private function testGetDeals(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new DealsApi(null, $config);
// Official SDK uses getDealsSummary instead of getDeals
$result = $apiInstance->getDealsSummary();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Deals Summary', true, "Retrieved deals summary in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Deals Summary', false, $e->getMessage(), $elapsed);
}
}
private function testGetPersons(Configuration $config): void
{
$this->info('Test 2b: Get Persons');
$startTime = microtime(true);
try {
$apiInstance = new PersonsApi(null, $config);
// Official SDK uses getAllPersons method
$result = $apiInstance->getAllPersons(['limit' => 10]);
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Persons', true, "Retrieved persons in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Persons', false, $e->getMessage(), $elapsed);
}
}
private function testGetActivities(Configuration $config): void
{
$this->info('Test 2c: Get Activities');
$startTime = microtime(true);
try {
$apiInstance = new ActivitiesApi(null, $config);
// Official SDK uses getAllActivities method
$result = $apiInstance->getAllActivities(['limit' => 10]);
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Activities', true, "Retrieved activities in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Activities', false, $e->getMessage(), $elapsed);
}
}
private function testFieldMetadata(SocialAccount $socialAccount): void
{
$this->info('Test 3: Field Metadata');
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
// Test 3a: Get Deal Fields
$this->testGetDealFields($config);
// Test 3b: Get Person Fields
$this->testGetPersonFields($config);
// Test 3c: Get Activity Fields
$this->testGetActivityFields($config);
} catch (Exception $e) {
$this->recordResult('Field Metadata', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testGetDealFields(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new DealFieldsApi(null, $config);
$result = $apiInstance->getDealFields();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$count = $result->getData() ? count($result->getData()) : 0;
$this->recordResult('Get Deal Fields', true, "Retrieved {$count} deal fields in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Deal Fields', false, $e->getMessage(), $elapsed);
}
}
private function testGetPersonFields(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new PersonFieldsApi(null, $config);
$result = $apiInstance->getPersonFields();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$count = $result->getData() ? count($result->getData()) : 0;
$this->recordResult('Get Person Fields', true, "Retrieved {$count} person fields in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Person Fields', false, $e->getMessage(), $elapsed);
}
}
private function testGetActivityFields(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new ActivityFieldsApi(null, $config);
$result = $apiInstance->getActivityFields();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$count = $result->getData() ? count($result->getData()) : 0;
$this->recordResult('Get Activity Fields', true, "Retrieved {$count} activity fields in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Activity Fields', false, $e->getMessage(), $elapsed);
}
}
private function testV2ApiSupport(SocialAccount $socialAccount): void
{
$this->info('Test 4: v2 API Support (Cursor-based Pagination)');
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
$startTime = microtime(true);
// Test v2 deals endpoint with cursor pagination
// Note: The official SDK may or may not have v2 API support
// This test will determine if v2 is available
try {
$apiInstance = new DealsApi(null, $config);
$updatedSince = Carbon::now()->subDays(30)->toRfc3339String();
$result = $apiInstance->getDealsSummary();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
// Check if response has cursor information (v2 indicator)
// The response object doesn't have getAdditionalData method
// Just test if the call works - v2 API support is limited in official SDK v1
$this->recordResult('v2 API Support', false, "v1 API works but v2 cursor pagination not available in official SDK v1. Retrieved summary in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('v2 API Support', false, $e->getMessage(), $elapsed);
}
} catch (Exception $e) {
$this->recordResult('v2 API Support', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testErrorHandling(SocialAccount $socialAccount): void
{
$this->info('Test 5: Error Handling');
try {
// Test 5a: Invalid Token
$this->testInvalidToken();
// Test 5b: Non-existent Resource
$this->testNonExistentResource($socialAccount);
} catch (Exception $e) {
$this->recordResult('Error Handling', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testInvalidToken(): void
{
$this->info('Test 5a: Invalid Token');
$startTime = microtime(true);
try {
$config = new Configuration();
$config->setAccessToken('invalid_token');
$client = new DealsApi(null, $config);
$client->getDealsSummary();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Invalid Token Error', false, "Expected error but request succeeded in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Invalid Token Error', true, "Correctly threw exception: {$e->getMessage()} in {$elapsed}ms", $elapsed);
}
}
private function testNonExistentResource(SocialAccount $socialAccount): void
{
$this->info('Test 5b: Non-existent Resource');
$startTime = microtime(true);
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
$client = new DealsApi(null, $config);
// Try to get a deal with a very high ID that likely doesn't exist
// Official SDK has getDealsSummary which works without specific ID
// Instead, let's test with an invalid field ID
try {
$fieldClient = new DealFieldsApi(null, $config);
$fieldClient->getDealField(999999999); // Very high ID that likely doesn't exist
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Non-existent Resource Error', false, "Expected error but request succeeded in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Non-existent Resource Error', true, "Correctly threw exception: {$e->getMessage()} in {$elapsed}ms", $elapsed);
}
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Non-existent Resource Error', false, $e->getMessage(), $elapsed);
}
}
private function recordResult(string $testName, bool $success, string $message, float $elapsed): void
{
$this->results[] = [
'test' => $testName,
'success' => $success,
'message' => $message,
'elapsed' => $elapsed,
];
$status = $success ? '✅ PASS' : '❌ FAIL';
$this->line(" {$status} - {$testName}: {$message}");
}
private function displayResults(): void
{
$this->newLine();
$this->info('=== POC Test Results Summary ===');
$this->newLine();
$total = count($this->results);
$passed = count(array_filter($this->results, fn ($r) => $r['success']));
$failed = $total - $passed;
$this->line("Total Tests: {$total}");
$this->line("Passed: {$passed}");
$this->line("Failed: {$failed}");
$this->newLine();
$totalTime = array_sum(array_column($this->results, 'elapsed'));
$this->line("Total Execution Time: {$totalTime}ms");
$this->newLine();
if ($failed > 0) {
$this->warn('Failed Tests:');
foreach ($this->results as $result) {
if (! $result['success']) {
$this->line(" - {$result['test']}: {$result['message']}");
}
}
$this->newLine();
}
$this->info('=== Detailed Results ===');
foreach ($this->results as $result) {
$status = $result['success'] ? '✅' : '❌';
$this->line("{$status} {$result['test']} ({$result['elapsed']}ms): {$result['message']}");
}
$this->newLine();
$this->info('=== Recommendations ===');
if ($passed === $total) {
$this->info('✅ All tests passed! The official SDK is viable for migration.');
$this->info('Next steps: Proceed with full migration planning.');
} else {
$this->warn('⚠️ Some tests failed. Review the failures above before proceeding.');
$this->warn('Consider hybrid approach or investigate alternatives if critical operations fail.');
}
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
49441
|
NULL
|
NULL
|
NULL
|
|
49441
|
1764
|
16
|
2026-05-18T06:44:39.941547+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779086679941_m1.jpg...
|
PhpStorm
|
faVsco.js – TestPipedriveOfficialSdkCommand.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
pipedrive-sdk-poc, menu
S Project: faVsco.js, menu
pipedrive-sdk-poc, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'...
|
[{"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":"pipedrive-sdk-poc, menu","depth":5,"on_screen":true,"help_text":"Git Branch: pipedrive-sdk-poc","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}]...
|
7266624862028544203
|
577591577001045378
|
visual_change
|
hybrid
|
NULL
|
Project: faVsco.js, menu
pipedrive-sdk-poc, menu
S Project: faVsco.js, menu
pipedrive-sdk-poc, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
iTerm2ShellEditViewSessionScriptsProfilesWindowHelp100% <78• Mon 18 May 9:44:39STAGE (ssh)181DOCKER81DEV (-zsh)O ₴2docker_lamp_12026-05-1806:42:22 Jiminny\Jobs\Crm\Hubspot\ProcessWebhookEventsJobdocker_1amp_12026-05-1806:42:22 Jiminny\Jobs\Crm\Hubspot\ProcessWebhookEventsJobdocker_lamp_12026-05-18 06:42:23 Jiminny\Jobs\Crm\Hubspot\ProcessWebhookEventsJobdocker_1amp_1docker_lamp_11 '/usr/local/bin/php' 'artisan' dialers:monitor-activities › '/proc/docker_lamp_12026-05-18 06:42:27 Running ['artisan' jiminny:monitor-social-accountdocker_1amp_1proc/1/fd/1'docker_lamp_11 '/usr/local/bin/php' 'artisan' jiminny:monitor-social-accounts > */2026-05-18 06:42:41 Running ['artisan' mailbox:skip-lists:refresh]docker_lamp_11/fd/1' 2>&11 '/usr/local/bin/php' 'artisan' mailbox:skip-lists:refresh › */proc/2026-05-18 06:42:54 Running ['artisan'mailbox:batch:process --max-badocker_lamp_11 '/usr/local/bin/php' 'artisan'mailbox:batch:process --max-batches=15 > '/proc/1/fd/1' 2>&1docker_1amp_12026-05-18 06:43:08 Running ['artisan'conference:monitor:count]docker_lamp_1fd/1' 2>&1docker_lamp_11 '/usr/local/bin/php' 'artisan'conference:monitor: count > '/proc/1/2026-05-18 06:43:22 Running ['artisan' mailbox:batch: create]docker_lamp_11 '/usr/local/bin/php' 'artisan'mailbox:batch: create › '/proc/1/fd/1docker_lamp_1docker_lamp_1docker_1amp_1run_artisan_schedule: Done waiting for schedule:run2026-05-18 06:43:37 Jiminny\Jobs\Mailbox\CreateBatchesdocker_lamp_11s DONEdocker_1amp_1docker_1amp_17s DONEdocker_lamp_1fd/1' 2>&12026-05-18 06:43:38 Jiminny Jobs Mailbox \CreateBatches2026-05-18 06:44:17 Running ['artisan' meeting-bot:schedule-bot] .. 11 '/usr/local/bin/php' 'artisan' meeting-bot:schedule-bot › */proc/1/APP (-zsh)*3screenpipe"X 12PROD (-zsh)Last login: Sat May 16 18:04:33 on ttys001Poetry could not find a pyproject.toml file in /Users/lukas or its parentsPoetry could not find a pyproject.toml file in /Users/lukas or its parentslukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ |X 13EU (-zsh)Last login: Sat May 16 18:04:33 on ttys001Poetry could not find a pyproject.toml file in /Users/lukas or its parentsPoetry could not find a pyproject.tomlfile in /Users/lukas or its parents@Lukas-Kovaliks-MacBook-Pro-Jiminny~$ IX T4 STAGE (ssh)Poetry could not find a pyproject.toml file in /Users/lukas or its parentsPoetry could not find a pyproject.toml file in /Users/lukas or its parentslukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ stg(lukas@jiminny-stage-bastion) Verification code:QA (-zsh)Last login: Mon May 18 09:17:28 on ttys003Poetry could not find a pyproject.toml file in /Users/lukas or its parentsPoetry could not find a pyproject.tomlfile in /Users/lukas or its parentsX 16FE (-zsh)Last login: Mon May 18 09:17:28 on ttys004O ₴4PRODSTAGEPoetry could not find a pyproject.toml file in /Users/lukas or its parentsFRONTENDPoetry could not find a pyproject.tomlfile in /Users/lukas or its parentslukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ IEXT (-zsh)Poetry could not find a pyproject.toml file in /Users/lukas or its parentsEXTENSIONPoetry could not find a pyproject.tomlfile in /Users/lukas or its parentslukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~View in Docker DesktopView ConfigEnable Watch...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
49440
|
1765
|
14
|
2026-05-18T06:44:37.754158+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779086677754_m2.jpg...
|
PhpStorm
|
faVsco.js – TestPipedriveOfficialSdkCommand.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
PnostormcodeFV faVsco.js?9 pipedrive-sdk-pocrolede PnostormcodeFV faVsco.js?9 pipedrive-sdk-pocroledey© SyncProfileMetadata.php©syncleammetadata.ong© TestPipedriveOfficialSdkCommand.phUpoateopponunityspecrications.ono>@ Dealinsights>@Dev> 0 DialersD DTOSC Elasticsearch› EnqagementStatsш GeckoExport>D LivestreamMallboxes• MidratePlavbackithemesM PlavbooksD Playlists> M Postmark> M PronhetAv M Renorto© AutomatedReportsCommand.phpC) AutomatedRenortsRetentionPolicvCor14 D© AutomatedReportsSendCommand.pht© CreateMockAskJiminnyReportResultC© DeleteReportCommand.php© GenerateMarketingReport.php© Team.php© Usage.php>C Slack›_ Teams>C TracksC Transcription>C TwilioC Users> M Vocabularv7301>ZoomC) Command.oho© CreateDatabaseUsers.ohpC)DatabaseTiableCount.oho© DeleteOIdAiCrmNotesCommand.ohvCDeleteS?Leftoverscommand.ohn@ DevPostmanCommand.phpC) DiarizeViaAiParticinantldentificationCom@ EncrvntTokensCommand.nhn(C EnaaaementStatsReaenerateCommand.n( FeatureFlaasHelner nhr69 6t)© FixCrossTenantlssues.php© FlushRolesPermissionsCache.php© GeneratelnternalWebhookToken.php© GroupSetDefaultLanguageCommand.phc© HelperTruncateCoachingTables.php74 6t81 6t>100% (7 • Mon 18 May 9:44:37AskJiminnyReportActivityServiceTest ve Q.C ActivityController.ongC) ConvertLeadActivities.ong© SyncPlanhat.phpcreatePlaybookcreatedevent.ongc) IntearationApp/Service.php© LeadConverted.php© CreateSelfCoachedEvent.phpC) CreateCommentedEvent.phoC) CreateSmsSentEvent.ohpC) PlanhatActivityListener.pho(C)AskAnvthinaPromotService.ohd(C)AutomatedReportsCommand.ohvphp api y2.ohoC) RequestGenerateReport.Job.oho/(C) AutomatedReportkesulconp© AutomatedReport.phpA console [STAGING]dectarelstrict_types-ty,nanespace Jininny console connanos crmuse…..class syncubnects extends command* The name and sianature of the console commandi* ovar strindnrotected ssionature = crm:sunc-ohnects -team?, *--svnct'** The console command descrintion.* Avan strinaprotected $description = 'Sync remote CRM objects.';* Eyecuto the concole commandlpublic function handle: voidf...;protected function getStaggerDelavSeconds@: floatf...protected function getMaxDelavSeconds@: int{...}protected function getlogPrefix@: stringf...;=custom.loglaravel.log4 SF [jiminny@localhost]« HS_local [jiminny@localhost]A console [PROD]© TestPipedriveOfficialSdkCommand.php x © Kernel.phpA console [EU]class TestPipedrive0fficialSdkCommand extends Commandprivate tunction testbasiceruduperations (soclaLAccount ssoclaLAccount vola134138-rawubloken = ssoclaLaccount->cecrrovlderuserlokenorSthis->info( string: "DB token (Length:"")"Sthis->info( string: "DB token preview: " . substr(SrawDbToken, offset: 0. lengthcheck token expirySexpires = SsocialAccount->expires:sthas->inrol string:"oken exoires at."Sexoires? carbon:: createrromiimesSthis->infod"Token is expired:(Sexnires ss Sexpires < timed) > 'v// Test with the DB token directlySthis->infod strinaTesting with DB token ---").Sconfio = new ConfiaurationOrScontia->se+AcceccToken(SrawlhToken):/ Test 2a: Get DealsReject// Test 2a: Get Deals Summarysunls->restberuealsscontlo// Test 2b: Get PersonsSthis->testGetPersons(Sconfiq)II Test 2c:sthis->testbetacrivitlesscontz0)catch (Excention Se) <sthis->recordResult testName:'Basic CRUD Operations".success: false. Se->aet152198Debugging Pipedrive s• TestPipedriveOfficialSdkCommand.phpcommand dockedocker exec docker_lamp_1 php artisan jiminny:test-pipedrive-official-sdk 19Starting Pipedrive Official SDK POC for Team ID: 19Found Pioedrive account for team: Pinedrive.Inc.Test PASS - DAuth 1aialization: Client initialized successfullv in 148.02mBasie cho operationstoken preview:т tn en rerted: 226405-15 15328194 ...Test 20: Get PersonsErrorcal to undetined method Pioedrive versions viAo1 PersonsAo1::detPersonsotry ppotricant sokofficial SoK uses getPersons methodtmit, a 101).?, trut, -Retrleved person's in (selapsed)ms", selapsed) ;I catch (Exception se) €ThouahtsAlA tocke dond• Update POC to test official SDK methods properly* Test actual CRUD operations with official SDK• TestPipedriveOfficialSdkCoThoushteThe token is expired again (expires at 2026-05-15 15:28:18). Please refresh the token so i can continue testing the official SDK methods.private function testCurrentSystemApproach(SocialAccount SsocialAccount): voidf...?1 usageprivate function testGetDeals(Configuration Sconfig): void{...}private function testGetPersons(Configuration Sconfig): void{...}private function testGetActivities(Configuration Sconfig): voidf...}1 usageprivate function testFieldMetadata(SocialAccount SsocialAccount): void{...}private function testGetDealFields(Confiquration Sconfig): void{...}l* Reiect allAsk anvthina (&4-L)÷ « CodeSWF.16+A-ATenli axeAccent alli•0 1.Po. 4 spac...
|
NULL
|
769658773358293449
|
NULL
|
click
|
ocr
|
NULL
|
PnostormcodeFV faVsco.js?9 pipedrive-sdk-pocrolede PnostormcodeFV faVsco.js?9 pipedrive-sdk-pocroledey© SyncProfileMetadata.php©syncleammetadata.ong© TestPipedriveOfficialSdkCommand.phUpoateopponunityspecrications.ono>@ Dealinsights>@Dev> 0 DialersD DTOSC Elasticsearch› EnqagementStatsш GeckoExport>D LivestreamMallboxes• MidratePlavbackithemesM PlavbooksD Playlists> M Postmark> M PronhetAv M Renorto© AutomatedReportsCommand.phpC) AutomatedRenortsRetentionPolicvCor14 D© AutomatedReportsSendCommand.pht© CreateMockAskJiminnyReportResultC© DeleteReportCommand.php© GenerateMarketingReport.php© Team.php© Usage.php>C Slack›_ Teams>C TracksC Transcription>C TwilioC Users> M Vocabularv7301>ZoomC) Command.oho© CreateDatabaseUsers.ohpC)DatabaseTiableCount.oho© DeleteOIdAiCrmNotesCommand.ohvCDeleteS?Leftoverscommand.ohn@ DevPostmanCommand.phpC) DiarizeViaAiParticinantldentificationCom@ EncrvntTokensCommand.nhn(C EnaaaementStatsReaenerateCommand.n( FeatureFlaasHelner nhr69 6t)© FixCrossTenantlssues.php© FlushRolesPermissionsCache.php© GeneratelnternalWebhookToken.php© GroupSetDefaultLanguageCommand.phc© HelperTruncateCoachingTables.php74 6t81 6t>100% (7 • Mon 18 May 9:44:37AskJiminnyReportActivityServiceTest ve Q.C ActivityController.ongC) ConvertLeadActivities.ong© SyncPlanhat.phpcreatePlaybookcreatedevent.ongc) IntearationApp/Service.php© LeadConverted.php© CreateSelfCoachedEvent.phpC) CreateCommentedEvent.phoC) CreateSmsSentEvent.ohpC) PlanhatActivityListener.pho(C)AskAnvthinaPromotService.ohd(C)AutomatedReportsCommand.ohvphp api y2.ohoC) RequestGenerateReport.Job.oho/(C) AutomatedReportkesulconp© AutomatedReport.phpA console [STAGING]dectarelstrict_types-ty,nanespace Jininny console connanos crmuse…..class syncubnects extends command* The name and sianature of the console commandi* ovar strindnrotected ssionature = crm:sunc-ohnects -team?, *--svnct'** The console command descrintion.* Avan strinaprotected $description = 'Sync remote CRM objects.';* Eyecuto the concole commandlpublic function handle: voidf...;protected function getStaggerDelavSeconds@: floatf...protected function getMaxDelavSeconds@: int{...}protected function getlogPrefix@: stringf...;=custom.loglaravel.log4 SF [jiminny@localhost]« HS_local [jiminny@localhost]A console [PROD]© TestPipedriveOfficialSdkCommand.php x © Kernel.phpA console [EU]class TestPipedrive0fficialSdkCommand extends Commandprivate tunction testbasiceruduperations (soclaLAccount ssoclaLAccount vola134138-rawubloken = ssoclaLaccount->cecrrovlderuserlokenorSthis->info( string: "DB token (Length:"")"Sthis->info( string: "DB token preview: " . substr(SrawDbToken, offset: 0. lengthcheck token expirySexpires = SsocialAccount->expires:sthas->inrol string:"oken exoires at."Sexoires? carbon:: createrromiimesSthis->infod"Token is expired:(Sexnires ss Sexpires < timed) > 'v// Test with the DB token directlySthis->infod strinaTesting with DB token ---").Sconfio = new ConfiaurationOrScontia->se+AcceccToken(SrawlhToken):/ Test 2a: Get DealsReject// Test 2a: Get Deals Summarysunls->restberuealsscontlo// Test 2b: Get PersonsSthis->testGetPersons(Sconfiq)II Test 2c:sthis->testbetacrivitlesscontz0)catch (Excention Se) <sthis->recordResult testName:'Basic CRUD Operations".success: false. Se->aet152198Debugging Pipedrive s• TestPipedriveOfficialSdkCommand.phpcommand dockedocker exec docker_lamp_1 php artisan jiminny:test-pipedrive-official-sdk 19Starting Pipedrive Official SDK POC for Team ID: 19Found Pioedrive account for team: Pinedrive.Inc.Test PASS - DAuth 1aialization: Client initialized successfullv in 148.02mBasie cho operationstoken preview:т tn en rerted: 226405-15 15328194 ...Test 20: Get PersonsErrorcal to undetined method Pioedrive versions viAo1 PersonsAo1::detPersonsotry ppotricant sokofficial SoK uses getPersons methodtmit, a 101).?, trut, -Retrleved person's in (selapsed)ms", selapsed) ;I catch (Exception se) €ThouahtsAlA tocke dond• Update POC to test official SDK methods properly* Test actual CRUD operations with official SDK• TestPipedriveOfficialSdkCoThoushteThe token is expired again (expires at 2026-05-15 15:28:18). Please refresh the token so i can continue testing the official SDK methods.private function testCurrentSystemApproach(SocialAccount SsocialAccount): voidf...?1 usageprivate function testGetDeals(Configuration Sconfig): void{...}private function testGetPersons(Configuration Sconfig): void{...}private function testGetActivities(Configuration Sconfig): voidf...}1 usageprivate function testFieldMetadata(SocialAccount SsocialAccount): void{...}private function testGetDealFields(Confiquration Sconfig): void{...}l* Reiect allAsk anvthina (&4-L)÷ « CodeSWF.16+A-ATenli axeAccent alli•0 1.Po. 4 spac...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
49439
|
1764
|
15
|
2026-05-18T06:44:37.659648+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779086677659_m1.jpg...
|
PhpStorm
|
faVsco.js – TestPipedriveOfficialSdkCommand.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
iTerm2ShellEditViewSessionScriptsProfilesWindowHel iTerm2ShellEditViewSessionScriptsProfilesWindowHelp100% C8 • Mon 18 May 9:44:37STAGE (ssh)T81DOCKERX 11 DOCKER (docker-compose)RUNNINGdocker_lamp_11s DONEdocker_1amp_1RUNNINGdocker_lamp_11s DONEdocker_1amp_1docker_lamp_11/fd/1'2>&1docker_lamp_181DEV (-zsh)O $22026-05-18 06:42:22 Jiminny\Jobs\Crm\Hubspot\ProcessWebhookEventsJob2026-05-1806:42:22 Jiminny\Jobs\Crm\Hubspot\ProcessWebhookEventsJob2026-05-18 06:42:23 Jiminny\Jobs\Crm\Hubspot\ProcessWebhookEventsJob11sDONE1 '/usr/local/bin/php' 'artisan' dialers:monitor-activities › '/proc/2026-05-18 06:42:27 Running ['artisan' jiminny:monitor-social-accountdocker_1amp_11 '/usr/local/bin/php' 'artisan' jiminny:monitor-social-accounts > */proc/1/fd/1'2>&1docker_lamp_12026-05-18 06:42:41 Running ['artisan' mailbox:skip-lists:refresh]13s DONEdocker_lamp_11 '/usr/local/bin/php' 'artisan' mailbox:skip-lists:refresh › */proc/1/fd/1' 2>&1docker_lamp_12026-05-18 06:42:54 Running ['artisan'mailbox:batch:process --max-batches=15]14S DONEdocker_lamp_11 '/usr/local/bin/php' 'artisan"mailbox:batch:process --max-batches=15 > '/proc/1/fd/1' 2>&1docker_1amp_12026-05-18 06:43:08 Running ['artisan'conference:monitor: count].. 13S DONEdocker_lamp_11 '/usr/local/bin/php' 'artisan'conference:monitor: count > '/proc/1/fd/1' 2>&1docker_lamp_12026-05-18 06:43:22 Running ['artisan' mailbox:batch: create]...... 14s DONEdocker_lamp_11 '/usr/local/bin/php' 'artisan'mailbox:batch: create › '/proc/1/fd/12>&1docker_lamp_1docker_lamp_1docker_1amp_1run_artisan_schedule: Done waiting for schedule:run2026-05-18 06:43:37 Jiminny\Jobs\Mailbox\CreateBatchesRUNNINGdocker_lamp_12026-05-18 06:43:38 Jiminny Jobs \Mailbox\CreateBatches1s DONEdocker_1amp_1docker_1amp_12026-05-18 06:44:17 Running ['artisan' meeting-bot:schedule-bot] .. 17s DONEdocker_lamp_11 '/usr/local/bin/php' 'artisan' meeting-bot:schedule-bot › */proc/1/fd/1' 2>&1APP (-zsh)*3screenpipe"Y2PROD (-zsh)Last login: Sat May 16 18:04:33 on ttys001Poetry could not find a pyproject.toml file in /Users/lukas or its parentsPoetry could notfind a pyproject.toml file in /Users/lukas or its parentslukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ ||X t3EU (-zsh)Last login: Sat May 16 18:04:33 on ttys001Poetry could not find a pyproject.toml file in /Users/lukas or its parentsPoetry could not find a pyproject.tomlfile in /Users/lukas or its parents@Lukas-Kovaliks-MacBook-Pro-JiminnyX T4 STAGE (ssh)Poetry could not find a pyproject.toml file in /Users/lukas or its parentsPoetry could not find a pyproject.toml file in /Users/lukas or its parentslukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ stg(lukas@jiminny-stage-bastion) Verification code: ?QA (-zsh)Last login: Mon May 18 09:17:28 on ttys003Poetry could not find a pyproject.toml file in /Users/lukas or its parentsPoetry could not find a pyproject.tomlfile in /Users/lukas or its parentsX 16FE (-zsh)Last login: Mon May 18 09:17:28 on ttys004O $4STAGEPoetry could not find a pyproject.toml file in /Users/lukas or its parentsFRONTENDPoetry could not find a pyproject.tomlfile in /Users/lukas or its parentslukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ IEXT (-zsh)Poetry could not find a pyproject.toml file in /Users/lukas or its parentsEXTENSIONPoetry could not find a pyproject.tomlfile in /Users/lukas or its parentslukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ [|View in Docker DesktopView ConfigEnable Watch...
|
NULL
|
-2280462260247206547
|
NULL
|
click
|
ocr
|
NULL
|
iTerm2ShellEditViewSessionScriptsProfilesWindowHel iTerm2ShellEditViewSessionScriptsProfilesWindowHelp100% C8 • Mon 18 May 9:44:37STAGE (ssh)T81DOCKERX 11 DOCKER (docker-compose)RUNNINGdocker_lamp_11s DONEdocker_1amp_1RUNNINGdocker_lamp_11s DONEdocker_1amp_1docker_lamp_11/fd/1'2>&1docker_lamp_181DEV (-zsh)O $22026-05-18 06:42:22 Jiminny\Jobs\Crm\Hubspot\ProcessWebhookEventsJob2026-05-1806:42:22 Jiminny\Jobs\Crm\Hubspot\ProcessWebhookEventsJob2026-05-18 06:42:23 Jiminny\Jobs\Crm\Hubspot\ProcessWebhookEventsJob11sDONE1 '/usr/local/bin/php' 'artisan' dialers:monitor-activities › '/proc/2026-05-18 06:42:27 Running ['artisan' jiminny:monitor-social-accountdocker_1amp_11 '/usr/local/bin/php' 'artisan' jiminny:monitor-social-accounts > */proc/1/fd/1'2>&1docker_lamp_12026-05-18 06:42:41 Running ['artisan' mailbox:skip-lists:refresh]13s DONEdocker_lamp_11 '/usr/local/bin/php' 'artisan' mailbox:skip-lists:refresh › */proc/1/fd/1' 2>&1docker_lamp_12026-05-18 06:42:54 Running ['artisan'mailbox:batch:process --max-batches=15]14S DONEdocker_lamp_11 '/usr/local/bin/php' 'artisan"mailbox:batch:process --max-batches=15 > '/proc/1/fd/1' 2>&1docker_1amp_12026-05-18 06:43:08 Running ['artisan'conference:monitor: count].. 13S DONEdocker_lamp_11 '/usr/local/bin/php' 'artisan'conference:monitor: count > '/proc/1/fd/1' 2>&1docker_lamp_12026-05-18 06:43:22 Running ['artisan' mailbox:batch: create]...... 14s DONEdocker_lamp_11 '/usr/local/bin/php' 'artisan'mailbox:batch: create › '/proc/1/fd/12>&1docker_lamp_1docker_lamp_1docker_1amp_1run_artisan_schedule: Done waiting for schedule:run2026-05-18 06:43:37 Jiminny\Jobs\Mailbox\CreateBatchesRUNNINGdocker_lamp_12026-05-18 06:43:38 Jiminny Jobs \Mailbox\CreateBatches1s DONEdocker_1amp_1docker_1amp_12026-05-18 06:44:17 Running ['artisan' meeting-bot:schedule-bot] .. 17s DONEdocker_lamp_11 '/usr/local/bin/php' 'artisan' meeting-bot:schedule-bot › */proc/1/fd/1' 2>&1APP (-zsh)*3screenpipe"Y2PROD (-zsh)Last login: Sat May 16 18:04:33 on ttys001Poetry could not find a pyproject.toml file in /Users/lukas or its parentsPoetry could notfind a pyproject.toml file in /Users/lukas or its parentslukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ ||X t3EU (-zsh)Last login: Sat May 16 18:04:33 on ttys001Poetry could not find a pyproject.toml file in /Users/lukas or its parentsPoetry could not find a pyproject.tomlfile in /Users/lukas or its parents@Lukas-Kovaliks-MacBook-Pro-JiminnyX T4 STAGE (ssh)Poetry could not find a pyproject.toml file in /Users/lukas or its parentsPoetry could not find a pyproject.toml file in /Users/lukas or its parentslukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ stg(lukas@jiminny-stage-bastion) Verification code: ?QA (-zsh)Last login: Mon May 18 09:17:28 on ttys003Poetry could not find a pyproject.toml file in /Users/lukas or its parentsPoetry could not find a pyproject.tomlfile in /Users/lukas or its parentsX 16FE (-zsh)Last login: Mon May 18 09:17:28 on ttys004O $4STAGEPoetry could not find a pyproject.toml file in /Users/lukas or its parentsFRONTENDPoetry could not find a pyproject.tomlfile in /Users/lukas or its parentslukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ IEXT (-zsh)Poetry could not find a pyproject.toml file in /Users/lukas or its parentsEXTENSIONPoetry could not find a pyproject.tomlfile in /Users/lukas or its parentslukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ [|View in Docker DesktopView ConfigEnable Watch...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
49433
|
1765
|
11
|
2026-05-18T06:44:24.705613+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779086664705_m2.jpg...
|
PhpStorm
|
faVsco.js – TestPipedriveOfficialSdkCommand.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
PhostormVIewINavicarecodeLaravelKeractorFV faVsco. PhostormVIewINavicarecodeLaravelKeractorFV faVsco.js?° pipedrive-sdk-pocProiectC ActivityController.ong© SyncProfileMetadata.phpCConvertLeadActivities.ong© SyncPlanhat.php©syncleammetadata.ong© TestPipedriveOfficialSdkCommand.phUpoateopponunityspecrications.onocreatePlaybookcreatedevent.ong>@ Dealinsights>@Dev> 0 Dialersc) IntearationApp/Service.php© LeadConverted.php© CreateSelfCoachedEvent.phpD DTOSC ElasticsearchC) CreateCommentedEvent.phoC) CreateSmsSentEvent.ohpC) PlanhatActivityListener.pho(C)AskAnvthinaPromotService.ohd› EnqagementStatsш GeckoExport(C)AutomatedReportsCommand.ohvphp api y2.ohoC) RequestGenerateReport.Job.oho/>D Livestream(C) AutomatedReportkesulconp© AutomatedReport.phpA console [STAGING]Mallboxes• MidratePlavbackithemes• M Plavbooksdectarelstrict_types-ty,Playlists> M Postmarknanespace Jininny console comnanos crm> M PronhetAuse…..v M Renorto© AutomatedReportsCommand.phpC) AutomatedRenortsRetentionPolicvCor14 Dclass syncubnects extends command© AutomatedReportsSendCommand.pht© CreateMockAskJiminnyReportResultC© DeleteReportCommand.php© GenerateMarketingReport.php© Team.php© Usage.php>C Slack* The name and sianature of the console commandi* ovar strind›_ Teams>C TracksC Transcription>C TwilioC Users> M Vocabularv7301nrotected ssionature = crm:sunc-ohnects -team?, ~--svnctr** The console command descrintion.>Zoom*Avan strinaC) Command.oho© CreateDatabaseUsers.ohpprotected $description = 'Sync remote CRM objects.';C)DatabaseTiableCount.oho© DeleteOIdAiCrmNotesCommand.ohv* Eyecuto the concole commandlCDeleteS?Leftoverscommand.ohn@ DevPostmanCommand.phppublic function handle: voidf...;C) DiarizeViaAiParticinantldentificationCom@ EncrvntTokensCommand.nhn(C EnaadementStatsRedenerateCommand.r( FeatureFlaasHelner nhr69 6t)protected function getStaggerDelavSeconds@: floatf...?© FixCrossTenantlssues.php© FlushRolesPermissionsCache.php© GeneratelnternalWebhookToken.php9 GrounSetDefaultLanguageCommand.php74 6tprotected function getMaxDelavSeconds@: int{...}81 6t>protected function getlogPrefix@: stringf...;© HelperTruncateCoachingTables.phpHelner Code will hoin INF to underctand vour Laravel ann code II Generate II Don't Show Anvmore (Q minutes aaol=custom.loglaravel.logA SF [jiminny@localhost]« HS_local [jiminny@localhost]A console [PROD]© TestPipedriveOfficialSdkCommand.php x © Kernel.phpA console [EU]class TestPipedrive0fficialSdkCommand extends Commandprivate tunction testbasiceruduperations (soclaLAccount ssoclaLAccount vola132134138-rawubloken = ssoclaLaccount->cecrrovlderuserlokenorSthis->info( string: "DB token (Length:"")"Sthis->info( string: "DB token preview: ". substr(SrawDbToken, offset: 0,check token expirySexpires = SsocialAccount->expires:sthas->inrol string:"oken exoires at."Sexoires? carbon::createrromiimesSthis->infod"Token is expired:(Sexnires ss Sexpires ‹ timeo) ? 'v// Test with the DB token directlySthis->infod strinaTesting with DB token ---").Sconfio = new Confiquration0:Scontia->se+AcceccToken(SrawlhToken):/ Test 2a: Get DealsReject// Test 2a: Get Deals Summarysunls->restberuealsscontlo// Test 2b: Get PersonsSthis->testGetPersons(Sconfiq)II Test 2c:sthis->testbetacrivitlesscontz0)catch (Excention Se) <sthis->recordResult testName:'Basic CRUD Operations".success: false. Se->aet152181198private function testCurrentSystemApproach(SocialAccount SsocialAccount): voidf...?Tusageprivate function testGetDeals(Configuration Sconfig): void{...}private function testGetPersons(Configuration Sconfig): void{...}private function testGetActivities(Configuration Sconfiq): void{...}1 usageprivate function testFieldMetadata(SocialAccount SsocialAccount): void{...}privata funation tgst' Accept File &- Cogfiauaatiion, Sconfig) or voigi → . }AskJiminnyReportActivityServiceTest vCascade• Debugging Pipedriveneolnis•TestPipedriveOfficialSdkCommand.php• TestPipedriveOfficialSdkCommand.phpdocker exec docker lamo 1 oho artisan liminnv.test-oinedrive-official-sdk 19Starting Pipedrive Official SDK POC for Team ID: 19Found Pipedrive account for team: Pipedrive, Inc.Test PAS - dauth intzatization: Cient initialized successfully in 148.02msrestoken asec th: operationsToken ex expired: 2E56-05-15 15:2N:2yu...Tstine witbealssmaresulted in a '401 Unauthorized"WC FAIL - Get Deals Summary: 1401l Client error: GET https://ap2.pipedrive.com/vz/deals/summapy resulted in a 401 Unauthorized reTest 2b: Get PersonsErrorCall to undefined method Pipedrive\versions\v1\Api\PersonsApi::getPersons()selapsed =' ret Persons),true, "Retrieved persons in &selapsedins", selanced).Alh tocke dano• Update POC to test official SDK methods properly6 Tect actual CPUD onerationc with official Snk.TectDinedriveOfficialSdkCommand.nhr100% Lz• • Mon 18 May 9:44:24+0 ..+16 -6Ask anvthina (&4-L)÷ « CodeSWF.16J 637 tokens* Reiect allAccent allWN Windsurf Teams127-1UTE.8Po 4 spaces...
|
NULL
|
-2260476629171350108
|
NULL
|
click
|
ocr
|
NULL
|
PhostormVIewINavicarecodeLaravelKeractorFV faVsco. PhostormVIewINavicarecodeLaravelKeractorFV faVsco.js?° pipedrive-sdk-pocProiectC ActivityController.ong© SyncProfileMetadata.phpCConvertLeadActivities.ong© SyncPlanhat.php©syncleammetadata.ong© TestPipedriveOfficialSdkCommand.phUpoateopponunityspecrications.onocreatePlaybookcreatedevent.ong>@ Dealinsights>@Dev> 0 Dialersc) IntearationApp/Service.php© LeadConverted.php© CreateSelfCoachedEvent.phpD DTOSC ElasticsearchC) CreateCommentedEvent.phoC) CreateSmsSentEvent.ohpC) PlanhatActivityListener.pho(C)AskAnvthinaPromotService.ohd› EnqagementStatsш GeckoExport(C)AutomatedReportsCommand.ohvphp api y2.ohoC) RequestGenerateReport.Job.oho/>D Livestream(C) AutomatedReportkesulconp© AutomatedReport.phpA console [STAGING]Mallboxes• MidratePlavbackithemes• M Plavbooksdectarelstrict_types-ty,Playlists> M Postmarknanespace Jininny console comnanos crm> M PronhetAuse…..v M Renorto© AutomatedReportsCommand.phpC) AutomatedRenortsRetentionPolicvCor14 Dclass syncubnects extends command© AutomatedReportsSendCommand.pht© CreateMockAskJiminnyReportResultC© DeleteReportCommand.php© GenerateMarketingReport.php© Team.php© Usage.php>C Slack* The name and sianature of the console commandi* ovar strind›_ Teams>C TracksC Transcription>C TwilioC Users> M Vocabularv7301nrotected ssionature = crm:sunc-ohnects -team?, ~--svnctr** The console command descrintion.>Zoom*Avan strinaC) Command.oho© CreateDatabaseUsers.ohpprotected $description = 'Sync remote CRM objects.';C)DatabaseTiableCount.oho© DeleteOIdAiCrmNotesCommand.ohv* Eyecuto the concole commandlCDeleteS?Leftoverscommand.ohn@ DevPostmanCommand.phppublic function handle: voidf...;C) DiarizeViaAiParticinantldentificationCom@ EncrvntTokensCommand.nhn(C EnaadementStatsRedenerateCommand.r( FeatureFlaasHelner nhr69 6t)protected function getStaggerDelavSeconds@: floatf...?© FixCrossTenantlssues.php© FlushRolesPermissionsCache.php© GeneratelnternalWebhookToken.php9 GrounSetDefaultLanguageCommand.php74 6tprotected function getMaxDelavSeconds@: int{...}81 6t>protected function getlogPrefix@: stringf...;© HelperTruncateCoachingTables.phpHelner Code will hoin INF to underctand vour Laravel ann code II Generate II Don't Show Anvmore (Q minutes aaol=custom.loglaravel.logA SF [jiminny@localhost]« HS_local [jiminny@localhost]A console [PROD]© TestPipedriveOfficialSdkCommand.php x © Kernel.phpA console [EU]class TestPipedrive0fficialSdkCommand extends Commandprivate tunction testbasiceruduperations (soclaLAccount ssoclaLAccount vola132134138-rawubloken = ssoclaLaccount->cecrrovlderuserlokenorSthis->info( string: "DB token (Length:"")"Sthis->info( string: "DB token preview: ". substr(SrawDbToken, offset: 0,check token expirySexpires = SsocialAccount->expires:sthas->inrol string:"oken exoires at."Sexoires? carbon::createrromiimesSthis->infod"Token is expired:(Sexnires ss Sexpires ‹ timeo) ? 'v// Test with the DB token directlySthis->infod strinaTesting with DB token ---").Sconfio = new Confiquration0:Scontia->se+AcceccToken(SrawlhToken):/ Test 2a: Get DealsReject// Test 2a: Get Deals Summarysunls->restberuealsscontlo// Test 2b: Get PersonsSthis->testGetPersons(Sconfiq)II Test 2c:sthis->testbetacrivitlesscontz0)catch (Excention Se) <sthis->recordResult testName:'Basic CRUD Operations".success: false. Se->aet152181198private function testCurrentSystemApproach(SocialAccount SsocialAccount): voidf...?Tusageprivate function testGetDeals(Configuration Sconfig): void{...}private function testGetPersons(Configuration Sconfig): void{...}private function testGetActivities(Configuration Sconfiq): void{...}1 usageprivate function testFieldMetadata(SocialAccount SsocialAccount): void{...}privata funation tgst' Accept File &- Cogfiauaatiion, Sconfig) or voigi → . }AskJiminnyReportActivityServiceTest vCascade• Debugging Pipedriveneolnis•TestPipedriveOfficialSdkCommand.php• TestPipedriveOfficialSdkCommand.phpdocker exec docker lamo 1 oho artisan liminnv.test-oinedrive-official-sdk 19Starting Pipedrive Official SDK POC for Team ID: 19Found Pipedrive account for team: Pipedrive, Inc.Test PAS - dauth intzatization: Cient initialized successfully in 148.02msrestoken asec th: operationsToken ex expired: 2E56-05-15 15:2N:2yu...Tstine witbealssmaresulted in a '401 Unauthorized"WC FAIL - Get Deals Summary: 1401l Client error: GET https://ap2.pipedrive.com/vz/deals/summapy resulted in a 401 Unauthorized reTest 2b: Get PersonsErrorCall to undefined method Pipedrive\versions\v1\Api\PersonsApi::getPersons()selapsed =' ret Persons),true, "Retrieved persons in &selapsedins", selanced).Alh tocke dano• Update POC to test official SDK methods properly6 Tect actual CPUD onerationc with official Snk.TectDinedriveOfficialSdkCommand.nhr100% Lz• • Mon 18 May 9:44:24+0 ..+16 -6Ask anvthina (&4-L)÷ « CodeSWF.16J 637 tokens* Reiect allAccent allWN Windsurf Teams127-1UTE.8Po 4 spaces...
|
49428
|
NULL
|
NULL
|
NULL
|
|
49431
|
1764
|
10
|
2026-05-18T06:44:24.704247+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779086664704_m1.jpg...
|
PhpStorm
|
faVsco.js – TestPipedriveOfficialSdkCommand.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
iTerm2ShelllEditViewSessionScriptsProfilesWindowHe iTerm2ShelllEditViewSessionScriptsProfilesWindowHelp•• 0APP (-zsh)DOCKERLast login: Mon May 18 09:17:28 on ttys007O 81DEV (-zsh)O ₴2Poetry could not find a pyproject.toml file in /Users/lukas/jiminny/app or its parentsPoetry could not find a pyproject.toml file in /Users/lukas/jiminny/app or its parentslukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/jiminny/app (pipedrive-sdk-poc) $ U‹ $0APP (-zsh)|83100% C47 8• Mon 18 May 9:44:24T₴1screenpipe*O &4APP...
|
NULL
|
-2775080756249258382
|
NULL
|
click
|
ocr
|
NULL
|
iTerm2ShelllEditViewSessionScriptsProfilesWindowHe iTerm2ShelllEditViewSessionScriptsProfilesWindowHelp•• 0APP (-zsh)DOCKERLast login: Mon May 18 09:17:28 on ttys007O 81DEV (-zsh)O ₴2Poetry could not find a pyproject.toml file in /Users/lukas/jiminny/app or its parentsPoetry could not find a pyproject.toml file in /Users/lukas/jiminny/app or its parentslukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/jiminny/app (pipedrive-sdk-poc) $ U‹ $0APP (-zsh)|83100% C47 8• Mon 18 May 9:44:24T₴1screenpipe*O &4APP...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
49430
|
1764
|
9
|
2026-05-18T06:44:23.530700+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779086663530_m1.jpg...
|
PhpStorm
|
faVsco.js – TestPipedriveOfficialSdkCommand.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
pipedrive-sdk-poc, menu
S Project: faVsco.js, menu
pipedrive-sdk-poc, menu
Start Listening for PHP Debug Connections...
|
[{"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":"pipedrive-sdk-poc, menu","depth":5,"on_screen":true,"help_text":"Git Branch: pipedrive-sdk-poc","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,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
4013489942575271730
|
-7483500116281160310
|
visual_change
|
hybrid
|
NULL
|
Project: faVsco.js, menu
pipedrive-sdk-poc, menu
S Project: faVsco.js, menu
pipedrive-sdk-poc, menu
Start Listening for PHP Debug Connections
iTerm2ShelllEditViewSessionScriptsProfilesWindowHelp•• 0APP (-zsh)DOCKERLast login: Mon May 18 09:17:28 on ttys007O 81DEV (-zsh)O ₴2Poetry could not find a pyproject.toml file in /Users/lukas/jiminny/app or its parentsPoetry could not find a pyproject.toml file in /Users/lukas/jiminny/app or its parentslukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/jiminny/app (pipedrive-sdk-poc) $ U‹ $0APP (-zsh)|83100% C47 8• Mon 18 May 9:44:23T₴1screenpipe*O &4APP...
|
49429
|
NULL
|
NULL
|
NULL
|
|
49429
|
1764
|
8
|
2026-05-18T06:44:20.503375+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779086660503_m1.jpg...
|
PhpStorm
|
faVsco.js – TestPipedriveOfficialSdkCommand.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
|
[{"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}]...
|
8043719072324535154
|
-8628527368849355612
|
visual_change
|
hybrid
|
NULL
|
Project: faVsco.js, menu
iTerm2ShellEditViewSessio Project: faVsco.js, menu
iTerm2ShellEditViewSessionScriptsProfilesWindowHelp100% C47 8• Mon 18 May 9:44:20•• 0APP (-zsh)T81DOCKERLast login: Mon May 18 09:17:28 on ttys007₴81DEV (-zsh)O ₴2Poetry could not find a pyproject.toml file in /Users/lukas/jiminny/app or its parentsPoetry could not find a pyproject.toml file in /Users/lukas/jiminny/app or its parentsLukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/jiminny/app (pipedrive-sdk-poc) $ 0APP (-zsh)83pmsetO ₴4APPNPS-Firefox...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
49428
|
1765
|
10
|
2026-05-18T06:44:13.080496+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779086653080_m2.jpg...
|
PhpStorm
|
faVsco.js – TestPipedriveOfficialSdkCommand.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
pipedrive-sdk-poc, menu
|
[{"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":"pipedrive-sdk-poc, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.05618351,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: pipedrive-sdk-poc","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
3036442092684777152
|
-5740307667640416884
|
visual_change
|
hybrid
|
NULL
|
Project: faVsco.js, menu
pipedrive-sdk-poc, menu
P Project: faVsco.js, menu
pipedrive-sdk-poc, menu
PhostormVIewINavicarecodeLaravelKeractorFV faVsco.js?° pipedrive-sdk-pocProiect vC ActivityController.ong© SyncProfileMetadata.phpCConvertLeadActivities.ong© SyncPlanhat.php©syncleammeradata.ong© TestPipedriveOfficialSdkCommand.phUpoateopponunityspecrications.onocreatePlaybookcreatedevent.ong>@ Dealinsights>@Dev> 0 Dialersc) IntearationApp/Service.php© LeadConverted.php© CreateSelfCoachedEvent.phpD DTOSC ElasticsearchC) CreateCommentedEvent.phoC) CreateSmsSentEvent.ohp© PlanhatActivityListener.php(C)AskAnvthinaPromotService.ohd› EnqagementStatsш GeckoExport(C)AutomatedReportsCommand.ohvphp api y2.ohoC) RequestGenerateReport.Job.oho/>D Livestream(C) AutomatedReportkesulconp© AutomatedReport.phpA console [STAGING]Mallboxes• MidratePlavbackithemes• M Plavbooksdectarelstrict_types-tyPlaylists> M Postmarknanespace Jininny console comnanos crm> M PronhetAuse…..v M Renorto© AutomatedReportsCommand.phpC) AutomatedRenortsRetentionPolicvCor14 Dclass syncubnects extends command© AutomatedReportsSendCommand.pht© CreateMockAskJiminnyReportResultC© DeleteReportCommand.php© GenerateMarketingReport.php© Team.php© Usage.php>C Slack* The name and sianature of the console commandi* ovar strind›_ Teams>C TracksC Transcription>C TwilioC Users> M Vocabularv7301nrotected ssionature = crm:sunc-ohnects -team?, ~--svnctr** The console command descrintion.>Zoom*Avan strinaC) Command.oho© CreateDatabaseUsers.ohpprotected $description = 'Sync remote CRM objects.';C)DatabaseTiableCount.oho© DeleteOIdAiCrmNotesCommand.ohv* Eyecuto the concole commandlCDeleteS?Leftoverscommand.ohn@ DevPostmanCommand.phppublic function handle: voidf...;C) DiarizeViaAiParticinantldentificationCom@ EncrvntTokensCommand.nhn(C EnaaaementStatsReaenerateCommand.n( FeatureFlaasHelner nhr69 6t)protected function getStaggerDelavSeconds@: floatf...?© FixCrossTenantlssues.php© FlushRolesPermissionsCache.php© GeneratelnternalWebhookToken.php9 GrounSetDefaultLanguageCommand.php© HelperTruncateCoachingTables.php74 6tprotected function getMaxDelavSeconds@: int{...}81 6t>protected function getlogPrefix@: stringf...;Helner Code will hoin INF to underctand vour Laravel ann code II Generate II Don't Show Anvmore (Q minutes aaolAskJiminnyReportActivityServiceTest v=custom.log=laravel.log4 SF [jiminny@localhost]« HS_local [jiminny@localhost]A console [PROD]© TestPipedriveOfficialSdkCommand.php x © Kernel.phpA console [EU]class TestPipedrive0fficialSdkCommand extends Commandprivate tunction testbasiceruduperations (soclaLAccount ssoclaLAccount vola132-rawubloken = ssoclaLaccount->cecrrovlderuserlokenorSthis->info( string: "DB token (Length:")");Sthis->info( string: "DB token preview: " . substr(SrawDbToken, offset: 0. lengthcheck token expirySexpires = SsocialAccount->expires:sthas->inrol string:"oken exoires at."Sexoires? carbon::createrromiimesSthis->infod"Token is expired:Sexoires . Sexpires < timeo ?// Test with the DB token directlySthis->infod strinaTesting wilth D8 token ---").Sconfio = new ConfiaurationOrScontia->se+AcceccToken(SrawlhToken):/ Test 2a: Get DealsReject// Test 2a: Get Deals SummarySthis->testGetDeals(Sconfig):// Test 2b: Get PersonsSthis->testGetPersons(Sconfiq)II Test 2c:sthis->testbetacrivitlescont10)} catch (Exception Se) {sthis->recordResult testName:'Basic CRUD Operations".success: false. Se->aetCascadeDebugging PipedriveRead TestPipedriveOfficialSdkCommand.php #L330-379Read TestPipedriveOtricialsakcommand.pho #L3/9-408• TestPipedriveOfficialSdkCommand.php• TestPipedriveOfficialSdkCommand.phpdocker exec docker lamn 1nhn artisan iiminnv.tect-ninedrive-official-cok 10Starting Pipedrive Official SDK POC for Team ID: 19Found Pioedrive account for team: Pivedrive. Inc.Test 1: OAuth InitializatiorVPASS = oxuth intlau zation: cuent inttlauzed successtully in 148.02m.Test 2: Basic CRUD OperationsToken expires a: 2026-05-45 15:2828 ...Token is expired: YES{"success": false, "error" :"Invalid token:Test 2b: Get PersonsErrorCall to undefined method Pipedrivelversions\v1\Api\PersonsApi:: getPersons()(e in.a so ua e reaaedoaa ut, fconta) » 191):, carca fese on sut ey cet ertone) Mer forr ed pr ohd n (selopsolas', solupse):100% Lz. Mon 18 May 9:44:12+0 ..+5-5+16 -6wresulted in a '401 Unauthorized response152181198257private function testCurrentSystemApproach(SocialAccount SsocialAccount): voidf.Tusageprivate function testGetDeals(Configuration Sconfig): void{...}private function testGetPersons(Configuration Sconfig): void{...}private function testGetActivities(Configuration Sconfig): void{...}1 usageprivate function testFieldMetadata(SocialAccount SsocialAccount): void{...}privata funation tgst' Accept File &- Cogfiauaatiion, Sconfig) or voigi → . }Ask anvthina (&4-L)÷ « CodeSWF.16* Reiect allWN Windsurf Teamo127-1 1Accent alliio 4 spaces...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
49427
|
1765
|
9
|
2026-05-18T06:44:10.144365+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779086650144_m2.jpg...
|
PhpStorm
|
faVsco.js – TestPipedriveOfficialSdkCommand.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
pipedrive-sdk-poc, menu
S Project: faVsco.js, menu
pipedrive-sdk-poc, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes...
|
[{"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":"pipedrive-sdk-poc, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.05618351,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: pipedrive-sdk-poc","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,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
7558552253815835299
|
-8132289082607555642
|
click
|
hybrid
|
NULL
|
Project: faVsco.js, menu
pipedrive-sdk-poc, menu
S Project: faVsco.js, menu
pipedrive-sdk-poc, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
PhostormVIewINavicarecodeLaravelKeractorFV faVsco.js?° pipedrive-sdk-pocProiectC ActivityController.ong© SyncProfileMetadata.phpCConvertLeadActivities.ong© SyncPlanhat.php©syncleammetadata.ong© TestPipedriveOfficialSdkCommand.phUpoateopponunityspecrications.onocreatePlaybookcreatedevent.ong>@ Dealinsights>@Dev> 0 Dialersc) IntearationApp/Service.php© LeadConverted.php© CreateSelfCoachedEvent.phpD DTOSC ElasticsearchC) CreateCommentedEvent.phoC) CreateSmsSentEvent.ohpC) PlanhatActivityListener.pho(C)AskAnvthinaPromotService.ohd› EnqagementStatsш GeckoExport(C)AutomatedReportsCommand.ohvphp api y2.ohoC) RequestGenerateReport.Job.oho/>D Livestream(C) AutomatedReportkesulconp© AutomatedReport.phpA console [STAGING]Mallboxes• MidratePlavbackithemes• M Plavbooksdectarelstrict_types-ty,Playlists> M Postmarknanespace Jininny console comnanos crm> M PronhetAuse…..v M Renorto© AutomatedReportsCommand.phpC) AutomatedRenortsRetentionPolicvCor14 Dclass syncubnects extends command© AutomatedReportsSendCommand.pht© CreateMockAskJiminnyReportResultC© DeleteReportCommand.php© GenerateMarketingReport.php© Team.php© Usage.php>C Slack* The name and sianature of the console commandi* ovar strind›_ Teams>C TracksC Transcription>C TwilioC Users> M Vocabularv7301nrotected ssionature = crm:sunc-ohnects -team?, ~--svnctr** The console command descrintion.>Zoom*Avan strinaC) Command.oho© CreateDatabaseUsers.phpprotected $description = 'Sync remote CRM objects.';C)DatabaseTiableCount.oho© DeleteOIdAiCrmNotesCommand.ohv* Eyecuto the concole commandlCDeleteS?Leftoverscommand.ohn@ DevPostmanCommand.phppublic function handle: voidf...;C) DiarizeViaAiParticinantldentificationCom@ EncrvntTokensCommand.nhn(C EnaaaementStatsReaenerateCommand.n( FeatureFlaasHelner nhr69 6t)protected function getStaggerDelavSeconds@: floatf...© FixCrossTenantlssues.php© FlushRolesPermissionsCache.php© GeneratelnternalWebhookToken.php9 GrounSetDefaultLanguageCommand.php74 6tprotected function getMaxDelavSeconds@: int{...}81 6t>protected function getlogPrefix@: stringf...;© HelperTruncateCoachingTables.phpHelner Code will hoin INF to underctand vour Laravel ann code II Generate II Don't Show Anvmore (Q minutes aaol=custom.loglaravel.log4 SF [jiminny@localhost]« HS_local [jiminny@localhost]A console [PROD]© TestPipedriveOfficialSdkCommand.php x © Kernel.phpA console [EU]class TestPipedrive0fficialSdkCommand extends Commandprivate tunction testbasiceruduperations (soclaLAccount ssoclaLAccount vola134138-rawubloken = ssoclaLaccount->cecrrovlderuserlokenorSthis->info( string: "DB token (Length:Sthis->info( string: "DB token preview: ". substr(SrawDbToken, offset: 0,lengthcheck token expirySexpires = SsocialAccount->expires:sthas->inrol string:"oken exoires at.Sexoires? carbon::createrromimesSthis->infod"Token is expired:(Sexnires ss Sexpires < timed) > 'v// Test with the DB token directlySthis->infod strinaTesting with DB token ---").Sconfio = new Confiquration0:Scontia->se+AcceccToken(SrawlhToken):/ Test 2a: Get DealsReject// Test 2a: Get Deals Summarysunls->restberuealsscontlo// Test 2b: Get PersonsSthis->testGetPersons(Sconfiq)II Test 2c:sthis->testbetacrivitlescont10)} catch (Exception Se) {sthis->recordResult testName:'Basic CRUD Operations".success: false. Se->aet100% Sz• Mon 18 May 9:44:09AskJiminnyReportActivityServiceTest v+0 ..continue and test all in the command.w152181198216Debugging PipedrivThought for 1s0 4 tasks done1 Update Poc to test ofticial SDK methods properlyTest vz APl support it avallable3 Test error handling properlv• TestPipedriveOfficialSdkCommand.phpThouahtscrror while ediuno lescripedriveurticlalsokcommano.onpThoughts ›Read TestPipedriveOfficialSdkCommand.pho #L175-224lhoughts• TestPipedriveOfficialSdkCommand.phpThoughts >•TestPipedriveOfficialSdkCommand.ohpDoad ToctDinodrivoOfficia|CdkCommand nhn #ll 220.270Thouahts>Read [EMAIL] #L3/9-408lallteinie•TestPipedriveOfficialSdkCommand.phpThoughtsTestPipedriveOfficialSdkCommand.phpThoughtscommand docker• docker exec dockerlamn 1 nhn artisanminnv.test-ninedrive-official-cdk 10Starting Pipedrive Official SDK POC for Team ID: 19Found Pipedrive account for team: Pipedrive. Inc.Test 1: OAuth Initialization on Client initialized successfullv in 148,02msTest 2: Basic CRUD Operations+6 -6+20 -14+5-5+16 -6private function testCurrentSystemApproach(SocialAccount SsocialAccount): voidf..?Tusageprivate function testGetDeals(Configuration Sconfig): void{...}private function testGetPersons(Configuration $config): void{...}private function testGetActivities(Configuration Sconfig): void{...}1 usageprivate function testFieldMetadata(SocialAccount SsocialAccount): void{...}privata funation tgst' Accept File &- Cogfiauaatiion, Sconfig) or voigi → . }Ask anvthina (&4b÷ « CodeSWF.16* Reiect allAccent alli•.0WN Windsurf Teamo127-1UTE.8io 4 spaces...
|
49425
|
NULL
|
NULL
|
NULL
|
|
49426
|
1764
|
7
|
2026-05-18T06:44:10.133614+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779086650133_m1.jpg...
|
PhpStorm
|
faVsco.js – TestPipedriveOfficialSdkCommand.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
pipedrive-sdk-poc, menu
S Project: faVsco.js, menu
pipedrive-sdk-poc, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes...
|
[{"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":"pipedrive-sdk-poc, menu","depth":5,"on_screen":true,"help_text":"Git Branch: pipedrive-sdk-poc","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}]...
|
7558552253815835299
|
-8132289082607555642
|
click
|
hybrid
|
NULL
|
Project: faVsco.js, menu
pipedrive-sdk-poc, menu
S Project: faVsco.js, menu
pipedrive-sdk-poc, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
iTerm2ShelllEditViewSessionScriptsProfilesWindowHelp•• 0APP (-zsh)DOCKERLast login: Mon May 18 09:17:28 on ttys007O 81DEV (-zsh)O 82Poetry could not find a pyproject.toml file in /Users/lukas/jiminny/app or its parentsPoetry could not find a pyproject.toml file in /Users/lukas/jiminny/app or its parentslukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/jiminny/app (pipedrive-sdk-poc) $ U‹ $0APP (-zsh)|83100% C47 8• Mon 18 May 9:44:09T₴1screenpipe*O &4APP...
|
49423
|
NULL
|
NULL
|
NULL
|
|
49425
|
1765
|
8
|
2026-05-18T06:43:52.586747+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779086632586_m2.jpg...
|
PhpStorm
|
faVsco.js – TestPipedriveOfficialSdkCommand.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
pipedrive-sdk-poc, menu
S Project: faVsco.js, menu
pipedrive-sdk-poc, 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...
|
[{"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":"pipedrive-sdk-poc, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.05618351,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: pipedrive-sdk-poc","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}]...
|
-8603436982613086966
|
-8708828999581586494
|
visual_change
|
hybrid
|
NULL
|
Project: faVsco.js, menu
pipedrive-sdk-poc, menu
S Project: faVsco.js, menu
pipedrive-sdk-poc, 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
PhostormVIewINavicarecodeLaravelKeractorFV faVsco.js?° pipedrive-sdk-pocProiectC ActivityController.ong© SyncProfileMetadata.phpCConvertLeadActivities.ong© SyncPlanhat.php©syncleammetadata.ong© TestPipedriveOfficialSdkCommand.phUpoateopponunityspecrications.onocreatePlaybookcreatedevent.ong>@ Dealinsights>@Dev> 0 Dialersc) IntearationApp/Service.php© LeadConverted.php© CreateSelfCoachedEvent.phpD DTOSC ElasticsearchC) CreateCommentedEvent.phoC) CreateSmsSentEvent.ohpC) PlanhatActivityListener.pho(C)AskAnvthinaPromotService.ohd› EnqagementStatsш GeckoExport(C)AutomatedReportsCommand.ohvphp api y2.ohoC) RequestGenerateReport.Job.oho/>D Livestream(C) AutomatedReportkesulconp© AutomatedReport.phpA console [STAGING]Mallboxes• MidratePlavbackithemes• M Plavbooksdectarelstrict_types-tyD Playlists> M Postmarknanespace Jininny console comnanos crm> M PronhetAuse…..v M Renorto© AutomatedReportsCommand.phpC) AutomatedRenortsRetentionPolicvCor14 Dclass syncubnects extends command© AutomatedReportsSendCommand.pht© CreateMockAskJiminnyReportResultC© DeleteReportCommand.php© GenerateMarketingReport.php© Team.php© Usage.php>C Slack* The name and sianature of the console commandi* ovar strind›_ Teams>C TracksC Transcription>C TwilioC Users> M Vocabularv7301nrotected ssionature = crm:sunc-ohnects -team?, ~--svnctr** The console command descrintion.>Zoom*Avan strinaC) Command.oho© CreateDatabaseUsers.phpprotected $description = 'Sync remote CRM objects.';C)DatabaseTiableCount.oho© DeleteOIdAiCrmNotesCommand.ohv* Eyecuto the concole commandlCDeleteS?Leftoverscommand.ohn@ DevPostmanCommand.phppublic function handle: voidf...;C) DiarizeViaAiParticinantldentificationCom@ EncrvntTokensCommand.nhn(C EnaadementStatsRedenerateCommand.r( FeatureFlaasHelner nhr69 6t)protected function getStaggerDelavSeconds@: floatf...?© FixCrossTenantlssues.php© FlushRolesPermissionsCache.php© GeneratelnternalWebhookToken.php9 GrounSetDefaultLanguageCommand.php74 6tprotected function getMaxDelavSeconds@: int{...}81 6t>protected function getLoqPrefix@: stringf...;l© HelperTruncateCoachingTables.phpHelner Code will hoin INF to underctand vour Laravel ann code II Generate II Don't Show Anvmore (8 minutes aaol=custom.log=laravel.log4 SF [jiminny@localhost]« HS_local [jiminny@localhost]A console [PROD]© TestPipedriveOfficialSdkCommand.php x © Kernel.phpA console [EU]class TestPipedrive0fficialSdkCommand extends Command117private tunction testbasiceruduperations (soclaLaccount esoclaLaccount): voia129V 132.134138140141142152181198-rawubloken = ssoclaLaccount->cecrrovlderuserlokenorSthis->info( string: "DB token (Length:Sthis->info( string: "DB token preview: ". substr(SrawDbToken, offset: 0,lengthcheck token expirySexpires = SsocialAccount->expires:sthas->inrol string:"oken exoires at:Sexoires? carbon::createrromiimesSthis->infod"Token is expired:• (Sexpines &s Sexpires ‹ timed) ? '// Test with the DB token directlySthis->infod strinaTesting wilth D8 token ---").Sconfio = new Confiquration0:Scontia->se+AcceccToken(SrawlhToken):/ Test 2a: Get DealsReject// Test 2a: Get Deals Summarysunls->restberuealsscontlo// Test 2b: Get PersonsSthis->testGetPersons(Sconfiq)II Test 2c:sthis->testbetacrivitlesscontz0)} catch (Exception Se) {sthis->recordResult testName:'Basic CRUD Operations".success: false. Se->aet100% Sz• Mon 18 May 9:43:52AskJiminnyReportActivityServiceTest v+0 ..continue and test all in the command.w• Debugging PipedriveThought for 1s0 4 tasks done1 Update Poc to test ofticial SDK methods properlyTest vz APl support it avallable3 Test error handlina properiv• TestPipedriveOfficialSdkCommand.phpThouahtserror while ediung lestripedriveurticlalsakcommano.onp ›Thoughts ›Read TestPipedriveOfficialSdkCommand.pho #L175-224lhoughts• TestPipedriveOfficialSdkCommand.phpThoughts >•TestPipedriveOfficialSdkCommand.phpRead TestPipedriveOfficialSdkCommand.php #L330-379Thouahts>Read [EMAIL] #L3/9-408lallteinie•TestPipedriveOfficialSdkCommand.phpThoughtsTestPipedriveOfficialSdkCommand.phpThoughtscommand docker• docker exec dockerlamn 1nhn artisanminnv.test-ninedrive-official-cdk 10+6 -6+20 -14+5 -5+16 -6private function testCurrentSystemApproach(SocialAccount SsocialAccount): void{...}private function testGetDeals(Configuration Sconfig): void{...}private function testGetPersons(Configuration $config): void{...}private function testGetActivities(Configuration Sconfig): void{...}1 usageprivate function testFieldMetadata(SocialAccount SsocialAccount): void{...}privata funation tgst' Accept File &- Cogfiauaatiion, Sconfig) or voigi → . }Ask anvthina (&4b÷ « CodeSWF.16* Reiect allAccent alliWN Windsurf Toams 127-1UTF.8Po 4 spaces...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
49424
|
1765
|
7
|
2026-05-18T06:43:49.587048+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779086629587_m2.jpg...
|
PhpStorm
|
faVsco.js – TestPipedriveOfficialSdkCommand.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
PhostormVIewINavicarecodeLaravelKeractorFV faVsco. PhostormVIewINavicarecodeLaravelKeractorFV faVsco.js?° pipedrive-sdk-pocProiectC ActivityController.ong© SyncProfileMetadata.phpCConvertLeadActivities.ong© SyncPlanhat.php©syncleammetadata.ong© TestPipedriveOfficialSdkCommand.phUpoateopponunityspecrications.onocreatePlaybookcreatedevent.ong>@ Dealinsights>@Dev> 0 Dialersc) IntearationApp/Service.php© LeadConverted.php© CreateSelfCoachedEvent.phpD DTOSC ElasticsearchC) CreateCommentedEvent.phoC) CreateSmsSentEvent.ohpC) PlanhatActivityListener.pho(C)AskAnvthinaPromotService.ohd› EnqagementStatsш GeckoExport(C)AutomatedReportsCommand.ohvphp api y2.ohoC) RequestGenerateReport.Job.oho/>D Livestream(C) AutomatedReportkesulconp© AutomatedReport.phpA console [STAGING]Mallboxes• MidratePlavbackithemes• M Plavbooksdectarelstrict_types-ty,Playlists> M Postmarknanespace Jininny console comnanos crm> M PronhetAuse…..v M Renorto© AutomatedReportsCommand.phpC) AutomatedRenortsRetentionPolicvCor14 Dclass syncubnects extends command© AutomatedReportsSendCommand.pht© CreateMockAskJiminnyReportResultC© DeleteReportCommand.php© GenerateMarketingReport.php© Team.php© Usage.php>CJ Slack* The name and sianature of the console commandi* ovar strind›_ Teams>C TracksC Transcription>C TwilioC Users> M Vocabularvorotected ssionature = crm:sunc-ohnects «team?, <--svncs'** The console command descrintion.>Zoom*Avan strinaC) Command.oho© CreateDatabaseUsers.ohpprotected $description = 'Sync remote CRM objects.';C)DatabaseTiableCount.oho© DeleteOIdAiCrmNotesCommand.ohv* Eyecuto the concole commandlCDeleteS?Leftoverscommand.ohn@ DevPostmanCommand.phppublic function handle: voidf...;C) DiarizeViaAiParticinantldentificationCom@ EncrvntTokensCommand.nhn(C EnaaaementStatsReaenerateCommand.n( FeatureFlaasHelner nhr69 6t)protected function getStaggerDelavSeconds@: floatf...?© FixCrossTenantlssues.php© FlushRolesPermissionsCache.php© GeneratelnternalWebhookToken.php9 GrounSetDefaultLanguageCommand.php74 6tprotected function getMaxDelavSeconds@: int{...}81 6t>protected function getlogPrefix@: stringf...;© HelperTruncateCoachingTables.phpHelner Code will hoin INF to underctand vour Laravel ann code II Generate II Don't Show Anvmore (8 minutes aaolV 132.134138=custom.log=laravel.log4 SF [jiminny@localhost]« HS_local [jiminny@localhost]A console [PROD]© TestPipedriveOfficialSdkCommand.php x © Kernel.phpA console [EU]class TestPipedrive0fficialSdkCommand extends Command117privace tunccion cestbastcuruauperaclons soclaLaccount ssoclaLAccounc. vola-rawubloken = ssoclaLaccount->cecrrovlderuserlokenoSthis->info( string: "DB token (Length:Sthis->info( string: "DB token preview: ". substr(SrawDbToken, offset: 0,lengthcheck token expirySexpires = SsocialAccount->expires:sthas->inrol string:"oken exoires at.Sexoires? carbon::createrromiimesSthis->infod"Token is expired:(Sexnires ss Sexpires ‹ timeo) ? 'v// Test with the DB token directlySthis->infod strinaTesting with DB token ---").Sconfio = new Confiquration0:Scontia->se+AcceccToken(SrawlhToken):/ Test 2a: Get DealsReject// Test 2a: Get Deals Summarysunls->restberuealsscontlo// Test 2b: Get PersonsSthis->testGetPersons(Sconfiq)/I Test 2c: Get Activitiessthis->testbetacrivitlesscontz0)} catch (Exception Se) {sthis->recordResult testName:'Basic CRUD Operations".success: false. Se->aet100% Sz• Mon 18 May 9:43:49AskJiminnyReportActivityServiceTest v+0 ..continue and test alllin the commandlwDebugging PipedrivThought for 1s0 / 4 tasks done• Update POC to test official SDK methods properlyTest vz APl support if avallable3. Test error handling properiy• TestPipedriveOfficialSdkCommand.phpcrror while ediuing lestripeoriveurticlalsakcommano.onpThoughtsRead TestPipedriveOfficialSdkCommand.oho #L175-224• TestPipedriveOfficialSdkCommand.php• TestPipedRead lestripecialsdkCommand.oho #L3/9-408•TestPipedriveOfficialSdkCommand.php• TestPipedriveOfficialSdkCommand.phpcommand docker• docker exec dockerlamn 1 nhn artisanminnv.test-ninedrive-official-cdk 10+6 -6+20 -14+5 -51+16 -6152181198216private function testCurrentSystemApproach(SocialAccount $socialAccount): voidt...}private function testGetDeals(Configuration Sconfig): void{...}private function testGetPersons(Configuration Sconfig): void{...}private function testGetActivities(Configuration Sconfig): void{...}1 usageprivate function testFieldMetadata(SocialAccount SsocialAccount): void{...}privata funation tgst' Accept File &- Cogfiauaatiion, Sconfig) or voigi → . }Ask anvthina (&4b÷ « CodeSWF.16* Reiect allAccent alliWN Windsurf Toams 127-1UTF.8io 4 spaces...
|
NULL
|
-8851624223078114382
|
NULL
|
visual_change
|
ocr
|
NULL
|
PhostormVIewINavicarecodeLaravelKeractorFV faVsco. PhostormVIewINavicarecodeLaravelKeractorFV faVsco.js?° pipedrive-sdk-pocProiectC ActivityController.ong© SyncProfileMetadata.phpCConvertLeadActivities.ong© SyncPlanhat.php©syncleammetadata.ong© TestPipedriveOfficialSdkCommand.phUpoateopponunityspecrications.onocreatePlaybookcreatedevent.ong>@ Dealinsights>@Dev> 0 Dialersc) IntearationApp/Service.php© LeadConverted.php© CreateSelfCoachedEvent.phpD DTOSC ElasticsearchC) CreateCommentedEvent.phoC) CreateSmsSentEvent.ohpC) PlanhatActivityListener.pho(C)AskAnvthinaPromotService.ohd› EnqagementStatsш GeckoExport(C)AutomatedReportsCommand.ohvphp api y2.ohoC) RequestGenerateReport.Job.oho/>D Livestream(C) AutomatedReportkesulconp© AutomatedReport.phpA console [STAGING]Mallboxes• MidratePlavbackithemes• M Plavbooksdectarelstrict_types-ty,Playlists> M Postmarknanespace Jininny console comnanos crm> M PronhetAuse…..v M Renorto© AutomatedReportsCommand.phpC) AutomatedRenortsRetentionPolicvCor14 Dclass syncubnects extends command© AutomatedReportsSendCommand.pht© CreateMockAskJiminnyReportResultC© DeleteReportCommand.php© GenerateMarketingReport.php© Team.php© Usage.php>CJ Slack* The name and sianature of the console commandi* ovar strind›_ Teams>C TracksC Transcription>C TwilioC Users> M Vocabularvorotected ssionature = crm:sunc-ohnects «team?, <--svncs'** The console command descrintion.>Zoom*Avan strinaC) Command.oho© CreateDatabaseUsers.ohpprotected $description = 'Sync remote CRM objects.';C)DatabaseTiableCount.oho© DeleteOIdAiCrmNotesCommand.ohv* Eyecuto the concole commandlCDeleteS?Leftoverscommand.ohn@ DevPostmanCommand.phppublic function handle: voidf...;C) DiarizeViaAiParticinantldentificationCom@ EncrvntTokensCommand.nhn(C EnaaaementStatsReaenerateCommand.n( FeatureFlaasHelner nhr69 6t)protected function getStaggerDelavSeconds@: floatf...?© FixCrossTenantlssues.php© FlushRolesPermissionsCache.php© GeneratelnternalWebhookToken.php9 GrounSetDefaultLanguageCommand.php74 6tprotected function getMaxDelavSeconds@: int{...}81 6t>protected function getlogPrefix@: stringf...;© HelperTruncateCoachingTables.phpHelner Code will hoin INF to underctand vour Laravel ann code II Generate II Don't Show Anvmore (8 minutes aaolV 132.134138=custom.log=laravel.log4 SF [jiminny@localhost]« HS_local [jiminny@localhost]A console [PROD]© TestPipedriveOfficialSdkCommand.php x © Kernel.phpA console [EU]class TestPipedrive0fficialSdkCommand extends Command117privace tunccion cestbastcuruauperaclons soclaLaccount ssoclaLAccounc. vola-rawubloken = ssoclaLaccount->cecrrovlderuserlokenoSthis->info( string: "DB token (Length:Sthis->info( string: "DB token preview: ". substr(SrawDbToken, offset: 0,lengthcheck token expirySexpires = SsocialAccount->expires:sthas->inrol string:"oken exoires at.Sexoires? carbon::createrromiimesSthis->infod"Token is expired:(Sexnires ss Sexpires ‹ timeo) ? 'v// Test with the DB token directlySthis->infod strinaTesting with DB token ---").Sconfio = new Confiquration0:Scontia->se+AcceccToken(SrawlhToken):/ Test 2a: Get DealsReject// Test 2a: Get Deals Summarysunls->restberuealsscontlo// Test 2b: Get PersonsSthis->testGetPersons(Sconfiq)/I Test 2c: Get Activitiessthis->testbetacrivitlesscontz0)} catch (Exception Se) {sthis->recordResult testName:'Basic CRUD Operations".success: false. Se->aet100% Sz• Mon 18 May 9:43:49AskJiminnyReportActivityServiceTest v+0 ..continue and test alllin the commandlwDebugging PipedrivThought for 1s0 / 4 tasks done• Update POC to test official SDK methods properlyTest vz APl support if avallable3. Test error handling properiy• TestPipedriveOfficialSdkCommand.phpcrror while ediuing lestripeoriveurticlalsakcommano.onpThoughtsRead TestPipedriveOfficialSdkCommand.oho #L175-224• TestPipedriveOfficialSdkCommand.php• TestPipedRead lestripecialsdkCommand.oho #L3/9-408•TestPipedriveOfficialSdkCommand.php• TestPipedriveOfficialSdkCommand.phpcommand docker• docker exec dockerlamn 1 nhn artisanminnv.test-ninedrive-official-cdk 10+6 -6+20 -14+5 -51+16 -6152181198216private function testCurrentSystemApproach(SocialAccount $socialAccount): voidt...}private function testGetDeals(Configuration Sconfig): void{...}private function testGetPersons(Configuration Sconfig): void{...}private function testGetActivities(Configuration Sconfig): void{...}1 usageprivate function testFieldMetadata(SocialAccount SsocialAccount): void{...}privata funation tgst' Accept File &- Cogfiauaatiion, Sconfig) or voigi → . }Ask anvthina (&4b÷ « CodeSWF.16* Reiect allAccent alliWN Windsurf Toams 127-1UTF.8io 4 spaces...
|
49422
|
NULL
|
NULL
|
NULL
|
|
49423
|
1764
|
6
|
2026-05-18T06:43:48.099119+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779086628099_m1.jpg...
|
PhpStorm
|
faVsco.js – TestPipedriveOfficialSdkCommand.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
|
[{"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}]...
|
8043719072324535154
|
-8628527368849355612
|
click
|
hybrid
|
NULL
|
Project: faVsco.js, menu
iTerm2ShelllEditViewSessi Project: faVsco.js, menu
iTerm2ShelllEditViewSessionScriptsProfilesWindowHelp•• 0APP (-zsh)DOCKERLast login: Mon May 18 09:17:28 on ttys007O 81DEV (-zsh)O 82Poetry could not find a pyproject.toml file in /Users/lukas/jiminny/app or its parentsPoetry could not find a pyproject.toml file in /Users/lukas/jiminny/app or its parentslukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/jiminny/app (pipedrive-sdk-poc) $ U‹ $0APP (-zsh)|83100% C47 8• Mon 18 May 9:43:47T₴1screenpipe*O &4APP...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
49422
|
1765
|
6
|
2026-05-18T06:43:46.505933+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779086626505_m2.jpg...
|
PhpStorm
|
faVsco.js – TestPipedriveOfficialSdkCommand.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
PhostormVIewINavicarecodeLaravelKeractorFV faVsco. PhostormVIewINavicarecodeLaravelKeractorFV faVsco.js?° pipedrive-sdk-pocProiectC ActivityController.ong© SyncProfileMetadata.phpCConvertLeadActivities.ong© SyncPlanhat.php©syncleammeradata.ong© TestPipedriveOfficialSdkCommand.phUpoateopponunityspecrications.onocreatePlaybookcreatedevent.ong>@ Dealinsights>@Dev> 0 Dialersc) IntearationApp/Service.php© LeadConverted.php© CreateSelfCoachedEvent.phpD DTOSC ElasticsearchC) CreateCommentedEvent.phoC) CreateSmsSentEvent.ohpC) PlanhatActivityListener.pho(C)AskAnvthinaPromotService.ohd› EnqagementStatsш GeckoExport(C)AutomatedReportsCommand.ohvphp api y2.ohoC) RequestGenerateReport.Job.oho/>D Livestream(C) AutomatedReportkesulconp© AutomatedReport.phpA console [STAGING]Mallboxes• MidratePlavbackithemes• M Plavbooksdectarelstrict_types-ty,Playlists> M Postmarknanespace Jininny console comnanos crm> M PronhetAuse…..v M Renorto© AutomatedReportsCommand.phpC) AutomatedRenortsRetentionPolicvCor14 Dclass syncubnects extends command© AutomatedReportsSendCommand.pht© CreateMockAskJiminnyReportResultC© DeleteReportCommand.php© GenerateMarketingReport.php© Team.php© Usage.php>C Slack* The name and sianature of the console commandi* ovar strind›_ Teams>C TracksC Transcription>C TwilioC Users> M Vocabularv7301nrotected ssionature = crm:sunc-ohnects -team?, ~--svnctr** The console command descrintion.>Zoom*Avan strinaC) Command.oho© CreateDatabaseUsers.ohpprotected $description = 'Sync remote CRM objects.';C)DatabaseTiableCount.oho© DeleteOIdAiCrmNotesCommand.ohv* Eyecuto the concole commandlCDeleteS?Leftoverscommand.ohn@ DevPostmanCommand.phppublic function handle: voidf...;C) DiarizeViaAiParticinantldentificationCom@ EncrvntTokensCommand.nhn(C EnaaaementStatsReaenerateCommand.n( FeatureFlaasHelner nhr69 6t)protected function getStaggerDelavSeconds@: floatf...© FixCrossTenantlssues.php© FlushRolesPermissionsCache.php© GeneratelnternalWebhookToken.php9 GrounSetDefaultLanguageCommand.php74 6tprotected function getMaxDelavSeconds@: int{...}81 6t>protected function getlogPrefix@: stringf...;© HelperTruncateCoachingTables.phpHelner Code will hoin INF to underctand vour Laravel ann code II Generate II Don't Show Anvmore (8 minutes aaol=custom.log=laravel.log4 SF [jiminny@localhost]« HS_local [jiminny@localhost]A console [PROD]© TestPipedriveOfficialSdkCommand.php x © Kernel.phpA console [EU]class TestPipedrive0fficialSdkCommand extends Command117private tunction testbasiceruduperations (soclaLaccount esoclaLaccount): voia132||133134138-rawubloken = ssoclaLaccount->cecrrovlderuserlokenorSthis->info( string: "DB token (Length:Sthis->info( string: "DB token preview: ". substr(SrawDbToken, offset: 0,lengthcheck token expirySexpires = SsocialAccount->expires:sthas->inrol string:"oken exoires at."Sexoires? carbon::createrromimesSthis->infod"Token is expired:(Sexnires ss Sexpires ‹ timeo) ? 'v// Test with the DB token directlySthis->infod strinaTesting wilth D8 token ---").Sconfio = new Confiquration0:Scontia->se+AcceccToken(SrawlhToken):/ Test 2a: Get DealsReject// Test 2a: Get Deals Summarysunls->restberuealsscontlo// Test 2b: Get PersonsSthis->testGetPersons(Sconfiq)/I Test 2c: Get Activitiessthis->testbetacrivitlescont10)} catch (Exception Se) {sthis->recordResult testName:'Basic CRUD Operations".success: false. Se->aet100% Sz• Mon 18 May 9:43:46AskJiminnyReportActivityServiceTest v+0 ..continue and test alllin the commandlDebugging Pipedriv0 / 4 tasks do• Update POC to test official SDK methods properlyTest vz APl support if avallable6 Test error handlina properis• TestPipedriveOfficialSdkCommand.phpError while editino lestripedriveurticlalsokcommano.onp• TestPnand.oho #L3/9-408• TestPipedriveOfficialSdkCommand.php• Testcommand docker• docker exec dockerlamn 1 nhn artisanminnv.test-ninedrive-official-cdk 10+6 -6+20 -14+16 -6152181198216Run *d Skioprivate function testCurrentSystemApproach(SocialAccount SsocialAccount): voidf...?private function testGetDeals(Configuration Sconfig): void{...}private function testGetPersons(Configuration $config): void{...}private function testGetActivities(Configuration Sconfig): void{...}1 usageprivate function testFieldMetadata(SocialAccount SsocialAccount): void{...}privata funation tgst' Accept File &- Cogfiauaatiion, Sconfig) or voigi → . }Ask anvthina (&4b÷ « CodeSWF.16* Reiect allAccent alliWN Windsurf Teams127-1UTE.8io 4 spaces...
|
NULL
|
8063380989875096772
|
NULL
|
visual_change
|
ocr
|
NULL
|
PhostormVIewINavicarecodeLaravelKeractorFV faVsco. PhostormVIewINavicarecodeLaravelKeractorFV faVsco.js?° pipedrive-sdk-pocProiectC ActivityController.ong© SyncProfileMetadata.phpCConvertLeadActivities.ong© SyncPlanhat.php©syncleammeradata.ong© TestPipedriveOfficialSdkCommand.phUpoateopponunityspecrications.onocreatePlaybookcreatedevent.ong>@ Dealinsights>@Dev> 0 Dialersc) IntearationApp/Service.php© LeadConverted.php© CreateSelfCoachedEvent.phpD DTOSC ElasticsearchC) CreateCommentedEvent.phoC) CreateSmsSentEvent.ohpC) PlanhatActivityListener.pho(C)AskAnvthinaPromotService.ohd› EnqagementStatsш GeckoExport(C)AutomatedReportsCommand.ohvphp api y2.ohoC) RequestGenerateReport.Job.oho/>D Livestream(C) AutomatedReportkesulconp© AutomatedReport.phpA console [STAGING]Mallboxes• MidratePlavbackithemes• M Plavbooksdectarelstrict_types-ty,Playlists> M Postmarknanespace Jininny console comnanos crm> M PronhetAuse…..v M Renorto© AutomatedReportsCommand.phpC) AutomatedRenortsRetentionPolicvCor14 Dclass syncubnects extends command© AutomatedReportsSendCommand.pht© CreateMockAskJiminnyReportResultC© DeleteReportCommand.php© GenerateMarketingReport.php© Team.php© Usage.php>C Slack* The name and sianature of the console commandi* ovar strind›_ Teams>C TracksC Transcription>C TwilioC Users> M Vocabularv7301nrotected ssionature = crm:sunc-ohnects -team?, ~--svnctr** The console command descrintion.>Zoom*Avan strinaC) Command.oho© CreateDatabaseUsers.ohpprotected $description = 'Sync remote CRM objects.';C)DatabaseTiableCount.oho© DeleteOIdAiCrmNotesCommand.ohv* Eyecuto the concole commandlCDeleteS?Leftoverscommand.ohn@ DevPostmanCommand.phppublic function handle: voidf...;C) DiarizeViaAiParticinantldentificationCom@ EncrvntTokensCommand.nhn(C EnaaaementStatsReaenerateCommand.n( FeatureFlaasHelner nhr69 6t)protected function getStaggerDelavSeconds@: floatf...© FixCrossTenantlssues.php© FlushRolesPermissionsCache.php© GeneratelnternalWebhookToken.php9 GrounSetDefaultLanguageCommand.php74 6tprotected function getMaxDelavSeconds@: int{...}81 6t>protected function getlogPrefix@: stringf...;© HelperTruncateCoachingTables.phpHelner Code will hoin INF to underctand vour Laravel ann code II Generate II Don't Show Anvmore (8 minutes aaol=custom.log=laravel.log4 SF [jiminny@localhost]« HS_local [jiminny@localhost]A console [PROD]© TestPipedriveOfficialSdkCommand.php x © Kernel.phpA console [EU]class TestPipedrive0fficialSdkCommand extends Command117private tunction testbasiceruduperations (soclaLaccount esoclaLaccount): voia132||133134138-rawubloken = ssoclaLaccount->cecrrovlderuserlokenorSthis->info( string: "DB token (Length:Sthis->info( string: "DB token preview: ". substr(SrawDbToken, offset: 0,lengthcheck token expirySexpires = SsocialAccount->expires:sthas->inrol string:"oken exoires at."Sexoires? carbon::createrromimesSthis->infod"Token is expired:(Sexnires ss Sexpires ‹ timeo) ? 'v// Test with the DB token directlySthis->infod strinaTesting wilth D8 token ---").Sconfio = new Confiquration0:Scontia->se+AcceccToken(SrawlhToken):/ Test 2a: Get DealsReject// Test 2a: Get Deals Summarysunls->restberuealsscontlo// Test 2b: Get PersonsSthis->testGetPersons(Sconfiq)/I Test 2c: Get Activitiessthis->testbetacrivitlescont10)} catch (Exception Se) {sthis->recordResult testName:'Basic CRUD Operations".success: false. Se->aet100% Sz• Mon 18 May 9:43:46AskJiminnyReportActivityServiceTest v+0 ..continue and test alllin the commandlDebugging Pipedriv0 / 4 tasks do• Update POC to test official SDK methods properlyTest vz APl support if avallable6 Test error handlina properis• TestPipedriveOfficialSdkCommand.phpError while editino lestripedriveurticlalsokcommano.onp• TestPnand.oho #L3/9-408• TestPipedriveOfficialSdkCommand.php• Testcommand docker• docker exec dockerlamn 1 nhn artisanminnv.test-ninedrive-official-cdk 10+6 -6+20 -14+16 -6152181198216Run *d Skioprivate function testCurrentSystemApproach(SocialAccount SsocialAccount): voidf...?private function testGetDeals(Configuration Sconfig): void{...}private function testGetPersons(Configuration $config): void{...}private function testGetActivities(Configuration Sconfig): void{...}1 usageprivate function testFieldMetadata(SocialAccount SsocialAccount): void{...}privata funation tgst' Accept File &- Cogfiauaatiion, Sconfig) or voigi → . }Ask anvthina (&4b÷ « CodeSWF.16* Reiect allAccent alliWN Windsurf Teams127-1UTE.8io 4 spaces...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
49420
|
1764
|
5
|
2026-05-18T06:43:42.839686+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779086622839_m1.jpg...
|
PhpStorm
|
faVsco.js – TestPipedriveOfficialSdkCommand.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
pipedrive-sdk-poc, menu
S Project: faVsco.js, menu
pipedrive-sdk-poc, 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\Console\Commands\Crm;
use Jiminny\Console\Commands\Command;
use Jiminny\Console\Commands\Crm\Traits\SyncObjectsCommandTrait;
use Jiminny\Jobs\Crm\SyncObjects as SyncObjectsJob;
use Jiminny\Jobs\Job;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Models\Team;
class SyncObjects extends Command
{
use SyncObjectsCommandTrait;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'crm:sync-objects {team?} {--sync}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Sync remote CRM objects.';
/**
* Execute the console command.
*/
public function handle(): void
{
$teamId = $this->argument('team');
$teams = [];
if ($teamId) {
$team = Team::idOrUuId($teamId);
if ($team && $team->crm?->provider === Configuration::PROVIDER_HUBSPOT) {
$this->error(sprintf(
'Team %s (%s) uses HubSpot. Use crm:sync-hubspot-objects instead.',
$team->getName(),
$team->getUuid()
));
return;
}
if ($team) {
$teams[] = $team;
}
} else {
// Exclude HubSpot teams - handled by crm:sync-hubspot-objects
$teams = Team::where('status', Team::STATUS_ACTIVE)
->whereHas(
'crm',
fn ($q) => $q
->where('provider', '!=', Configuration::PROVIDER_HUBSPOT)
->where('sync_objects', true)
)
->get();
}
$this->dispatchSyncJobsForTeams($teams);
}
protected function getStaggerDelaySeconds(): float
{
return 2;
}
protected function getMaxDelaySeconds(): int
{
// Cap delay at 15 minutes (SQS max delay limit)
// capacity of 450 teams before reach
return 900;
}
protected function getLogPrefix(): string
{
return '';
}
protected function createSyncJob(Team $team): Job
{
return new SyncObjectsJob($team);
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
1
51
1
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Console\Commands\Crm;
use Carbon\Carbon;
use Exception;
use Illuminate\Console\Command;
use Jiminny\Component\Encryption\EncryptedTokenManager;
use Jiminny\Models\SocialAccount;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Pipedrive\versions\v1\Api\ActivitiesApi;
use Pipedrive\versions\v1\Api\ActivityFieldsApi;
use Pipedrive\versions\v1\Api\DealFieldsApi;
use Pipedrive\versions\v1\Api\DealsApi;
use Pipedrive\versions\v1\Api\PersonFieldsApi;
use Pipedrive\versions\v1\Api\PersonsApi;
use Pipedrive\versions\v1\Configuration;
class TestPipedriveOfficialSdkCommand extends Command
{
protected $signature = 'jiminny:test-pipedrive-official-sdk {teamId}';
protected $description = 'Test official Pipedrive SDK (pipedrive/pipedrive) with existing OAuth tokens';
private array $results = [];
private EncryptedTokenManager $tokenManager;
public function __construct(EncryptedTokenManager $tokenManager)
{
parent::__construct();
$this->tokenManager = $tokenManager;
}
public function handle(): int
{
$teamId = (int) $this->argument('teamId');
$this->info("Starting Pipedrive Official SDK POC for Team ID: {$teamId}");
$this->newLine();
try {
$this->runTests($teamId);
} catch (Exception $e) {
$this->error("POC failed with error: {$e->getMessage()}");
$this->error($e->getTraceAsString());
return 1;
}
$this->displayResults();
return 0;
}
private function runTests(int $teamId): void
{
$team = Team::find($teamId);
if (! $team) {
throw new Exception("Team with ID {$teamId} not found");
}
$socialAccount = SocialAccount::where('provider', SocialAccount::PROVIDER_PIPEDRIVE)
->whereHas('sociable', function ($query) use ($team) {
$query->where('team_id', $team->id);
})
->orderByDesc('expires') // Get the most recently expiring token
->first();
if (! $socialAccount) {
throw new Exception("No Pipedrive social account found for team {$teamId}");
}
$this->info("Found Pipedrive account for team: {$team->name}");
$this->newLine();
// Test 1: OAuth Initialization
$this->testOAuthInitialization($socialAccount);
// Test 2: Basic CRUD Operations
$this->testBasicCrudOperations($socialAccount);
// Test 3: Field Metadata
$this->testFieldMetadata($socialAccount);
// Test 4: v2 API Support
$this->testV2ApiSupport($socialAccount);
// Test 5: Error Handling
$this->testErrorHandling($socialAccount);
}
private function testOAuthInitialization(SocialAccount $socialAccount): void
{
$this->info('Test 1: OAuth Initialization');
$startTime = microtime(true);
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
$client = new DealsApi(null, $config);
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('OAuth Initialization', true, "Client initialized successfully in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('OAuth Initialization', false, $e->getMessage(), $elapsed);
}
$this->newLine();
}
private function testBasicCrudOperations(SocialAccount $socialAccount): void
{
$this->info('Test 2: Basic CRUD Operations');
try {
// Get the raw token from database
$rawDbToken = $socialAccount->getProviderUserToken();
$this->info("DB token (length: " . strlen($rawDbToken) . ")");
$this->info("DB token preview: " . substr($rawDbToken, 0, 20) . "...");
// Check token expiry
$expires = $socialAccount->expires;
$this->info("Token expires at: " . ($expires ? Carbon::createFromTimestamp($expires)->toDateTimeString() : 'null'));
$this->info("Token is expired: " . ($expires && $expires < time() ? 'YES' : 'NO'));
// Test with the DB token directly
$this->info("\n--- Testing with DB token ---");
$config = new Configuration();
$config->setAccessToken($rawDbToken);
// Test 2a: Get Deals Summary
$this->testGetDeals($config);
// Test 2b: Get Persons
$this->testGetPersons($config);
// Test 2c: Get Activities
$this->testGetActivities($config);
} catch (Exception $e) {
$this->recordResult('Basic CRUD Operations', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testCurrentSystemApproach(SocialAccount $socialAccount): void
{
$startTime = microtime(true);
try {
// Simulate how current system makes requests
$token = $socialAccount->getProviderUserToken();
$url = 'https://api.pipedrive.com/api/v2/deals';
$headers = [
'Authorization' => 'Bearer ' . $token,
'Accept' => 'application/json',
];
$client = new \GuzzleHttp\Client();
$response = $client->get($url, [
'headers' => $headers,
'timeout' => 30,
]);
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$statusCode = $response->getStatusCode();
$this->recordResult('Current System v2 API', $statusCode === 200, "Status: {$statusCode} in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Current System v2 API', false, $e->getMessage(), $elapsed);
}
}
private function testGetDeals(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new DealsApi(null, $config);
// Official SDK uses getDealsSummary instead of getDeals
$result = $apiInstance->getDealsSummary();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Deals Summary', true, "Retrieved deals summary in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Deals Summary', false, $e->getMessage(), $elapsed);
}
}
private function testGetPersons(Configuration $config): void
{
$this->info('Test 2b: Get Persons');
$startTime = microtime(true);
try {
$apiInstance = new PersonsApi(null, $config);
// Official SDK uses getPersons method
$result = $apiInstance->getPersons(['limit' => 10]);
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Persons', true, "Retrieved persons in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Persons', false, $e->getMessage(), $elapsed);
}
}
private function testGetActivities(Configuration $config): void
{
$this->info('Test 2c: Get Activities');
$startTime = microtime(true);
try {
$apiInstance = new ActivitiesApi(null, $config);
// Official SDK uses getActivities method
$result = $apiInstance->getActivities(['limit' => 10]);
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Activities', true, "Retrieved activities in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Activities', false, $e->getMessage(), $elapsed);
}
}
private function testFieldMetadata(SocialAccount $socialAccount): void
{
$this->info('Test 3: Field Metadata');
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
// Test 3a: Get Deal Fields
$this->testGetDealFields($config);
// Test 3b: Get Person Fields
$this->testGetPersonFields($config);
// Test 3c: Get Activity Fields
$this->testGetActivityFields($config);
} catch (Exception $e) {
$this->recordResult('Field Metadata', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testGetDealFields(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new DealFieldsApi(null, $config);
$result = $apiInstance->getDealFields();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$count = $result->getData() ? count($result->getData()) : 0;
$this->recordResult('Get Deal Fields', true, "Retrieved {$count} deal fields in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Deal Fields', false, $e->getMessage(), $elapsed);
}
}
private function testGetPersonFields(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new PersonFieldsApi(null, $config);
$result = $apiInstance->getPersonFields();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$count = $result->getData() ? count($result->getData()) : 0;
$this->recordResult('Get Person Fields', true, "Retrieved {$count} person fields in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Person Fields', false, $e->getMessage(), $elapsed);
}
}
private function testGetActivityFields(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new ActivityFieldsApi(null, $config);
$result = $apiInstance->getActivityFields();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$count = $result->getData() ? count($result->getData()) : 0;
$this->recordResult('Get Activity Fields', true, "Retrieved {$count} activity fields in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Activity Fields', false, $e->getMessage(), $elapsed);
}
}
private function testV2ApiSupport(SocialAccount $socialAccount): void
{
$this->info('Test 4: v2 API Support (Cursor-based Pagination)');
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
$startTime = microtime(true);
// Test v2 deals endpoint with cursor pagination
// Note: The official SDK may or may not have v2 API support
// This test will determine if v2 is available
try {
$apiInstance = new DealsApi(null, $config);
$updatedSince = Carbon::now()->subDays(30)->toRfc3339String();
$result = $apiInstance->getDealsSummary();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
// Check if response has cursor information (v2 indicator)
// The response object doesn't have getAdditionalData method
// Just test if the call works - v2 API support is limited in official SDK v1
$this->recordResult('v2 API Support', false, "v1 API works but v2 cursor pagination not available in official SDK v1. Retrieved summary in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('v2 API Support', false, $e->getMessage(), $elapsed);
}
} catch (Exception $e) {
$this->recordResult('v2 API Support', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testErrorHandling(SocialAccount $socialAccount): void
{
$this->info('Test 5: Error Handling');
try {
// Test 5a: Invalid Token
$this->testInvalidToken();
// Test 5b: Non-existent Resource
$this->testNonExistentResource($socialAccount);
} catch (Exception $e) {
$this->recordResult('Error Handling', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testInvalidToken(): void
{
$this->info('Test 5a: Invalid Token');
$startTime = microtime(true);
try {
$config = new Configuration();
$config->setAccessToken('invalid_token');
$client = new DealsApi(null, $config);
$client->getDealsSummary();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Invalid Token Error', false, "Expected error but request succeeded in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Invalid Token Error', true, "Correctly threw exception: {$e->getMessage()} in {$elapsed}ms", $elapsed);
}
}
private function testNonExistentResource(SocialAccount $socialAccount): void
{
$this->info('Test 5b: Non-existent Resource');
$startTime = microtime(true);
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
$client = new DealsApi(null, $config);
// Try to get a deal with a very high ID that likely doesn't exist
// Official SDK has getDealsSummary which works without specific ID
// Instead, let's test with an invalid field ID
try {
$fieldClient = new DealFieldsApi(null, $config);
$fieldClient->getDealField(999999999); // Very high ID that likely doesn't exist
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Non-existent Resource Error', false, "Expected error but request succeeded in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Non-existent Resource Error', true, "Correctly threw exception: {$e->getMessage()} in {$elapsed}ms", $elapsed);
}
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Non-existent Resource Error', false, $e->getMessage(), $elapsed);
}
}
private function recordResult(string $testName, bool $success, string $message, float $elapsed): void
{
$this->results[] = [
'test' => $testName,
'success' => $success,
'message' => $message,
'elapsed' => $elapsed,
];
$status = $success ? '✅ PASS' : '❌ FAIL';
$this->line(" {$status} - {$testName}: {$message}");
}
private function displayResults(): void
{
$this->newLine();
$this->info('=== POC Test Results Summary ===');
$this->newLine();
$total = count($this->results);
$passed = count(array_filter($this->results, fn ($r) => $r['success']));
$failed = $total - $passed;
$this->line("Total Tests: {$total}");
$this->line("Passed: {$passed}");
$this->line("Failed: {$failed}");
$this->newLine();
$totalTime = array_sum(array_column($this->results, 'elapsed'));
$this->line("Total Execution Time: {$totalTime}ms");
$this->newLine();
if ($failed > 0) {
$this->warn('Failed Tests:');
foreach ($this->results as $result) {
if (! $result['success']) {
$this->line(" - {$result['test']}: {$result['message']}");
}
}
$this->newLine();
}
$this->info('=== Detailed Results ===');
foreach ($this->results as $result) {
$status = $result['success'] ? '✅' : '❌';
$this->line("{$status} {$result['test']} ({$result['elapsed']}ms): {$result['message']}");
}
$this->newLine();
$this->info('=== Recommendations ===');
if ($passed === $total) {
$this->info('✅ All tests passed! The official SDK is viable for migration.');
$this->info('Next steps: Proceed with full migration planning.');
} else {
$this->warn('⚠️ Some tests failed. Review the failures above before proceeding.');
$this->warn('Consider hybrid approach or investigate alternatives if critical operations fail.');
}
}
}
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":"pipedrive-sdk-poc, menu","depth":5,"on_screen":true,"help_text":"Git Branch: pipedrive-sdk-poc","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\\Console\\Commands\\Crm;\n\nuse Jiminny\\Console\\Commands\\Command;\nuse Jiminny\\Console\\Commands\\Crm\\Traits\\SyncObjectsCommandTrait;\nuse Jiminny\\Jobs\\Crm\\SyncObjects as SyncObjectsJob;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Models\\Team;\n\nclass SyncObjects extends Command\n{\n use SyncObjectsCommandTrait;\n\n /**\n * The name and signature of the console command.\n *\n * @var string\n */\n protected $signature = 'crm:sync-objects {team?} {--sync}';\n\n /**\n * The console command description.\n *\n * @var string\n */\n protected $description = 'Sync remote CRM objects.';\n\n /**\n * Execute the console command.\n */\n public function handle(): void\n {\n $teamId = $this->argument('team');\n $teams = [];\n\n if ($teamId) {\n $team = Team::idOrUuId($teamId);\n if ($team && $team->crm?->provider === Configuration::PROVIDER_HUBSPOT) {\n $this->error(sprintf(\n 'Team %s (%s) uses HubSpot. Use crm:sync-hubspot-objects instead.',\n $team->getName(),\n $team->getUuid()\n ));\n\n return;\n }\n if ($team) {\n $teams[] = $team;\n }\n } else {\n // Exclude HubSpot teams - handled by crm:sync-hubspot-objects\n $teams = Team::where('status', Team::STATUS_ACTIVE)\n ->whereHas(\n 'crm',\n fn ($q) => $q\n ->where('provider', '!=', Configuration::PROVIDER_HUBSPOT)\n ->where('sync_objects', true)\n )\n ->get();\n }\n\n $this->dispatchSyncJobsForTeams($teams);\n }\n\n protected function getStaggerDelaySeconds(): float\n {\n return 2;\n }\n\n protected function getMaxDelaySeconds(): int\n {\n // Cap delay at 15 minutes (SQS max delay limit)\n // capacity of 450 teams before reach\n return 900;\n }\n\n protected function getLogPrefix(): string\n {\n return '';\n }\n\n protected function createSyncJob(Team $team): Job\n {\n return new SyncObjectsJob($team);\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Console\\Commands\\Crm;\n\nuse Jiminny\\Console\\Commands\\Command;\nuse Jiminny\\Console\\Commands\\Crm\\Traits\\SyncObjectsCommandTrait;\nuse Jiminny\\Jobs\\Crm\\SyncObjects as SyncObjectsJob;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Models\\Team;\n\nclass SyncObjects extends Command\n{\n use SyncObjectsCommandTrait;\n\n /**\n * The name and signature of the console command.\n *\n * @var string\n */\n protected $signature = 'crm:sync-objects {team?} {--sync}';\n\n /**\n * The console command description.\n *\n * @var string\n */\n protected $description = 'Sync remote CRM objects.';\n\n /**\n * Execute the console command.\n */\n public function handle(): void\n {\n $teamId = $this->argument('team');\n $teams = [];\n\n if ($teamId) {\n $team = Team::idOrUuId($teamId);\n if ($team && $team->crm?->provider === Configuration::PROVIDER_HUBSPOT) {\n $this->error(sprintf(\n 'Team %s (%s) uses HubSpot. Use crm:sync-hubspot-objects instead.',\n $team->getName(),\n $team->getUuid()\n ));\n\n return;\n }\n if ($team) {\n $teams[] = $team;\n }\n } else {\n // Exclude HubSpot teams - handled by crm:sync-hubspot-objects\n $teams = Team::where('status', Team::STATUS_ACTIVE)\n ->whereHas(\n 'crm',\n fn ($q) => $q\n ->where('provider', '!=', Configuration::PROVIDER_HUBSPOT)\n ->where('sync_objects', true)\n )\n ->get();\n }\n\n $this->dispatchSyncJobsForTeams($teams);\n }\n\n protected function getStaggerDelaySeconds(): float\n {\n return 2;\n }\n\n protected function getMaxDelaySeconds(): int\n {\n // Cap delay at 15 minutes (SQS max delay limit)\n // capacity of 450 teams before reach\n return 900;\n }\n\n protected function getLogPrefix(): string\n {\n return '';\n }\n\n protected function createSyncJob(Team $team): Job\n {\n return new SyncObjectsJob($team);\n }\n}","role_description":"text entry area","is_enabled":true,"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":"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":"AXStaticText","text":"1","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.015277778,"height":0.02111111},"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"51","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.02013889,"height":0.02111111},"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"1","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.015277778,"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\\Console\\Commands\\Crm;\n\nuse Carbon\\Carbon;\nuse Exception;\nuse Illuminate\\Console\\Command;\nuse Jiminny\\Component\\Encryption\\EncryptedTokenManager;\nuse Jiminny\\Models\\SocialAccount;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Pipedrive\\versions\\v1\\Api\\ActivitiesApi;\nuse Pipedrive\\versions\\v1\\Api\\ActivityFieldsApi;\nuse Pipedrive\\versions\\v1\\Api\\DealFieldsApi;\nuse Pipedrive\\versions\\v1\\Api\\DealsApi;\nuse Pipedrive\\versions\\v1\\Api\\PersonFieldsApi;\nuse Pipedrive\\versions\\v1\\Api\\PersonsApi;\nuse Pipedrive\\versions\\v1\\Configuration;\n\nclass TestPipedriveOfficialSdkCommand extends Command\n{\n protected $signature = 'jiminny:test-pipedrive-official-sdk {teamId}';\n\n protected $description = 'Test official Pipedrive SDK (pipedrive/pipedrive) with existing OAuth tokens';\n\n private array $results = [];\n\n private EncryptedTokenManager $tokenManager;\n\n public function __construct(EncryptedTokenManager $tokenManager)\n {\n parent::__construct();\n $this->tokenManager = $tokenManager;\n }\n\n public function handle(): int\n {\n $teamId = (int) $this->argument('teamId');\n\n $this->info(\"Starting Pipedrive Official SDK POC for Team ID: {$teamId}\");\n $this->newLine();\n\n try {\n $this->runTests($teamId);\n } catch (Exception $e) {\n $this->error(\"POC failed with error: {$e->getMessage()}\");\n $this->error($e->getTraceAsString());\n\n return 1;\n }\n\n $this->displayResults();\n\n return 0;\n }\n\n private function runTests(int $teamId): void\n {\n $team = Team::find($teamId);\n if (! $team) {\n throw new Exception(\"Team with ID {$teamId} not found\");\n }\n\n $socialAccount = SocialAccount::where('provider', SocialAccount::PROVIDER_PIPEDRIVE)\n ->whereHas('sociable', function ($query) use ($team) {\n $query->where('team_id', $team->id);\n })\n ->orderByDesc('expires') // Get the most recently expiring token\n ->first();\n\n if (! $socialAccount) {\n throw new Exception(\"No Pipedrive social account found for team {$teamId}\");\n }\n\n $this->info(\"Found Pipedrive account for team: {$team->name}\");\n $this->newLine();\n\n // Test 1: OAuth Initialization\n $this->testOAuthInitialization($socialAccount);\n\n // Test 2: Basic CRUD Operations\n $this->testBasicCrudOperations($socialAccount);\n\n // Test 3: Field Metadata\n $this->testFieldMetadata($socialAccount);\n\n // Test 4: v2 API Support\n $this->testV2ApiSupport($socialAccount);\n\n // Test 5: Error Handling\n $this->testErrorHandling($socialAccount);\n }\n\n private function testOAuthInitialization(SocialAccount $socialAccount): void\n {\n $this->info('Test 1: OAuth Initialization');\n $startTime = microtime(true);\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n\n $client = new DealsApi(null, $config);\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('OAuth Initialization', true, \"Client initialized successfully in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('OAuth Initialization', false, $e->getMessage(), $elapsed);\n }\n\n $this->newLine();\n }\n\n private function testBasicCrudOperations(SocialAccount $socialAccount): void\n {\n $this->info('Test 2: Basic CRUD Operations');\n\n try {\n // Get the raw token from database\n $rawDbToken = $socialAccount->getProviderUserToken();\n $this->info(\"DB token (length: \" . strlen($rawDbToken) . \")\");\n $this->info(\"DB token preview: \" . substr($rawDbToken, 0, 20) . \"...\");\n\n // Check token expiry\n $expires = $socialAccount->expires;\n $this->info(\"Token expires at: \" . ($expires ? Carbon::createFromTimestamp($expires)->toDateTimeString() : 'null'));\n $this->info(\"Token is expired: \" . ($expires && $expires < time() ? 'YES' : 'NO'));\n\n // Test with the DB token directly\n $this->info(\"\\n--- Testing with DB token ---\");\n $config = new Configuration();\n $config->setAccessToken($rawDbToken);\n\n // Test 2a: Get Deals Summary\n $this->testGetDeals($config);\n\n // Test 2b: Get Persons\n $this->testGetPersons($config);\n\n // Test 2c: Get Activities\n $this->testGetActivities($config);\n } catch (Exception $e) {\n $this->recordResult('Basic CRUD Operations', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testCurrentSystemApproach(SocialAccount $socialAccount): void\n {\n $startTime = microtime(true);\n\n try {\n // Simulate how current system makes requests\n $token = $socialAccount->getProviderUserToken();\n $url = 'https://api.pipedrive.com/api/v2/deals';\n\n $headers = [\n 'Authorization' => 'Bearer ' . $token,\n 'Accept' => 'application/json',\n ];\n\n $client = new \\GuzzleHttp\\Client();\n $response = $client->get($url, [\n 'headers' => $headers,\n 'timeout' => 30,\n ]);\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $statusCode = $response->getStatusCode();\n $this->recordResult('Current System v2 API', $statusCode === 200, \"Status: {$statusCode} in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Current System v2 API', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetDeals(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new DealsApi(null, $config);\n // Official SDK uses getDealsSummary instead of getDeals\n $result = $apiInstance->getDealsSummary();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Deals Summary', true, \"Retrieved deals summary in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Deals Summary', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetPersons(Configuration $config): void\n {\n $this->info('Test 2b: Get Persons');\n $startTime = microtime(true);\n\n try {\n $apiInstance = new PersonsApi(null, $config);\n // Official SDK uses getPersons method\n $result = $apiInstance->getPersons(['limit' => 10]);\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Persons', true, \"Retrieved persons in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Persons', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetActivities(Configuration $config): void\n {\n $this->info('Test 2c: Get Activities');\n $startTime = microtime(true);\n\n try {\n $apiInstance = new ActivitiesApi(null, $config);\n // Official SDK uses getActivities method\n $result = $apiInstance->getActivities(['limit' => 10]);\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Activities', true, \"Retrieved activities in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Activities', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testFieldMetadata(SocialAccount $socialAccount): void\n {\n $this->info('Test 3: Field Metadata');\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n\n // Test 3a: Get Deal Fields\n $this->testGetDealFields($config);\n\n // Test 3b: Get Person Fields\n $this->testGetPersonFields($config);\n\n // Test 3c: Get Activity Fields\n $this->testGetActivityFields($config);\n } catch (Exception $e) {\n $this->recordResult('Field Metadata', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testGetDealFields(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new DealFieldsApi(null, $config);\n $result = $apiInstance->getDealFields();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $count = $result->getData() ? count($result->getData()) : 0;\n $this->recordResult('Get Deal Fields', true, \"Retrieved {$count} deal fields in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Deal Fields', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetPersonFields(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new PersonFieldsApi(null, $config);\n $result = $apiInstance->getPersonFields();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $count = $result->getData() ? count($result->getData()) : 0;\n $this->recordResult('Get Person Fields', true, \"Retrieved {$count} person fields in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Person Fields', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetActivityFields(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new ActivityFieldsApi(null, $config);\n $result = $apiInstance->getActivityFields();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $count = $result->getData() ? count($result->getData()) : 0;\n $this->recordResult('Get Activity Fields', true, \"Retrieved {$count} activity fields in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Activity Fields', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testV2ApiSupport(SocialAccount $socialAccount): void\n {\n $this->info('Test 4: v2 API Support (Cursor-based Pagination)');\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n\n $startTime = microtime(true);\n\n // Test v2 deals endpoint with cursor pagination\n // Note: The official SDK may or may not have v2 API support\n // This test will determine if v2 is available\n try {\n $apiInstance = new DealsApi(null, $config);\n $updatedSince = Carbon::now()->subDays(30)->toRfc3339String();\n $result = $apiInstance->getDealsSummary();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n\n // Check if response has cursor information (v2 indicator)\n // The response object doesn't have getAdditionalData method\n // Just test if the call works - v2 API support is limited in official SDK v1\n $this->recordResult('v2 API Support', false, \"v1 API works but v2 cursor pagination not available in official SDK v1. Retrieved summary in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('v2 API Support', false, $e->getMessage(), $elapsed);\n }\n } catch (Exception $e) {\n $this->recordResult('v2 API Support', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testErrorHandling(SocialAccount $socialAccount): void\n {\n $this->info('Test 5: Error Handling');\n\n try {\n // Test 5a: Invalid Token\n $this->testInvalidToken();\n\n // Test 5b: Non-existent Resource\n $this->testNonExistentResource($socialAccount);\n } catch (Exception $e) {\n $this->recordResult('Error Handling', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testInvalidToken(): void\n {\n $this->info('Test 5a: Invalid Token');\n $startTime = microtime(true);\n\n try {\n $config = new Configuration();\n $config->setAccessToken('invalid_token');\n $client = new DealsApi(null, $config);\n\n $client->getDealsSummary();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Invalid Token Error', false, \"Expected error but request succeeded in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Invalid Token Error', true, \"Correctly threw exception: {$e->getMessage()} in {$elapsed}ms\", $elapsed);\n }\n }\n\n private function testNonExistentResource(SocialAccount $socialAccount): void\n {\n $this->info('Test 5b: Non-existent Resource');\n $startTime = microtime(true);\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n $client = new DealsApi(null, $config);\n\n // Try to get a deal with a very high ID that likely doesn't exist\n // Official SDK has getDealsSummary which works without specific ID\n // Instead, let's test with an invalid field ID\n try {\n $fieldClient = new DealFieldsApi(null, $config);\n $fieldClient->getDealField(999999999); // Very high ID that likely doesn't exist\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Non-existent Resource Error', false, \"Expected error but request succeeded in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Non-existent Resource Error', true, \"Correctly threw exception: {$e->getMessage()} in {$elapsed}ms\", $elapsed);\n }\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Non-existent Resource Error', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function recordResult(string $testName, bool $success, string $message, float $elapsed): void\n {\n $this->results[] = [\n 'test' => $testName,\n 'success' => $success,\n 'message' => $message,\n 'elapsed' => $elapsed,\n ];\n\n $status = $success ? '✅ PASS' : '❌ FAIL';\n $this->line(\" {$status} - {$testName}: {$message}\");\n }\n\n private function displayResults(): void\n {\n $this->newLine();\n $this->info('=== POC Test Results Summary ===');\n $this->newLine();\n\n $total = count($this->results);\n $passed = count(array_filter($this->results, fn ($r) => $r['success']));\n $failed = $total - $passed;\n\n $this->line(\"Total Tests: {$total}\");\n $this->line(\"Passed: {$passed}\");\n $this->line(\"Failed: {$failed}\");\n $this->newLine();\n\n $totalTime = array_sum(array_column($this->results, 'elapsed'));\n $this->line(\"Total Execution Time: {$totalTime}ms\");\n $this->newLine();\n\n if ($failed > 0) {\n $this->warn('Failed Tests:');\n foreach ($this->results as $result) {\n if (! $result['success']) {\n $this->line(\" - {$result['test']}: {$result['message']}\");\n }\n }\n $this->newLine();\n }\n\n $this->info('=== Detailed Results ===');\n foreach ($this->results as $result) {\n $status = $result['success'] ? '✅' : '❌';\n $this->line(\"{$status} {$result['test']} ({$result['elapsed']}ms): {$result['message']}\");\n }\n\n $this->newLine();\n $this->info('=== Recommendations ===');\n\n if ($passed === $total) {\n $this->info('✅ All tests passed! The official SDK is viable for migration.');\n $this->info('Next steps: Proceed with full migration planning.');\n } else {\n $this->warn('⚠️ Some tests failed. Review the failures above before proceeding.');\n $this->warn('Consider hybrid approach or investigate alternatives if critical operations fail.');\n }\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Console\\Commands\\Crm;\n\nuse Carbon\\Carbon;\nuse Exception;\nuse Illuminate\\Console\\Command;\nuse Jiminny\\Component\\Encryption\\EncryptedTokenManager;\nuse Jiminny\\Models\\SocialAccount;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Pipedrive\\versions\\v1\\Api\\ActivitiesApi;\nuse Pipedrive\\versions\\v1\\Api\\ActivityFieldsApi;\nuse Pipedrive\\versions\\v1\\Api\\DealFieldsApi;\nuse Pipedrive\\versions\\v1\\Api\\DealsApi;\nuse Pipedrive\\versions\\v1\\Api\\PersonFieldsApi;\nuse Pipedrive\\versions\\v1\\Api\\PersonsApi;\nuse Pipedrive\\versions\\v1\\Configuration;\n\nclass TestPipedriveOfficialSdkCommand extends Command\n{\n protected $signature = 'jiminny:test-pipedrive-official-sdk {teamId}';\n\n protected $description = 'Test official Pipedrive SDK (pipedrive/pipedrive) with existing OAuth tokens';\n\n private array $results = [];\n\n private EncryptedTokenManager $tokenManager;\n\n public function __construct(EncryptedTokenManager $tokenManager)\n {\n parent::__construct();\n $this->tokenManager = $tokenManager;\n }\n\n public function handle(): int\n {\n $teamId = (int) $this->argument('teamId');\n\n $this->info(\"Starting Pipedrive Official SDK POC for Team ID: {$teamId}\");\n $this->newLine();\n\n try {\n $this->runTests($teamId);\n } catch (Exception $e) {\n $this->error(\"POC failed with error: {$e->getMessage()}\");\n $this->error($e->getTraceAsString());\n\n return 1;\n }\n\n $this->displayResults();\n\n return 0;\n }\n\n private function runTests(int $teamId): void\n {\n $team = Team::find($teamId);\n if (! $team) {\n throw new Exception(\"Team with ID {$teamId} not found\");\n }\n\n $socialAccount = SocialAccount::where('provider', SocialAccount::PROVIDER_PIPEDRIVE)\n ->whereHas('sociable', function ($query) use ($team) {\n $query->where('team_id', $team->id);\n })\n ->orderByDesc('expires') // Get the most recently expiring token\n ->first();\n\n if (! $socialAccount) {\n throw new Exception(\"No Pipedrive social account found for team {$teamId}\");\n }\n\n $this->info(\"Found Pipedrive account for team: {$team->name}\");\n $this->newLine();\n\n // Test 1: OAuth Initialization\n $this->testOAuthInitialization($socialAccount);\n\n // Test 2: Basic CRUD Operations\n $this->testBasicCrudOperations($socialAccount);\n\n // Test 3: Field Metadata\n $this->testFieldMetadata($socialAccount);\n\n // Test 4: v2 API Support\n $this->testV2ApiSupport($socialAccount);\n\n // Test 5: Error Handling\n $this->testErrorHandling($socialAccount);\n }\n\n private function testOAuthInitialization(SocialAccount $socialAccount): void\n {\n $this->info('Test 1: OAuth Initialization');\n $startTime = microtime(true);\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n\n $client = new DealsApi(null, $config);\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('OAuth Initialization', true, \"Client initialized successfully in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('OAuth Initialization', false, $e->getMessage(), $elapsed);\n }\n\n $this->newLine();\n }\n\n private function testBasicCrudOperations(SocialAccount $socialAccount): void\n {\n $this->info('Test 2: Basic CRUD Operations');\n\n try {\n // Get the raw token from database\n $rawDbToken = $socialAccount->getProviderUserToken();\n $this->info(\"DB token (length: \" . strlen($rawDbToken) . \")\");\n $this->info(\"DB token preview: \" . substr($rawDbToken, 0, 20) . \"...\");\n\n // Check token expiry\n $expires = $socialAccount->expires;\n $this->info(\"Token expires at: \" . ($expires ? Carbon::createFromTimestamp($expires)->toDateTimeString() : 'null'));\n $this->info(\"Token is expired: \" . ($expires && $expires < time() ? 'YES' : 'NO'));\n\n // Test with the DB token directly\n $this->info(\"\\n--- Testing with DB token ---\");\n $config = new Configuration();\n $config->setAccessToken($rawDbToken);\n\n // Test 2a: Get Deals Summary\n $this->testGetDeals($config);\n\n // Test 2b: Get Persons\n $this->testGetPersons($config);\n\n // Test 2c: Get Activities\n $this->testGetActivities($config);\n } catch (Exception $e) {\n $this->recordResult('Basic CRUD Operations', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testCurrentSystemApproach(SocialAccount $socialAccount): void\n {\n $startTime = microtime(true);\n\n try {\n // Simulate how current system makes requests\n $token = $socialAccount->getProviderUserToken();\n $url = 'https://api.pipedrive.com/api/v2/deals';\n\n $headers = [\n 'Authorization' => 'Bearer ' . $token,\n 'Accept' => 'application/json',\n ];\n\n $client = new \\GuzzleHttp\\Client();\n $response = $client->get($url, [\n 'headers' => $headers,\n 'timeout' => 30,\n ]);\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $statusCode = $response->getStatusCode();\n $this->recordResult('Current System v2 API', $statusCode === 200, \"Status: {$statusCode} in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Current System v2 API', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetDeals(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new DealsApi(null, $config);\n // Official SDK uses getDealsSummary instead of getDeals\n $result = $apiInstance->getDealsSummary();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Deals Summary', true, \"Retrieved deals summary in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Deals Summary', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetPersons(Configuration $config): void\n {\n $this->info('Test 2b: Get Persons');\n $startTime = microtime(true);\n\n try {\n $apiInstance = new PersonsApi(null, $config);\n // Official SDK uses getPersons method\n $result = $apiInstance->getPersons(['limit' => 10]);\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Persons', true, \"Retrieved persons in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Persons', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetActivities(Configuration $config): void\n {\n $this->info('Test 2c: Get Activities');\n $startTime = microtime(true);\n\n try {\n $apiInstance = new ActivitiesApi(null, $config);\n // Official SDK uses getActivities method\n $result = $apiInstance->getActivities(['limit' => 10]);\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Activities', true, \"Retrieved activities in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Activities', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testFieldMetadata(SocialAccount $socialAccount): void\n {\n $this->info('Test 3: Field Metadata');\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n\n // Test 3a: Get Deal Fields\n $this->testGetDealFields($config);\n\n // Test 3b: Get Person Fields\n $this->testGetPersonFields($config);\n\n // Test 3c: Get Activity Fields\n $this->testGetActivityFields($config);\n } catch (Exception $e) {\n $this->recordResult('Field Metadata', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testGetDealFields(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new DealFieldsApi(null, $config);\n $result = $apiInstance->getDealFields();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $count = $result->getData() ? count($result->getData()) : 0;\n $this->recordResult('Get Deal Fields', true, \"Retrieved {$count} deal fields in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Deal Fields', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetPersonFields(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new PersonFieldsApi(null, $config);\n $result = $apiInstance->getPersonFields();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $count = $result->getData() ? count($result->getData()) : 0;\n $this->recordResult('Get Person Fields', true, \"Retrieved {$count} person fields in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Person Fields', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetActivityFields(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new ActivityFieldsApi(null, $config);\n $result = $apiInstance->getActivityFields();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $count = $result->getData() ? count($result->getData()) : 0;\n $this->recordResult('Get Activity Fields', true, \"Retrieved {$count} activity fields in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Activity Fields', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testV2ApiSupport(SocialAccount $socialAccount): void\n {\n $this->info('Test 4: v2 API Support (Cursor-based Pagination)');\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n\n $startTime = microtime(true);\n\n // Test v2 deals endpoint with cursor pagination\n // Note: The official SDK may or may not have v2 API support\n // This test will determine if v2 is available\n try {\n $apiInstance = new DealsApi(null, $config);\n $updatedSince = Carbon::now()->subDays(30)->toRfc3339String();\n $result = $apiInstance->getDealsSummary();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n\n // Check if response has cursor information (v2 indicator)\n // The response object doesn't have getAdditionalData method\n // Just test if the call works - v2 API support is limited in official SDK v1\n $this->recordResult('v2 API Support', false, \"v1 API works but v2 cursor pagination not available in official SDK v1. Retrieved summary in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('v2 API Support', false, $e->getMessage(), $elapsed);\n }\n } catch (Exception $e) {\n $this->recordResult('v2 API Support', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testErrorHandling(SocialAccount $socialAccount): void\n {\n $this->info('Test 5: Error Handling');\n\n try {\n // Test 5a: Invalid Token\n $this->testInvalidToken();\n\n // Test 5b: Non-existent Resource\n $this->testNonExistentResource($socialAccount);\n } catch (Exception $e) {\n $this->recordResult('Error Handling', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testInvalidToken(): void\n {\n $this->info('Test 5a: Invalid Token');\n $startTime = microtime(true);\n\n try {\n $config = new Configuration();\n $config->setAccessToken('invalid_token');\n $client = new DealsApi(null, $config);\n\n $client->getDealsSummary();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Invalid Token Error', false, \"Expected error but request succeeded in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Invalid Token Error', true, \"Correctly threw exception: {$e->getMessage()} in {$elapsed}ms\", $elapsed);\n }\n }\n\n private function testNonExistentResource(SocialAccount $socialAccount): void\n {\n $this->info('Test 5b: Non-existent Resource');\n $startTime = microtime(true);\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n $client = new DealsApi(null, $config);\n\n // Try to get a deal with a very high ID that likely doesn't exist\n // Official SDK has getDealsSummary which works without specific ID\n // Instead, let's test with an invalid field ID\n try {\n $fieldClient = new DealFieldsApi(null, $config);\n $fieldClient->getDealField(999999999); // Very high ID that likely doesn't exist\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Non-existent Resource Error', false, \"Expected error but request succeeded in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Non-existent Resource Error', true, \"Correctly threw exception: {$e->getMessage()} in {$elapsed}ms\", $elapsed);\n }\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Non-existent Resource Error', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function recordResult(string $testName, bool $success, string $message, float $elapsed): void\n {\n $this->results[] = [\n 'test' => $testName,\n 'success' => $success,\n 'message' => $message,\n 'elapsed' => $elapsed,\n ];\n\n $status = $success ? '✅ PASS' : '❌ FAIL';\n $this->line(\" {$status} - {$testName}: {$message}\");\n }\n\n private function displayResults(): void\n {\n $this->newLine();\n $this->info('=== POC Test Results Summary ===');\n $this->newLine();\n\n $total = count($this->results);\n $passed = count(array_filter($this->results, fn ($r) => $r['success']));\n $failed = $total - $passed;\n\n $this->line(\"Total Tests: {$total}\");\n $this->line(\"Passed: {$passed}\");\n $this->line(\"Failed: {$failed}\");\n $this->newLine();\n\n $totalTime = array_sum(array_column($this->results, 'elapsed'));\n $this->line(\"Total Execution Time: {$totalTime}ms\");\n $this->newLine();\n\n if ($failed > 0) {\n $this->warn('Failed Tests:');\n foreach ($this->results as $result) {\n if (! $result['success']) {\n $this->line(\" - {$result['test']}: {$result['message']}\");\n }\n }\n $this->newLine();\n }\n\n $this->info('=== Detailed Results ===');\n foreach ($this->results as $result) {\n $status = $result['success'] ? '✅' : '❌';\n $this->line(\"{$status} {$result['test']} ({$result['elapsed']}ms): {$result['message']}\");\n }\n\n $this->newLine();\n $this->info('=== Recommendations ===');\n\n if ($passed === $total) {\n $this->info('✅ All tests passed! The official SDK is viable for migration.');\n $this->info('Next steps: Proceed with full migration planning.');\n } else {\n $this->warn('⚠️ Some tests failed. Review the failures above before proceeding.');\n $this->warn('Consider hybrid approach or investigate alternatives if critical operations fail.');\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}]...
|
-7605369110515316950
|
25623949098764932
|
visual_change
|
accessibility
|
NULL
|
Project: faVsco.js, menu
pipedrive-sdk-poc, menu
S Project: faVsco.js, menu
pipedrive-sdk-poc, 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\Console\Commands\Crm;
use Jiminny\Console\Commands\Command;
use Jiminny\Console\Commands\Crm\Traits\SyncObjectsCommandTrait;
use Jiminny\Jobs\Crm\SyncObjects as SyncObjectsJob;
use Jiminny\Jobs\Job;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Models\Team;
class SyncObjects extends Command
{
use SyncObjectsCommandTrait;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'crm:sync-objects {team?} {--sync}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Sync remote CRM objects.';
/**
* Execute the console command.
*/
public function handle(): void
{
$teamId = $this->argument('team');
$teams = [];
if ($teamId) {
$team = Team::idOrUuId($teamId);
if ($team && $team->crm?->provider === Configuration::PROVIDER_HUBSPOT) {
$this->error(sprintf(
'Team %s (%s) uses HubSpot. Use crm:sync-hubspot-objects instead.',
$team->getName(),
$team->getUuid()
));
return;
}
if ($team) {
$teams[] = $team;
}
} else {
// Exclude HubSpot teams - handled by crm:sync-hubspot-objects
$teams = Team::where('status', Team::STATUS_ACTIVE)
->whereHas(
'crm',
fn ($q) => $q
->where('provider', '!=', Configuration::PROVIDER_HUBSPOT)
->where('sync_objects', true)
)
->get();
}
$this->dispatchSyncJobsForTeams($teams);
}
protected function getStaggerDelaySeconds(): float
{
return 2;
}
protected function getMaxDelaySeconds(): int
{
// Cap delay at 15 minutes (SQS max delay limit)
// capacity of 450 teams before reach
return 900;
}
protected function getLogPrefix(): string
{
return '';
}
protected function createSyncJob(Team $team): Job
{
return new SyncObjectsJob($team);
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
1
51
1
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Console\Commands\Crm;
use Carbon\Carbon;
use Exception;
use Illuminate\Console\Command;
use Jiminny\Component\Encryption\EncryptedTokenManager;
use Jiminny\Models\SocialAccount;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Pipedrive\versions\v1\Api\ActivitiesApi;
use Pipedrive\versions\v1\Api\ActivityFieldsApi;
use Pipedrive\versions\v1\Api\DealFieldsApi;
use Pipedrive\versions\v1\Api\DealsApi;
use Pipedrive\versions\v1\Api\PersonFieldsApi;
use Pipedrive\versions\v1\Api\PersonsApi;
use Pipedrive\versions\v1\Configuration;
class TestPipedriveOfficialSdkCommand extends Command
{
protected $signature = 'jiminny:test-pipedrive-official-sdk {teamId}';
protected $description = 'Test official Pipedrive SDK (pipedrive/pipedrive) with existing OAuth tokens';
private array $results = [];
private EncryptedTokenManager $tokenManager;
public function __construct(EncryptedTokenManager $tokenManager)
{
parent::__construct();
$this->tokenManager = $tokenManager;
}
public function handle(): int
{
$teamId = (int) $this->argument('teamId');
$this->info("Starting Pipedrive Official SDK POC for Team ID: {$teamId}");
$this->newLine();
try {
$this->runTests($teamId);
} catch (Exception $e) {
$this->error("POC failed with error: {$e->getMessage()}");
$this->error($e->getTraceAsString());
return 1;
}
$this->displayResults();
return 0;
}
private function runTests(int $teamId): void
{
$team = Team::find($teamId);
if (! $team) {
throw new Exception("Team with ID {$teamId} not found");
}
$socialAccount = SocialAccount::where('provider', SocialAccount::PROVIDER_PIPEDRIVE)
->whereHas('sociable', function ($query) use ($team) {
$query->where('team_id', $team->id);
})
->orderByDesc('expires') // Get the most recently expiring token
->first();
if (! $socialAccount) {
throw new Exception("No Pipedrive social account found for team {$teamId}");
}
$this->info("Found Pipedrive account for team: {$team->name}");
$this->newLine();
// Test 1: OAuth Initialization
$this->testOAuthInitialization($socialAccount);
// Test 2: Basic CRUD Operations
$this->testBasicCrudOperations($socialAccount);
// Test 3: Field Metadata
$this->testFieldMetadata($socialAccount);
// Test 4: v2 API Support
$this->testV2ApiSupport($socialAccount);
// Test 5: Error Handling
$this->testErrorHandling($socialAccount);
}
private function testOAuthInitialization(SocialAccount $socialAccount): void
{
$this->info('Test 1: OAuth Initialization');
$startTime = microtime(true);
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
$client = new DealsApi(null, $config);
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('OAuth Initialization', true, "Client initialized successfully in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('OAuth Initialization', false, $e->getMessage(), $elapsed);
}
$this->newLine();
}
private function testBasicCrudOperations(SocialAccount $socialAccount): void
{
$this->info('Test 2: Basic CRUD Operations');
try {
// Get the raw token from database
$rawDbToken = $socialAccount->getProviderUserToken();
$this->info("DB token (length: " . strlen($rawDbToken) . ")");
$this->info("DB token preview: " . substr($rawDbToken, 0, 20) . "...");
// Check token expiry
$expires = $socialAccount->expires;
$this->info("Token expires at: " . ($expires ? Carbon::createFromTimestamp($expires)->toDateTimeString() : 'null'));
$this->info("Token is expired: " . ($expires && $expires < time() ? 'YES' : 'NO'));
// Test with the DB token directly
$this->info("\n--- Testing with DB token ---");
$config = new Configuration();
$config->setAccessToken($rawDbToken);
// Test 2a: Get Deals Summary
$this->testGetDeals($config);
// Test 2b: Get Persons
$this->testGetPersons($config);
// Test 2c: Get Activities
$this->testGetActivities($config);
} catch (Exception $e) {
$this->recordResult('Basic CRUD Operations', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testCurrentSystemApproach(SocialAccount $socialAccount): void
{
$startTime = microtime(true);
try {
// Simulate how current system makes requests
$token = $socialAccount->getProviderUserToken();
$url = 'https://api.pipedrive.com/api/v2/deals';
$headers = [
'Authorization' => 'Bearer ' . $token,
'Accept' => 'application/json',
];
$client = new \GuzzleHttp\Client();
$response = $client->get($url, [
'headers' => $headers,
'timeout' => 30,
]);
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$statusCode = $response->getStatusCode();
$this->recordResult('Current System v2 API', $statusCode === 200, "Status: {$statusCode} in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Current System v2 API', false, $e->getMessage(), $elapsed);
}
}
private function testGetDeals(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new DealsApi(null, $config);
// Official SDK uses getDealsSummary instead of getDeals
$result = $apiInstance->getDealsSummary();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Deals Summary', true, "Retrieved deals summary in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Deals Summary', false, $e->getMessage(), $elapsed);
}
}
private function testGetPersons(Configuration $config): void
{
$this->info('Test 2b: Get Persons');
$startTime = microtime(true);
try {
$apiInstance = new PersonsApi(null, $config);
// Official SDK uses getPersons method
$result = $apiInstance->getPersons(['limit' => 10]);
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Persons', true, "Retrieved persons in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Persons', false, $e->getMessage(), $elapsed);
}
}
private function testGetActivities(Configuration $config): void
{
$this->info('Test 2c: Get Activities');
$startTime = microtime(true);
try {
$apiInstance = new ActivitiesApi(null, $config);
// Official SDK uses getActivities method
$result = $apiInstance->getActivities(['limit' => 10]);
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Activities', true, "Retrieved activities in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Activities', false, $e->getMessage(), $elapsed);
}
}
private function testFieldMetadata(SocialAccount $socialAccount): void
{
$this->info('Test 3: Field Metadata');
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
// Test 3a: Get Deal Fields
$this->testGetDealFields($config);
// Test 3b: Get Person Fields
$this->testGetPersonFields($config);
// Test 3c: Get Activity Fields
$this->testGetActivityFields($config);
} catch (Exception $e) {
$this->recordResult('Field Metadata', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testGetDealFields(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new DealFieldsApi(null, $config);
$result = $apiInstance->getDealFields();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$count = $result->getData() ? count($result->getData()) : 0;
$this->recordResult('Get Deal Fields', true, "Retrieved {$count} deal fields in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Deal Fields', false, $e->getMessage(), $elapsed);
}
}
private function testGetPersonFields(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new PersonFieldsApi(null, $config);
$result = $apiInstance->getPersonFields();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$count = $result->getData() ? count($result->getData()) : 0;
$this->recordResult('Get Person Fields', true, "Retrieved {$count} person fields in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Person Fields', false, $e->getMessage(), $elapsed);
}
}
private function testGetActivityFields(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new ActivityFieldsApi(null, $config);
$result = $apiInstance->getActivityFields();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$count = $result->getData() ? count($result->getData()) : 0;
$this->recordResult('Get Activity Fields', true, "Retrieved {$count} activity fields in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Activity Fields', false, $e->getMessage(), $elapsed);
}
}
private function testV2ApiSupport(SocialAccount $socialAccount): void
{
$this->info('Test 4: v2 API Support (Cursor-based Pagination)');
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
$startTime = microtime(true);
// Test v2 deals endpoint with cursor pagination
// Note: The official SDK may or may not have v2 API support
// This test will determine if v2 is available
try {
$apiInstance = new DealsApi(null, $config);
$updatedSince = Carbon::now()->subDays(30)->toRfc3339String();
$result = $apiInstance->getDealsSummary();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
// Check if response has cursor information (v2 indicator)
// The response object doesn't have getAdditionalData method
// Just test if the call works - v2 API support is limited in official SDK v1
$this->recordResult('v2 API Support', false, "v1 API works but v2 cursor pagination not available in official SDK v1. Retrieved summary in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('v2 API Support', false, $e->getMessage(), $elapsed);
}
} catch (Exception $e) {
$this->recordResult('v2 API Support', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testErrorHandling(SocialAccount $socialAccount): void
{
$this->info('Test 5: Error Handling');
try {
// Test 5a: Invalid Token
$this->testInvalidToken();
// Test 5b: Non-existent Resource
$this->testNonExistentResource($socialAccount);
} catch (Exception $e) {
$this->recordResult('Error Handling', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testInvalidToken(): void
{
$this->info('Test 5a: Invalid Token');
$startTime = microtime(true);
try {
$config = new Configuration();
$config->setAccessToken('invalid_token');
$client = new DealsApi(null, $config);
$client->getDealsSummary();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Invalid Token Error', false, "Expected error but request succeeded in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Invalid Token Error', true, "Correctly threw exception: {$e->getMessage()} in {$elapsed}ms", $elapsed);
}
}
private function testNonExistentResource(SocialAccount $socialAccount): void
{
$this->info('Test 5b: Non-existent Resource');
$startTime = microtime(true);
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
$client = new DealsApi(null, $config);
// Try to get a deal with a very high ID that likely doesn't exist
// Official SDK has getDealsSummary which works without specific ID
// Instead, let's test with an invalid field ID
try {
$fieldClient = new DealFieldsApi(null, $config);
$fieldClient->getDealField(999999999); // Very high ID that likely doesn't exist
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Non-existent Resource Error', false, "Expected error but request succeeded in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Non-existent Resource Error', true, "Correctly threw exception: {$e->getMessage()} in {$elapsed}ms", $elapsed);
}
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Non-existent Resource Error', false, $e->getMessage(), $elapsed);
}
}
private function recordResult(string $testName, bool $success, string $message, float $elapsed): void
{
$this->results[] = [
'test' => $testName,
'success' => $success,
'message' => $message,
'elapsed' => $elapsed,
];
$status = $success ? '✅ PASS' : '❌ FAIL';
$this->line(" {$status} - {$testName}: {$message}");
}
private function displayResults(): void
{
$this->newLine();
$this->info('=== POC Test Results Summary ===');
$this->newLine();
$total = count($this->results);
$passed = count(array_filter($this->results, fn ($r) => $r['success']));
$failed = $total - $passed;
$this->line("Total Tests: {$total}");
$this->line("Passed: {$passed}");
$this->line("Failed: {$failed}");
$this->newLine();
$totalTime = array_sum(array_column($this->results, 'elapsed'));
$this->line("Total Execution Time: {$totalTime}ms");
$this->newLine();
if ($failed > 0) {
$this->warn('Failed Tests:');
foreach ($this->results as $result) {
if (! $result['success']) {
$this->line(" - {$result['test']}: {$result['message']}");
}
}
$this->newLine();
}
$this->info('=== Detailed Results ===');
foreach ($this->results as $result) {
$status = $result['success'] ? '✅' : '❌';
$this->line("{$status} {$result['test']} ({$result['elapsed']}ms): {$result['message']}");
}
$this->newLine();
$this->info('=== Recommendations ===');
if ($passed === $total) {
$this->info('✅ All tests passed! The official SDK is viable for migration.');
$this->info('Next steps: Proceed with full migration planning.');
} else {
$this->warn('⚠️ Some tests failed. Review the failures above before proceeding.');
$this->warn('Consider hybrid approach or investigate alternatives if critical operations fail.');
}
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
49419
|
1765
|
4
|
2026-05-18T06:43:37.511121+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779086617511_m2.jpg...
|
PhpStorm
|
faVsco.js – TestPipedriveOfficialSdkCommand.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
PhostormVIewINavicarecodeLaravelKeractorFV faVsco. PhostormVIewINavicarecodeLaravelKeractorFV faVsco.js?° pipedrive-sdk-pocProiectC ActivityController.ong© SyncProfileMetadata.phpCConvertLeadActivities.ong© SyncPlanhat.php©syncleammetadata.ong© TestPipedriveOfficialSdkCommand.phUpoateopponunityspecrications.onocreatePlaybookcreatedevent.ong>@ Dealinsights>@Dev> 0 Dialersc) IntearationApp/Service.php© LeadConverted.php© CreateSelfCoachedEvent.phpD DTOSC ElasticsearchC) CreateCommentedEvent.phoC) CreateSmsSentEvent.ohpC) PlanhatActivityListener.pho(C)AskAnvthinaPromotService.ohd› EnqagementStatsш GeckoExport(C)AutomatedReportsCommand.ohvphp api y2.ohoC) RequestGenerateReport.Job.oho/>D Livestream(C) AutomatedReportkesulconp© AutomatedReport.phpA console [STAGING]Mallboxes• MidratePlavbackithemes• M Plavbooksdectarelstrict_types-tyD Playlists> M Postmarknanespace Jininny console comnanos crm> M PronhetAuse…..v M Renorto© AutomatedReportsCommand.phpC) AutomatedRenortsRetentionPolicvCor14 Dclass syncubnects extends command© AutomatedReportsSendCommand.pht© CreateMockAskJiminnyReportResultC© DeleteReportCommand.php© GenerateMarketingReport.php© Team.php© Usage.php>C Slack* The name and sianature of the console commandi* ovar strind›_ Teams>C TracksC Transcription>C TwilioC Users> M Vocabularv7301nrotected ssionature = crm:sunc-ohnects -team?, ~--svnctr** The console command descrintion.>Zoom*Avan strinaC) Command.oho© CreateDatabaseUsers.ohpprotected $description = 'Sync remote CRM objects.';C)DatabaseTiableCount.oho© DeleteOIdAiCrmNotesCommand.ohv* Eyecuto the concole commandlCDeleteS?Leftoverscommand.ohn@ DevPostmanCommand.phppublic function handle: voidf...;C) DiarizeViaAiParticinantldentificationCom@ EncrvntTokensCommand.nhn(C EnaadementStatsRedenerateCommand.r( FeatureFlaasHelner nhr69 6t)protected function getStaggerDelavSeconds@: floatf...?© FixCrossTenantlssues.php© FlushRolesPermissionsCache.php© GeneratelnternalWebhookToken.php9 GrounSetDefaultLanguageCommand.php74 6tprotected function getMaxDelavSeconds@: int{...}81 6t>protected function getLoqPrefix@: stringf...;l© HelperTruncateCoachingTables.phpHelner Code will hoin INF to underctand vour Laravel ann code II Generate II Don't Show Anvmore (8 minutes aaol=custom.loglaravel.logA SF [jiminny@localhost]« HS_local [jiminny@localhost]A console [PROD]© TestPipedriveOfficialSdkCommand.php x © Kernel.phpA console [EU]class TestPipedrive0fficialSdkCommand extends Command private tunction testbasiccruduperations (soclaLAccount ssoclaLAccount vola-rawubloken = ssoclaLaccount->cecrrovlderuserlokenorSthis->info( string: "DB token (Length:"")"Sthis->info( string: "DB token preview: ". substr(SrawDbToken. offset: 0,lengthcheck token expirySexpires = SsocialAccount->expires:sthas->inrol string:"oken exoires at.Sexoires? carbon::createrromiimesSthis->info strina:"Token is expired:(Sexnires ss Sexpires ‹ timeo) ? 'v// Test with the DB token directlySthis->infod strinaTesting with DB token ---").Sconfio = new Confiquration0:Scontia->se+AcceccToken(SrawlhToken):/ Test 2a: Get DealsReject// Test 2a: Get Deals Summarysunls->restberuealsscontlo// Test 2b: Get PersonsSthis->testGetPersons(Sconfiq)II Test 2c:sthis->testbetacrivitlesscontz0)} catch (Exception Se) {sthis->recordResult testName:'Basic CRUD Operations".success: false. Se->aetno usadedprivate function testCurrentSystemApproach(SocialAccount SsocialAccount): voidf..?private function testGetDeals(Configuration Sconfig): void{...}private function testGetPersons(Configuration Sconfig): void{...}private function testGetActivities(Configuration Sconfiq): void{...}1 usageprivate function testFieldMetadata(SocialAccount SsocialAccount): void{...}privatq functigns tgst' Accept File &- Cogfiauaatiion, Sconfig) or voigi → . }100% Lz• Mon 18 May 9:43:37AskJiminnyReportActivityServiceTest v+0 ..continue and test all in the command.wDebugging PipedriveThought for 1s0 4 tasks done1 Update Poc to test ofticial SDK methods properly• Test v2 API support if available3 Test error handlina properiv• TestPipedriveOfficialSdkCommand.phpThouahtserror while ediung lestripedriveurticlalsakcommano.onp ›Thoughts ›Read TestPipedriveOfficialSdkCommand.pho #L175-224lhoughts@ TestPipedriveOfficialSdkCommand.phpThoughts >•TestPipedriveOfficialSdkCommand.phpDoad ToctDinodrivoOfficia|CdkCommand nhn #ll 220.270Thouahts>Read [EMAIL] #L3/9-408lallteinie•TestPipedriveOfficialSdkCommand.phpTestPipedriveOfficialSdkCommand.php+6 -6757 tokens+5 -5+16 -6681 tokens* Reiect allAccent alliAsk anvthina (&4-L<> CodeSWF.16WN Windsurf Toams 127-1UTF.8Po 4 spaces...
|
NULL
|
-356708856042595059
|
NULL
|
click
|
ocr
|
NULL
|
PhostormVIewINavicarecodeLaravelKeractorFV faVsco. PhostormVIewINavicarecodeLaravelKeractorFV faVsco.js?° pipedrive-sdk-pocProiectC ActivityController.ong© SyncProfileMetadata.phpCConvertLeadActivities.ong© SyncPlanhat.php©syncleammetadata.ong© TestPipedriveOfficialSdkCommand.phUpoateopponunityspecrications.onocreatePlaybookcreatedevent.ong>@ Dealinsights>@Dev> 0 Dialersc) IntearationApp/Service.php© LeadConverted.php© CreateSelfCoachedEvent.phpD DTOSC ElasticsearchC) CreateCommentedEvent.phoC) CreateSmsSentEvent.ohpC) PlanhatActivityListener.pho(C)AskAnvthinaPromotService.ohd› EnqagementStatsш GeckoExport(C)AutomatedReportsCommand.ohvphp api y2.ohoC) RequestGenerateReport.Job.oho/>D Livestream(C) AutomatedReportkesulconp© AutomatedReport.phpA console [STAGING]Mallboxes• MidratePlavbackithemes• M Plavbooksdectarelstrict_types-tyD Playlists> M Postmarknanespace Jininny console comnanos crm> M PronhetAuse…..v M Renorto© AutomatedReportsCommand.phpC) AutomatedRenortsRetentionPolicvCor14 Dclass syncubnects extends command© AutomatedReportsSendCommand.pht© CreateMockAskJiminnyReportResultC© DeleteReportCommand.php© GenerateMarketingReport.php© Team.php© Usage.php>C Slack* The name and sianature of the console commandi* ovar strind›_ Teams>C TracksC Transcription>C TwilioC Users> M Vocabularv7301nrotected ssionature = crm:sunc-ohnects -team?, ~--svnctr** The console command descrintion.>Zoom*Avan strinaC) Command.oho© CreateDatabaseUsers.ohpprotected $description = 'Sync remote CRM objects.';C)DatabaseTiableCount.oho© DeleteOIdAiCrmNotesCommand.ohv* Eyecuto the concole commandlCDeleteS?Leftoverscommand.ohn@ DevPostmanCommand.phppublic function handle: voidf...;C) DiarizeViaAiParticinantldentificationCom@ EncrvntTokensCommand.nhn(C EnaadementStatsRedenerateCommand.r( FeatureFlaasHelner nhr69 6t)protected function getStaggerDelavSeconds@: floatf...?© FixCrossTenantlssues.php© FlushRolesPermissionsCache.php© GeneratelnternalWebhookToken.php9 GrounSetDefaultLanguageCommand.php74 6tprotected function getMaxDelavSeconds@: int{...}81 6t>protected function getLoqPrefix@: stringf...;l© HelperTruncateCoachingTables.phpHelner Code will hoin INF to underctand vour Laravel ann code II Generate II Don't Show Anvmore (8 minutes aaol=custom.loglaravel.logA SF [jiminny@localhost]« HS_local [jiminny@localhost]A console [PROD]© TestPipedriveOfficialSdkCommand.php x © Kernel.phpA console [EU]class TestPipedrive0fficialSdkCommand extends Command private tunction testbasiccruduperations (soclaLAccount ssoclaLAccount vola-rawubloken = ssoclaLaccount->cecrrovlderuserlokenorSthis->info( string: "DB token (Length:"")"Sthis->info( string: "DB token preview: ". substr(SrawDbToken. offset: 0,lengthcheck token expirySexpires = SsocialAccount->expires:sthas->inrol string:"oken exoires at.Sexoires? carbon::createrromiimesSthis->info strina:"Token is expired:(Sexnires ss Sexpires ‹ timeo) ? 'v// Test with the DB token directlySthis->infod strinaTesting with DB token ---").Sconfio = new Confiquration0:Scontia->se+AcceccToken(SrawlhToken):/ Test 2a: Get DealsReject// Test 2a: Get Deals Summarysunls->restberuealsscontlo// Test 2b: Get PersonsSthis->testGetPersons(Sconfiq)II Test 2c:sthis->testbetacrivitlesscontz0)} catch (Exception Se) {sthis->recordResult testName:'Basic CRUD Operations".success: false. Se->aetno usadedprivate function testCurrentSystemApproach(SocialAccount SsocialAccount): voidf..?private function testGetDeals(Configuration Sconfig): void{...}private function testGetPersons(Configuration Sconfig): void{...}private function testGetActivities(Configuration Sconfiq): void{...}1 usageprivate function testFieldMetadata(SocialAccount SsocialAccount): void{...}privatq functigns tgst' Accept File &- Cogfiauaatiion, Sconfig) or voigi → . }100% Lz• Mon 18 May 9:43:37AskJiminnyReportActivityServiceTest v+0 ..continue and test all in the command.wDebugging PipedriveThought for 1s0 4 tasks done1 Update Poc to test ofticial SDK methods properly• Test v2 API support if available3 Test error handlina properiv• TestPipedriveOfficialSdkCommand.phpThouahtserror while ediung lestripedriveurticlalsakcommano.onp ›Thoughts ›Read TestPipedriveOfficialSdkCommand.pho #L175-224lhoughts@ TestPipedriveOfficialSdkCommand.phpThoughts >•TestPipedriveOfficialSdkCommand.phpDoad ToctDinodrivoOfficia|CdkCommand nhn #ll 220.270Thouahts>Read [EMAIL] #L3/9-408lallteinie•TestPipedriveOfficialSdkCommand.phpTestPipedriveOfficialSdkCommand.php+6 -6757 tokens+5 -5+16 -6681 tokens* Reiect allAccent alliAsk anvthina (&4-L<> CodeSWF.16WN Windsurf Toams 127-1UTF.8Po 4 spaces...
|
49416
|
NULL
|
NULL
|
NULL
|
|
49418
|
1764
|
4
|
2026-05-18T06:43:37.511115+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779086617511_m1.jpg...
|
PhpStorm
|
faVsco.js – TestPipedriveOfficialSdkCommand.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
iTerm2Shell|EditViewSessionScriptsProfilesWindowHe iTerm2Shell|EditViewSessionScriptsProfilesWindowHelp• 0APP (-zsh)DOCKERLast login: Mon May 18 09:17:28 on ttys007₴81DEV (-zsh)O ₴2Poetry could not find a pyproject.toml file in /Users/lukas/jiminny/app or its parentsPoetry could not find a pyproject.toml file in /Users/lukas/jiminny/app or its parentslukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/jiminny/app (pipedrive-sdk-poc) $ U‹ $0APP (-zsh)83100% C47 8• Mon 18 May 9:43:37T₴1screenpipe*O ₴4APP...
|
NULL
|
-8012899098312889318
|
NULL
|
click
|
ocr
|
NULL
|
iTerm2Shell|EditViewSessionScriptsProfilesWindowHe iTerm2Shell|EditViewSessionScriptsProfilesWindowHelp• 0APP (-zsh)DOCKERLast login: Mon May 18 09:17:28 on ttys007₴81DEV (-zsh)O ₴2Poetry could not find a pyproject.toml file in /Users/lukas/jiminny/app or its parentsPoetry could not find a pyproject.toml file in /Users/lukas/jiminny/app or its parentslukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/jiminny/app (pipedrive-sdk-poc) $ U‹ $0APP (-zsh)83100% C47 8• Mon 18 May 9:43:37T₴1screenpipe*O ₴4APP...
|
49417
|
NULL
|
NULL
|
NULL
|
|
49417
|
1764
|
3
|
2026-05-18T06:43:33.735238+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779086613735_m1.jpg...
|
PhpStorm
|
faVsco.js – TestPipedriveOfficialSdkCommand.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
iTerm2ShelllEditViewSessionScriptsProfilesWindowHe iTerm2ShelllEditViewSessionScriptsProfilesWindowHelp•• 0APP (-zsh)DOCKERLast login: Mon May 18 09:17:28 on ttys007O 81DEV (-zsh)O 82Poetry could not find a pyproject.toml file in /Users/lukas/jiminny/app or its parentsPoetry could not find a pyproject.toml file in /Users/lukas/jiminny/app or its parentslukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/jiminny/app (pipedrive-sdk-poc) $ U‹ $0APP (-zsh)|83100% C47 8• Mon 18 May 9:43:33T₴1screenpipe*O &4APP...
|
NULL
|
4730714162020166078
|
NULL
|
visual_change
|
ocr
|
NULL
|
iTerm2ShelllEditViewSessionScriptsProfilesWindowHe iTerm2ShelllEditViewSessionScriptsProfilesWindowHelp•• 0APP (-zsh)DOCKERLast login: Mon May 18 09:17:28 on ttys007O 81DEV (-zsh)O 82Poetry could not find a pyproject.toml file in /Users/lukas/jiminny/app or its parentsPoetry could not find a pyproject.toml file in /Users/lukas/jiminny/app or its parentslukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/jiminny/app (pipedrive-sdk-poc) $ U‹ $0APP (-zsh)|83100% C47 8• Mon 18 May 9:43:33T₴1screenpipe*O &4APP...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
49416
|
1765
|
3
|
2026-05-18T06:43:32.075606+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779086612075_m2.jpg...
|
PhpStorm
|
faVsco.js – TestPipedriveOfficialSdkCommand.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
|
[{"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}]...
|
8043719072324535154
|
-8628527368849355612
|
visual_change
|
hybrid
|
NULL
|
Project: faVsco.js, menu
Firefox•.•Platform Sprint Project: faVsco.js, menu
Firefox•.•Platform Sprint 4 Q2 - PlatfornVIewMistorbookmarksProtllesToolsWindowmelpny.atlassian.net/jira/software/c/projects/JY/boards/37100% C4 &• Mon 18 May 9:43:31()JY-20891 add support for seconda(UY-20891] Sidekick SMS issue - JJY-20915) Add environment-spedUsage | Windsurf+ (SRD-6853] Moxso - Potential deaPipelines - jiminny/appl Feed - jiminny - SentryfJY-209061 Review of Pipedrive SP Pipedrive API Reference and Docur•pipedrive/client-php: Pipedrive APfiiminny/infrastructurel.JY-20623Pull requests - jiminny/app(UY-20912) Fallback mechanism foS 1IY-209061 Review of Pinedrive SPersonal Access Tokens (Classic)+ New Tab00O JIMINNY@ For you© Recent|# Starred•$ Apps0, Spaces+...Recent( Service-Desk@ Jiminny (New) + ...10D Platform TeamII Capture TeamW Enterprise Stability I...Processing TeamWD SE Kanban= More spaces= FiltersC DashboardsC OperationsQ Search+ Create5 Ask RovoA& Confluence: Teams"= Customise sidebar|Spaces / Jiminny (New)Platform Team P+Summary& TimelineE BacklogII Active sprints8 Calendar Reports Testing Board List Z Forms E Components % Development % Code O Security & Releases A Deployments E Archived workitems E Docs @ Shortcuts ~Slack integration& Reporting CenterQ Search board00000EpicvTypev Quick filtersComplete sprintGroup: QueriesREADY FOR DEV 4IN DEV 3CODE REVIEW1BLOCKEDNotitv the user it a Panorama promots is.deleted but is used in AJ ReportAJ REPORTSBacklog…JY-206763..=€MCP › Enable users to get a list of callsand their detailsJIMINNY MCP CONNECTORIn Dev[JY-20833MCP > Enable the AI to know detailsabout the userJIMINNY MCP CONNECTORCode Review9..=0[ JY-208461.5 82 000 =0OA 3[HubSpot] Optimise CRM rematching ondelete hubspot accounts/contactsPLATFORM STABILITYIn QA# JY-207251.5 82 .000 = 8PO ACCEPTANCEDEPLOY 7Notify the user if a SS is deleted but isused in AJ ReportAJ REPORTSBacklog[ JY-206152.5 0000 = 0MCP > Enable users to get a list of dealsand their detailsJIMINNY MCP CONNECTORIn DevQ JY-2083520 /2 000 = 0Upgrade to PHP 8.5PHP 8.5 UPGRADEReady for QA1.5 82 •00 = 0Upgrade BE libraries - MayMAINTENANCEBacklog©Y-199581 .000=@Allow owner's role to be selected whensettina uo a trialiEIMPROVEMENT OF OUR EFFICIENCYIn Dev# JY-20613Upgrade Python and libraries - MayMAINTENANCEIn QAE JY-20881182 •0=11.5 = QImprove Activity Type suggestions (AUTO-DETECTED ACTIVITY TYPEBackloaI… JY-2041012 0000 =₴Au Panorama for Call Scoring in ODAUTOMATED AI SCORINGVeolovedI… JY-2036110.5 •0÷[Deadline 25 May] Migrate depricatedGemini s.l rlash Lue Preview modellMAINTENANCEDeployed[ JY-208801 Ý) •00 =|Setup test coverage for Prophet in SonarMAINTENANCEDeployedV JY-1995110•=2[Deadline 17 Junel Miarate depricatedGemini modelsMAINTENANCEDeployedE JY-202720...=1Sidekick SMS issueDeployedJY-2089172 00=0Update activity stage on opportunityuodateDeployed+* JY-20903, 0=@UndateActivitvSlasticSearchDocumentCdmmand...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
49414
|
1764
|
1
|
2026-05-18T06:43:10.483083+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779086590483_m1.jpg...
|
PhpStorm
|
faVsco.js – TestPipedriveOfficialSdkCommand.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
|
[{"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}]...
|
8043719072324535154
|
-8628527368849355612
|
idle
|
hybrid
|
NULL
|
Project: faVsco.js, menu
iTerm2Shell|EditViewSessi Project: faVsco.js, menu
iTerm2Shell|EditViewSessionScriptsProfilesWindowHelp• 0APP (-zsh)DOCKERLast login: Mon May 18 09:17:28 on ttys007₴81DEV (-zsh)O ₴2Poetry could not find a pyproject.toml file in /Users/lukas/jiminny/app or its parentsPoetry could not find a pyproject.toml file in /Users/lukas/jiminny/app or its parentslukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/jiminny/app (pipedrive-sdk-poc) $ U‹ $0APP (-zsh)83100% C47 8• Mon 18 May 9:43:10T₴1screenpipe*O ₴4APP...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
49413
|
1765
|
2
|
2026-05-18T06:43:04.842600+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779086584842_m2.jpg...
|
PhpStorm
|
faVsco.js – TestPipedriveOfficialSdkCommand.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
pipedrive-sdk-poc, menu
S Project: faVsco.js, menu
pipedrive-sdk-poc, 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\Console\Commands\Crm;
use Jiminny\Console\Commands\Command;
use Jiminny\Console\Commands\Crm\Traits\SyncObjectsCommandTrait;
use Jiminny\Jobs\Crm\SyncObjects as SyncObjectsJob;
use Jiminny\Jobs\Job;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Models\Team;
class SyncObjects extends Command
{
use SyncObjectsCommandTrait;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'crm:sync-objects {team?} {--sync}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Sync remote CRM objects.';
/**
* Execute the console command.
*/
public function handle(): void
{
$teamId = $this->argument('team');
$teams = [];
if ($teamId) {
$team = Team::idOrUuId($teamId);
if ($team && $team->crm?->provider === Configuration::PROVIDER_HUBSPOT) {
$this->error(sprintf(
'Team %s (%s) uses HubSpot. Use crm:sync-hubspot-objects instead.',
$team->getName(),
$team->getUuid()
));
return;
}
if ($team) {
$teams[] = $team;
}
} else {
// Exclude HubSpot teams - handled by crm:sync-hubspot-objects
$teams = Team::where('status', Team::STATUS_ACTIVE)
->whereHas(
'crm',
fn ($q) => $q
->where('provider', '!=', Configuration::PROVIDER_HUBSPOT)
->where('sync_objects', true)
)
->get();
}
$this->dispatchSyncJobsForTeams($teams);
}
protected function getStaggerDelaySeconds(): float
{
return 2;
}
protected function getMaxDelaySeconds(): int
{
// Cap delay at 15 minutes (SQS max delay limit)
// capacity of 450 teams before reach
return 900;
}
protected function getLogPrefix(): string
{
return '';
}
protected function createSyncJob(Team $team): Job
{
return new SyncObjectsJob($team);
}
}...
|
[{"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":"pipedrive-sdk-poc, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.05618351,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: pipedrive-sdk-poc","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\\Console\\Commands\\Crm;\n\nuse Jiminny\\Console\\Commands\\Command;\nuse Jiminny\\Console\\Commands\\Crm\\Traits\\SyncObjectsCommandTrait;\nuse Jiminny\\Jobs\\Crm\\SyncObjects as SyncObjectsJob;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Models\\Team;\n\nclass SyncObjects extends Command\n{\n use SyncObjectsCommandTrait;\n\n /**\n * The name and signature of the console command.\n *\n * @var string\n */\n protected $signature = 'crm:sync-objects {team?} {--sync}';\n\n /**\n * The console command description.\n *\n * @var string\n */\n protected $description = 'Sync remote CRM objects.';\n\n /**\n * Execute the console command.\n */\n public function handle(): void\n {\n $teamId = $this->argument('team');\n $teams = [];\n\n if ($teamId) {\n $team = Team::idOrUuId($teamId);\n if ($team && $team->crm?->provider === Configuration::PROVIDER_HUBSPOT) {\n $this->error(sprintf(\n 'Team %s (%s) uses HubSpot. Use crm:sync-hubspot-objects instead.',\n $team->getName(),\n $team->getUuid()\n ));\n\n return;\n }\n if ($team) {\n $teams[] = $team;\n }\n } else {\n // Exclude HubSpot teams - handled by crm:sync-hubspot-objects\n $teams = Team::where('status', Team::STATUS_ACTIVE)\n ->whereHas(\n 'crm',\n fn ($q) => $q\n ->where('provider', '!=', Configuration::PROVIDER_HUBSPOT)\n ->where('sync_objects', true)\n )\n ->get();\n }\n\n $this->dispatchSyncJobsForTeams($teams);\n }\n\n protected function getStaggerDelaySeconds(): float\n {\n return 2;\n }\n\n protected function getMaxDelaySeconds(): int\n {\n // Cap delay at 15 minutes (SQS max delay limit)\n // capacity of 450 teams before reach\n return 900;\n }\n\n protected function getLogPrefix(): string\n {\n return '';\n }\n\n protected function createSyncJob(Team $team): Job\n {\n return new SyncObjectsJob($team);\n }\n}","depth":4,"bounds":{"left":0.15724733,"top":0.2952913,"width":0.21476063,"height":0.7047087},"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Console\\Commands\\Crm;\n\nuse Jiminny\\Console\\Commands\\Command;\nuse Jiminny\\Console\\Commands\\Crm\\Traits\\SyncObjectsCommandTrait;\nuse Jiminny\\Jobs\\Crm\\SyncObjects as SyncObjectsJob;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Models\\Team;\n\nclass SyncObjects extends Command\n{\n use SyncObjectsCommandTrait;\n\n /**\n * The name and signature of the console command.\n *\n * @var string\n */\n protected $signature = 'crm:sync-objects {team?} {--sync}';\n\n /**\n * The console command description.\n *\n * @var string\n */\n protected $description = 'Sync remote CRM objects.';\n\n /**\n * Execute the console command.\n */\n public function handle(): void\n {\n $teamId = $this->argument('team');\n $teams = [];\n\n if ($teamId) {\n $team = Team::idOrUuId($teamId);\n if ($team && $team->crm?->provider === Configuration::PROVIDER_HUBSPOT) {\n $this->error(sprintf(\n 'Team %s (%s) uses HubSpot. Use crm:sync-hubspot-objects instead.',\n $team->getName(),\n $team->getUuid()\n ));\n\n return;\n }\n if ($team) {\n $teams[] = $team;\n }\n } else {\n // Exclude HubSpot teams - handled by crm:sync-hubspot-objects\n $teams = Team::where('status', Team::STATUS_ACTIVE)\n ->whereHas(\n 'crm',\n fn ($q) => $q\n ->where('provider', '!=', Configuration::PROVIDER_HUBSPOT)\n ->where('sync_objects', true)\n )\n ->get();\n }\n\n $this->dispatchSyncJobsForTeams($teams);\n }\n\n protected function getStaggerDelaySeconds(): float\n {\n return 2;\n }\n\n protected function getMaxDelaySeconds(): int\n {\n // Cap delay at 15 minutes (SQS max delay limit)\n // capacity of 450 teams before reach\n return 900;\n }\n\n protected function getLogPrefix(): string\n {\n return '';\n }\n\n protected function createSyncJob(Team $team): Job\n {\n return new SyncObjectsJob($team);\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-7615602772621210147
|
-8678315669563599930
|
visual_change
|
accessibility
|
NULL
|
Project: faVsco.js, menu
pipedrive-sdk-poc, menu
S Project: faVsco.js, menu
pipedrive-sdk-poc, 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\Console\Commands\Crm;
use Jiminny\Console\Commands\Command;
use Jiminny\Console\Commands\Crm\Traits\SyncObjectsCommandTrait;
use Jiminny\Jobs\Crm\SyncObjects as SyncObjectsJob;
use Jiminny\Jobs\Job;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Models\Team;
class SyncObjects extends Command
{
use SyncObjectsCommandTrait;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'crm:sync-objects {team?} {--sync}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Sync remote CRM objects.';
/**
* Execute the console command.
*/
public function handle(): void
{
$teamId = $this->argument('team');
$teams = [];
if ($teamId) {
$team = Team::idOrUuId($teamId);
if ($team && $team->crm?->provider === Configuration::PROVIDER_HUBSPOT) {
$this->error(sprintf(
'Team %s (%s) uses HubSpot. Use crm:sync-hubspot-objects instead.',
$team->getName(),
$team->getUuid()
));
return;
}
if ($team) {
$teams[] = $team;
}
} else {
// Exclude HubSpot teams - handled by crm:sync-hubspot-objects
$teams = Team::where('status', Team::STATUS_ACTIVE)
->whereHas(
'crm',
fn ($q) => $q
->where('provider', '!=', Configuration::PROVIDER_HUBSPOT)
->where('sync_objects', true)
)
->get();
}
$this->dispatchSyncJobsForTeams($teams);
}
protected function getStaggerDelaySeconds(): float
{
return 2;
}
protected function getMaxDelaySeconds(): int
{
// Cap delay at 15 minutes (SQS max delay limit)
// capacity of 450 teams before reach
return 900;
}
protected function getLogPrefix(): string
{
return '';
}
protected function createSyncJob(Team $team): Job
{
return new SyncObjectsJob($team);
}
}...
|
49411
|
NULL
|
NULL
|
NULL
|
|
49412
|
1764
|
0
|
2026-05-18T06:42:39.257993+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779086559257_m1.jpg...
|
PhpStorm
|
faVsco.js – TestPipedriveOfficialSdkCommand.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
pipedrive-sdk-poc, menu
|
[{"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":"pipedrive-sdk-poc, menu","depth":5,"on_screen":true,"help_text":"Git Branch: pipedrive-sdk-poc","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
3036442092684777152
|
-5740307667640416884
|
visual_change
|
hybrid
|
NULL
|
Project: faVsco.js, menu
pipedrive-sdk-poc, menu
i Project: faVsco.js, menu
pipedrive-sdk-poc, menu
iTerm2ShelllEditViewSessionScriptsProfilesWindowHelp•• 0APP (-zsh)DOCKERLast login: Mon May 18 09:17:28 on ttys007O 81DEV (-zsh)O ₴2Poetry could not find a pyproject.toml file in /Users/lukas/jiminny/app or its parentsPoetry could not find a pyproject.toml file in /Users/lukas/jiminny/app or its parentslukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/jiminny/app (pipedrive-sdk-poc) $ U‹ $0APP (-zsh)|83100% C47 8• Mon 18 May 9:42:38T₴1screenpipe*O &4APP...
|
49408
|
NULL
|
NULL
|
NULL
|
|
49411
|
1765
|
1
|
2026-05-18T06:42:37.607757+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779086557607_m2.jpg...
|
PhpStorm
|
faVsco.js – TestPipedriveOfficialSdkCommand.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
pipedrive-sdk-poc, menu
S Project: faVsco.js, menu
pipedrive-sdk-poc, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'...
|
[{"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":"pipedrive-sdk-poc, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.05618351,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: pipedrive-sdk-poc","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}]...
|
7962651264686056251
|
-7483856360197085818
|
visual_change
|
hybrid
|
NULL
|
Project: faVsco.js, menu
pipedrive-sdk-poc, menu
S Project: faVsco.js, menu
pipedrive-sdk-poc, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
PhostormVIewINavicarecodeLaravelKeractorFV faVsco.js?° pipedrive-sdk-pocroledey© SyncProfileMetadata.phpCConvertLeadActivities.ong© SyncPlanhat.php©syncleammetadata.ong© TestPipedriveOfficialSdkCommand.phUpoateopponunityspecrications.onocreatePlaybookcreatedevent.ong>@ Dealinsights>@Dev> 0 Dialersc) IntearationApp/Service.php© LeadConverted.php© CreateSelfCoachedEvent.phpD DTOSC) CreateCommentedEvent.phoC) CreateSmsSentEvent.ohpC) PlanhatActivityListener.phoC Elasticsearch(C)AskAnvthinaPromotService.ohd› EnqagementStatsш GeckoExport(C)AutomatedReportsCommand.ohvphp api y2.ohoC) RequestGenerateReport.Job.oho/>D Livestream(C) AutomatedReportkesulconp© AutomatedReport.phpA console [STAGING]Mallboxes• MidratePlavbackithemes• M Plavbooksdectarelstrict_types-tyD Playlists> M Postmarknanespace Jininny console comnanos crm> M PronhetAuse…..v M Renorto© AutomatedReportsCommand.phpC) AutomatedRenortsRetentionPolicvCor14 Dclass syncubnects extends command© AutomatedReportsSendCommand.pht© CreateMockAskJiminnyReportResultC© DeleteReportCommand.php© GenerateMarketingReport.php© Team.php© Usage.php* The name and sianature of the console commandi>C Slack* ovar strind›_ Teams>C Tracks7301nrotected ssionature = crm:sunc-ohnects -team?, *--svnct'*w Transcription>C TwilioC Users> M Vocabularv* The console command descrintion.>Zoom*Avan strinaC) Command.oho© CreateDatabaseUsers.phpprotected $description = 'Sync remote CRM objects.';C)DatabaseTiableCount.oho© DeleteOIdAiCrmNotesCommand.ohv* Eyecuto the concole commandlCDeleteS?Leftoverscommand.ohn@ DevPostmanCommand.phpC) DiarizeViaAiParticinantldentificationCompublic function handle: void{...}@ EncrvntTokensCommand.nhn(C EnaaaementStatsReaenerateCommand.n( FeatureFlaasHelner nhr69 6t)protected function getStaggerDelavSeconds@: floatf...© FixCrossTenantlssues.php© FlushRolesPermissionsCache.php© GeneratelnternalWebhookToken.php© GroupSetDefaultLanguageCommand.phc74 6tprotected function getMaxDelavSeconds@: int{...}81 6t>protected function getLoqPrefix@: stringf...;l© HelperTruncateCoachingTables.php=custom.loglaravel.log4 SF [jiminny@localhost]« HS_local [jiminny@localhost]A console [PROD]© TestPipedriveOfficialSdkCommand.php x © Kernel.phpA console [EU]123135class TestPipedrive0fficialSdkCommand extends Commandprivate function testBasicCrudOperations(SocialAccount $socialAccount): voidschls->inrol string."DB token (Length:scrlen(srawudloken)")"):l$this->info( string: "DB token preview: " . substr(SrawDbToken, offset: 0,length// Check token expirysexolres = ssoclalAccounc->explres.$this->info( string: "Token expires at: " . ($expires ? Carbon::createFromTimesSthis->info string:"Token 1s expired: ". Sexpires ∞x Sexpires < timeo?"YTest with the D8 token directlySthis->info( string: "\n--- Testing with DB token ---")sconfio = new conflourationonsconf1o->serAccesstoken(srawdblloken):I/ Test 2a: Get DealsAccept Reject// Test 2a: Get Deals SummarySthis->testGetDeals(Sconfig);S1 te-7 test eef Reons (Sconf19);I/ Test 2b: Get Persons using official SDK methodsthis->testbetPersons0fficialsconf10)i// Test 2c: Get Activitiesscnis->rescbecacclviclesscontlopnTect 2c. Got Activities ucina official Clk methodSthis->testGetActivities0fficial($config):}catch (Exception $e) {$this->recordResult( testName: 'Basic CRUD Operations'.success: false, $e->get$this->newLineO;private function testCurrentSystemApproach(SocialAccount SsocialAccount): void{...}1 usageprivate function testGetDeals(Configuration Sconfig): void{...}1usade.orivate function testGetPersons(Confiauration Sconfio): voids.-1 usageorivate function testGetActivities(Confiauration Sconfio: void ...?CascadeDebugging PipedrivThought for 1s0 4 tasks done• Update POC to test official SDK methods properlyOTest actual CRUD operations with official SDKTest vz APl support it avallable3 Test error handlina properiv• TestPipedriveOfficialSckCommand.ohgThouahtserror while ediung lestripedriveurticlalsakcommano.onp ›Thoughts ›Read TestPipedriveOfficialSdkCommand.pho #L175-224lhoughtsE • TestPipedriveOfficialSdkCommand.php100% Lz• Mon 18 May 9:42:37AskJiminnyReportActivityServiceTest v+0 ..continue and test all in the command.+6 -656 tokens2272 files +6-5Ask anvthina (&4-L)÷ « CodeSWF.16* Reiect allAccent alli•.0W Windsurf TeamoPo 4 spaces...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
49403
|
1762
|
24
|
2026-05-18T06:41:52.265305+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779086512265_m1.jpg...
|
PhpStorm
|
faVsco.js – TestPipedriveOfficialSdkCommand.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
pipedrive-sdk-poc, menu
S Project: faVsco.js, menu
pipedrive-sdk-poc, 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\Console\Commands\Crm;
use Jiminny\Console\Commands\Command;
use Jiminny\Console\Commands\Crm\Traits\SyncObjectsCommandTrait;
use Jiminny\Jobs\Crm\SyncObjects as SyncObjectsJob;
use Jiminny\Jobs\Job;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Models\Team;
class SyncObjects extends Command
{
use SyncObjectsCommandTrait;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'crm:sync-objects {team?} {--sync}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Sync remote CRM objects.';
/**
* Execute the console command.
*/
public function handle(): void
{
$teamId = $this->argument('team');
$teams = [];
if ($teamId) {
$team = Team::idOrUuId($teamId);
if ($team && $team->crm?->provider === Configuration::PROVIDER_HUBSPOT) {
$this->error(sprintf(
'Team %s (%s) uses HubSpot. Use crm:sync-hubspot-objects instead.',
$team->getName(),
$team->getUuid()
));
return;
}
if ($team) {
$teams[] = $team;
}
} else {
// Exclude HubSpot teams - handled by crm:sync-hubspot-objects
$teams = Team::where('status', Team::STATUS_ACTIVE)
->whereHas(
'crm',
fn ($q) => $q
->where('provider', '!=', Configuration::PROVIDER_HUBSPOT)
->where('sync_objects', true)
)
->get();
}
$this->dispatchSyncJobsForTeams($teams);
}
protected function getStaggerDelaySeconds(): float
{
return 2;
}
protected function getMaxDelaySeconds(): int
{
// Cap delay at 15 minutes (SQS max delay limit)
// capacity of 450 teams before reach
return 900;
}
protected function getLogPrefix(): string
{
return '';
}
protected function createSyncJob(Team $team): Job
{
return new SyncObjectsJob($team);
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
1
51
1
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Console\Commands\Crm;
use Carbon\Carbon;
use Exception;
use Illuminate\Console\Command;
use Jiminny\Component\Encryption\EncryptedTokenManager;
use Jiminny\Models\SocialAccount;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Pipedrive\versions\v1\Api\ActivitiesApi;
use Pipedrive\versions\v1\Api\ActivityFieldsApi;
use Pipedrive\versions\v1\Api\DealFieldsApi;
use Pipedrive\versions\v1\Api\DealsApi;
use Pipedrive\versions\v1\Api\PersonFieldsApi;
use Pipedrive\versions\v1\Api\PersonsApi;
use Pipedrive\versions\v1\Configuration;
class TestPipedriveOfficialSdkCommand extends Command
{
protected $signature = 'jiminny:test-pipedrive-official-sdk {teamId}';
protected $description = 'Test official Pipedrive SDK (pipedrive/pipedrive) with existing OAuth tokens';
private array $results = [];
private EncryptedTokenManager $tokenManager;
public function __construct(EncryptedTokenManager $tokenManager)
{
parent::__construct();
$this->tokenManager = $tokenManager;
}
public function handle(): int
{
$teamId = (int) $this->argument('teamId');
$this->info("Starting Pipedrive Official SDK POC for Team ID: {$teamId}");
$this->newLine();
try {
$this->runTests($teamId);
} catch (Exception $e) {
$this->error("POC failed with error: {$e->getMessage()}");
$this->error($e->getTraceAsString());
return 1;
}
$this->displayResults();
return 0;
}
private function runTests(int $teamId): void
{
$team = Team::find($teamId);
if (! $team) {
throw new Exception("Team with ID {$teamId} not found");
}
$socialAccount = SocialAccount::where('provider', SocialAccount::PROVIDER_PIPEDRIVE)
->whereHas('sociable', function ($query) use ($team) {
$query->where('team_id', $team->id);
})
->orderByDesc('expires') // Get the most recently expiring token
->first();
if (! $socialAccount) {
throw new Exception("No Pipedrive social account found for team {$teamId}");
}
$this->info("Found Pipedrive account for team: {$team->name}");
$this->newLine();
// Test 1: OAuth Initialization
$this->testOAuthInitialization($socialAccount);
// Test 2: Basic CRUD Operations
$this->testBasicCrudOperations($socialAccount);
// Test 3: Field Metadata
$this->testFieldMetadata($socialAccount);
// Test 4: v2 API Support
$this->testV2ApiSupport($socialAccount);
// Test 5: Error Handling
$this->testErrorHandling($socialAccount);
}
private function testOAuthInitialization(SocialAccount $socialAccount): void
{
$this->info('Test 1: OAuth Initialization');
$startTime = microtime(true);
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
$client = new DealsApi(null, $config);
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('OAuth Initialization', true, "Client initialized successfully in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('OAuth Initialization', false, $e->getMessage(), $elapsed);
}
$this->newLine();
}
private function testBasicCrudOperations(SocialAccount $socialAccount): void
{
$this->info('Test 2: Basic CRUD Operations');
try {
// Get the raw token from database
$rawDbToken = $socialAccount->getProviderUserToken();
$this->info("DB token (length: " . strlen($rawDbToken) . ")");
$this->info("DB token preview: " . substr($rawDbToken, 0, 20) . "...");
// Check token expiry
$expires = $socialAccount->expires;
$this->info("Token expires at: " . ($expires ? Carbon::createFromTimestamp($expires)->toDateTimeString() : 'null'));
$this->info("Token is expired: " . ($expires && $expires < time() ? 'YES' : 'NO'));
// Test with the DB token directly
$this->info("\n--- Testing with DB token ---");
$config = new Configuration();
$config->setAccessToken($rawDbToken);
// Test 2a: Get Deals
$this->testGetDeals($config);
// Test 2b: Get Persons
$this->testGetPersons($config);
// Test 2c: Get Activities
$this->testGetActivities($config);
} catch (Exception $e) {
$this->recordResult('Basic CRUD Operations', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testCurrentSystemApproach(SocialAccount $socialAccount): void
{
$startTime = microtime(true);
try {
// Simulate how current system makes requests
$token = $socialAccount->getProviderUserToken();
$url = 'https://api.pipedrive.com/api/v2/deals';
$headers = [
'Authorization' => 'Bearer ' . $token,
'Accept' => 'application/json',
];
$client = new \GuzzleHttp\Client();
$response = $client->get($url, [
'headers' => $headers,
'timeout' => 30,
]);
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$statusCode = $response->getStatusCode();
$this->recordResult('Current System v2 API', $statusCode === 200, "Status: {$statusCode} in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Current System v2 API', false, $e->getMessage(), $elapsed);
}
}
private function testGetDeals(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new DealsApi(null, $config);
// Official SDK uses getDealsSummary instead of getDeals
$result = $apiInstance->getDealsSummary();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Deals Summary', true, "Retrieved deals summary in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Deals Summary', false, $e->getMessage(), $elapsed);
}
}
private function testGetPersons(Configuration $config): void
{
$startTime = microtime(true);
try {
// Official SDK doesn't have a simple getPersons method
// Skip this test for now as the API structure is different
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Persons', false, "Method not available in official SDK - API structure different", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Persons', false, $e->getMessage(), $elapsed);
}
}
private function testGetActivities(Configuration $config): void
{
$startTime = microtime(true);
try {
// Official SDK doesn't have a simple getActivities method
// Skip this test for now as the API structure is different
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Activities', false, "Method not available in official SDK - API structure different", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Activities', false, $e->getMessage(), $elapsed);
}
}
private function testFieldMetadata(SocialAccount $socialAccount): void
{
$this->info('Test 3: Field Metadata');
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
// Test 3a: Get Deal Fields
$this->testGetDealFields($config);
// Test 3b: Get Person Fields
$this->testGetPersonFields($config);
// Test 3c: Get Activity Fields
$this->testGetActivityFields($config);
} catch (Exception $e) {
$this->recordResult('Field Metadata', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testGetDealFields(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new DealFieldsApi(null, $config);
$result = $apiInstance->getDealFields();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$count = $result->getData() ? count($result->getData()) : 0;
$this->recordResult('Get Deal Fields', true, "Retrieved {$count} deal fields in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Deal Fields', false, $e->getMessage(), $elapsed);
}
}
private function testGetPersonFields(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new PersonFieldsApi(null, $config);
$result = $apiInstance->getPersonFields();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$count = $result->getData() ? count($result->getData()) : 0;
$this->recordResult('Get Person Fields', true, "Retrieved {$count} person fields in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Person Fields', false, $e->getMessage(), $elapsed);
}
}
private function testGetActivityFields(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new ActivityFieldsApi(null, $config);
$result = $apiInstance->getActivityFields();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$count = $result->getData() ? count($result->getData()) : 0;
$this->recordResult('Get Activity Fields', true, "Retrieved {$count} activity fields in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Activity Fields', false, $e->getMessage(), $elapsed);
}
}
private function testV2ApiSupport(SocialAccount $socialAccount): void
{
$this->info('Test 4: v2 API Support (Cursor-based Pagination)');
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
$startTime = microtime(true);
// Test v2 deals endpoint with cursor pagination
// Note: The official SDK may or may not have v2 API support
// This test will determine if v2 is available
try {
$apiInstance = new DealsApi(null, $config);
$updatedSince = Carbon::now()->subDays(30)->toRfc3339String();
$result = $apiInstance->getDealsSummary();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
// Check if response has cursor information (v2 indicator)
// The response object doesn't have getAdditionalData method
// Just test if the call works - v2 API support is limited in official SDK v1
$this->recordResult('v2 API Support', false, "v1 API works but v2 cursor pagination not available in official SDK v1. Retrieved summary in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('v2 API Support', false, $e->getMessage(), $elapsed);
}
} catch (Exception $e) {
$this->recordResult('v2 API Support', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testErrorHandling(SocialAccount $socialAccount): void
{
$this->info('Test 5: Error Handling');
try {
// Test 5a: Invalid Token
$this->testInvalidToken();
// Test 5b: Non-existent Resource
$this->testNonExistentResource($socialAccount);
} catch (Exception $e) {
$this->recordResult('Error Handling', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testInvalidToken(): void
{
$startTime = microtime(true);
try {
$config = new Configuration();
$config->setAccessToken('invalid_token');
$client = new DealsApi(null, $config);
$client->getDealsSummary();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Invalid Token Error', false, "Expected error but request succeeded in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Invalid Token Error', true, "Correctly threw exception: {$e->getMessage()} in {$elapsed}ms", $elapsed);
}
}
private function testNonExistentResource(SocialAccount $socialAccount): void
{
$startTime = microtime(true);
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
$client = new DealsApi(null, $config);
// Try to get a deal with a very high ID that likely doesn't exist
// Note: Official SDK doesn't have a simple getDeal method, so skip this
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Non-existent Resource Error', false, "Method not available in official SDK - API structure different", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Non-existent Resource Error', true, "Correctly threw exception: {$e->getMessage()} in {$elapsed}ms", $elapsed);
}
}
private function recordResult(string $testName, bool $success, string $message, float $elapsed): void
{
$this->results[] = [
'test' => $testName,
'success' => $success,
'message' => $message,
'elapsed' => $elapsed,
];
$status = $success ? '✅ PASS' : '❌ FAIL';
$this->line(" {$status} - {$testName}: {$message}");
}
private function displayResults(): void
{
$this->newLine();
$this->info('=== POC Test Results Summary ===');
$this->newLine();
$total = count($this->results);
$passed = count(array_filter($this->results, fn ($r) => $r['success']));
$failed = $total - $passed;
$this->line("Total Tests: {$total}");
$this->line("Passed: {$passed}");
$this->line("Failed: {$failed}");
$this->newLine();
$totalTime = array_sum(array_column($this->results, 'elapsed'));
$this->line("Total Execution Time: {$totalTime}ms");
$this->newLine();
if ($failed > 0) {
$this->warn('Failed Tests:');
foreach ($this->results as $result) {
if (! $result['success']) {
$this->line(" - {$result['test']}: {$result['message']}");
}
}
$this->newLine();
}
$this->info('=== Detailed Results ===');
foreach ($this->results as $result) {
$status = $result['success'] ? '✅' : '❌';
$this->line("{$status} {$result['test']} ({$result['elapsed']}ms): {$result['message']}");
}
$this->newLine();
$this->info('=== Recommendations ===');
if ($passed === $total) {
$this->info('✅ All tests passed! The official SDK is viable for migration.');
$this->info('Next steps: Proceed with full migration planning.');
} else {
$this->warn('⚠️ Some tests failed. Review the failures above before proceeding.');
$this->warn('Consider hybrid approach or investigate alternatives if critical operations fail.');
}
}
}
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":"pipedrive-sdk-poc, menu","depth":5,"on_screen":true,"help_text":"Git Branch: pipedrive-sdk-poc","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\\Console\\Commands\\Crm;\n\nuse Jiminny\\Console\\Commands\\Command;\nuse Jiminny\\Console\\Commands\\Crm\\Traits\\SyncObjectsCommandTrait;\nuse Jiminny\\Jobs\\Crm\\SyncObjects as SyncObjectsJob;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Models\\Team;\n\nclass SyncObjects extends Command\n{\n use SyncObjectsCommandTrait;\n\n /**\n * The name and signature of the console command.\n *\n * @var string\n */\n protected $signature = 'crm:sync-objects {team?} {--sync}';\n\n /**\n * The console command description.\n *\n * @var string\n */\n protected $description = 'Sync remote CRM objects.';\n\n /**\n * Execute the console command.\n */\n public function handle(): void\n {\n $teamId = $this->argument('team');\n $teams = [];\n\n if ($teamId) {\n $team = Team::idOrUuId($teamId);\n if ($team && $team->crm?->provider === Configuration::PROVIDER_HUBSPOT) {\n $this->error(sprintf(\n 'Team %s (%s) uses HubSpot. Use crm:sync-hubspot-objects instead.',\n $team->getName(),\n $team->getUuid()\n ));\n\n return;\n }\n if ($team) {\n $teams[] = $team;\n }\n } else {\n // Exclude HubSpot teams - handled by crm:sync-hubspot-objects\n $teams = Team::where('status', Team::STATUS_ACTIVE)\n ->whereHas(\n 'crm',\n fn ($q) => $q\n ->where('provider', '!=', Configuration::PROVIDER_HUBSPOT)\n ->where('sync_objects', true)\n )\n ->get();\n }\n\n $this->dispatchSyncJobsForTeams($teams);\n }\n\n protected function getStaggerDelaySeconds(): float\n {\n return 2;\n }\n\n protected function getMaxDelaySeconds(): int\n {\n // Cap delay at 15 minutes (SQS max delay limit)\n // capacity of 450 teams before reach\n return 900;\n }\n\n protected function getLogPrefix(): string\n {\n return '';\n }\n\n protected function createSyncJob(Team $team): Job\n {\n return new SyncObjectsJob($team);\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Console\\Commands\\Crm;\n\nuse Jiminny\\Console\\Commands\\Command;\nuse Jiminny\\Console\\Commands\\Crm\\Traits\\SyncObjectsCommandTrait;\nuse Jiminny\\Jobs\\Crm\\SyncObjects as SyncObjectsJob;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Models\\Team;\n\nclass SyncObjects extends Command\n{\n use SyncObjectsCommandTrait;\n\n /**\n * The name and signature of the console command.\n *\n * @var string\n */\n protected $signature = 'crm:sync-objects {team?} {--sync}';\n\n /**\n * The console command description.\n *\n * @var string\n */\n protected $description = 'Sync remote CRM objects.';\n\n /**\n * Execute the console command.\n */\n public function handle(): void\n {\n $teamId = $this->argument('team');\n $teams = [];\n\n if ($teamId) {\n $team = Team::idOrUuId($teamId);\n if ($team && $team->crm?->provider === Configuration::PROVIDER_HUBSPOT) {\n $this->error(sprintf(\n 'Team %s (%s) uses HubSpot. Use crm:sync-hubspot-objects instead.',\n $team->getName(),\n $team->getUuid()\n ));\n\n return;\n }\n if ($team) {\n $teams[] = $team;\n }\n } else {\n // Exclude HubSpot teams - handled by crm:sync-hubspot-objects\n $teams = Team::where('status', Team::STATUS_ACTIVE)\n ->whereHas(\n 'crm',\n fn ($q) => $q\n ->where('provider', '!=', Configuration::PROVIDER_HUBSPOT)\n ->where('sync_objects', true)\n )\n ->get();\n }\n\n $this->dispatchSyncJobsForTeams($teams);\n }\n\n protected function getStaggerDelaySeconds(): float\n {\n return 2;\n }\n\n protected function getMaxDelaySeconds(): int\n {\n // Cap delay at 15 minutes (SQS max delay limit)\n // capacity of 450 teams before reach\n return 900;\n }\n\n protected function getLogPrefix(): string\n {\n return '';\n }\n\n protected function createSyncJob(Team $team): Job\n {\n return new SyncObjectsJob($team);\n }\n}","role_description":"text entry area","is_enabled":true,"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":"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":"AXStaticText","text":"1","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"51","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"1","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"on_screen":true,"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\\Console\\Commands\\Crm;\n\nuse Carbon\\Carbon;\nuse Exception;\nuse Illuminate\\Console\\Command;\nuse Jiminny\\Component\\Encryption\\EncryptedTokenManager;\nuse Jiminny\\Models\\SocialAccount;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Pipedrive\\versions\\v1\\Api\\ActivitiesApi;\nuse Pipedrive\\versions\\v1\\Api\\ActivityFieldsApi;\nuse Pipedrive\\versions\\v1\\Api\\DealFieldsApi;\nuse Pipedrive\\versions\\v1\\Api\\DealsApi;\nuse Pipedrive\\versions\\v1\\Api\\PersonFieldsApi;\nuse Pipedrive\\versions\\v1\\Api\\PersonsApi;\nuse Pipedrive\\versions\\v1\\Configuration;\n\nclass TestPipedriveOfficialSdkCommand extends Command\n{\n protected $signature = 'jiminny:test-pipedrive-official-sdk {teamId}';\n\n protected $description = 'Test official Pipedrive SDK (pipedrive/pipedrive) with existing OAuth tokens';\n\n private array $results = [];\n\n private EncryptedTokenManager $tokenManager;\n\n public function __construct(EncryptedTokenManager $tokenManager)\n {\n parent::__construct();\n $this->tokenManager = $tokenManager;\n }\n\n public function handle(): int\n {\n $teamId = (int) $this->argument('teamId');\n\n $this->info(\"Starting Pipedrive Official SDK POC for Team ID: {$teamId}\");\n $this->newLine();\n\n try {\n $this->runTests($teamId);\n } catch (Exception $e) {\n $this->error(\"POC failed with error: {$e->getMessage()}\");\n $this->error($e->getTraceAsString());\n\n return 1;\n }\n\n $this->displayResults();\n\n return 0;\n }\n\n private function runTests(int $teamId): void\n {\n $team = Team::find($teamId);\n if (! $team) {\n throw new Exception(\"Team with ID {$teamId} not found\");\n }\n\n $socialAccount = SocialAccount::where('provider', SocialAccount::PROVIDER_PIPEDRIVE)\n ->whereHas('sociable', function ($query) use ($team) {\n $query->where('team_id', $team->id);\n })\n ->orderByDesc('expires') // Get the most recently expiring token\n ->first();\n\n if (! $socialAccount) {\n throw new Exception(\"No Pipedrive social account found for team {$teamId}\");\n }\n\n $this->info(\"Found Pipedrive account for team: {$team->name}\");\n $this->newLine();\n\n // Test 1: OAuth Initialization\n $this->testOAuthInitialization($socialAccount);\n\n // Test 2: Basic CRUD Operations\n $this->testBasicCrudOperations($socialAccount);\n\n // Test 3: Field Metadata\n $this->testFieldMetadata($socialAccount);\n\n // Test 4: v2 API Support\n $this->testV2ApiSupport($socialAccount);\n\n // Test 5: Error Handling\n $this->testErrorHandling($socialAccount);\n }\n\n private function testOAuthInitialization(SocialAccount $socialAccount): void\n {\n $this->info('Test 1: OAuth Initialization');\n $startTime = microtime(true);\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n\n $client = new DealsApi(null, $config);\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('OAuth Initialization', true, \"Client initialized successfully in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('OAuth Initialization', false, $e->getMessage(), $elapsed);\n }\n\n $this->newLine();\n }\n\n private function testBasicCrudOperations(SocialAccount $socialAccount): void\n {\n $this->info('Test 2: Basic CRUD Operations');\n\n try {\n // Get the raw token from database\n $rawDbToken = $socialAccount->getProviderUserToken();\n $this->info(\"DB token (length: \" . strlen($rawDbToken) . \")\");\n $this->info(\"DB token preview: \" . substr($rawDbToken, 0, 20) . \"...\");\n\n // Check token expiry\n $expires = $socialAccount->expires;\n $this->info(\"Token expires at: \" . ($expires ? Carbon::createFromTimestamp($expires)->toDateTimeString() : 'null'));\n $this->info(\"Token is expired: \" . ($expires && $expires < time() ? 'YES' : 'NO'));\n\n // Test with the DB token directly\n $this->info(\"\\n--- Testing with DB token ---\");\n $config = new Configuration();\n $config->setAccessToken($rawDbToken);\n\n // Test 2a: Get Deals\n $this->testGetDeals($config);\n\n // Test 2b: Get Persons\n $this->testGetPersons($config);\n\n // Test 2c: Get Activities\n $this->testGetActivities($config);\n } catch (Exception $e) {\n $this->recordResult('Basic CRUD Operations', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testCurrentSystemApproach(SocialAccount $socialAccount): void\n {\n $startTime = microtime(true);\n\n try {\n // Simulate how current system makes requests\n $token = $socialAccount->getProviderUserToken();\n $url = 'https://api.pipedrive.com/api/v2/deals';\n\n $headers = [\n 'Authorization' => 'Bearer ' . $token,\n 'Accept' => 'application/json',\n ];\n\n $client = new \\GuzzleHttp\\Client();\n $response = $client->get($url, [\n 'headers' => $headers,\n 'timeout' => 30,\n ]);\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $statusCode = $response->getStatusCode();\n $this->recordResult('Current System v2 API', $statusCode === 200, \"Status: {$statusCode} in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Current System v2 API', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetDeals(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new DealsApi(null, $config);\n // Official SDK uses getDealsSummary instead of getDeals\n $result = $apiInstance->getDealsSummary();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Deals Summary', true, \"Retrieved deals summary in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Deals Summary', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetPersons(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n // Official SDK doesn't have a simple getPersons method\n // Skip this test for now as the API structure is different\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Persons', false, \"Method not available in official SDK - API structure different\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Persons', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetActivities(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n // Official SDK doesn't have a simple getActivities method\n // Skip this test for now as the API structure is different\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Activities', false, \"Method not available in official SDK - API structure different\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Activities', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testFieldMetadata(SocialAccount $socialAccount): void\n {\n $this->info('Test 3: Field Metadata');\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n\n // Test 3a: Get Deal Fields\n $this->testGetDealFields($config);\n\n // Test 3b: Get Person Fields\n $this->testGetPersonFields($config);\n\n // Test 3c: Get Activity Fields\n $this->testGetActivityFields($config);\n } catch (Exception $e) {\n $this->recordResult('Field Metadata', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testGetDealFields(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new DealFieldsApi(null, $config);\n $result = $apiInstance->getDealFields();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $count = $result->getData() ? count($result->getData()) : 0;\n $this->recordResult('Get Deal Fields', true, \"Retrieved {$count} deal fields in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Deal Fields', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetPersonFields(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new PersonFieldsApi(null, $config);\n $result = $apiInstance->getPersonFields();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $count = $result->getData() ? count($result->getData()) : 0;\n $this->recordResult('Get Person Fields', true, \"Retrieved {$count} person fields in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Person Fields', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetActivityFields(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new ActivityFieldsApi(null, $config);\n $result = $apiInstance->getActivityFields();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $count = $result->getData() ? count($result->getData()) : 0;\n $this->recordResult('Get Activity Fields', true, \"Retrieved {$count} activity fields in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Activity Fields', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testV2ApiSupport(SocialAccount $socialAccount): void\n {\n $this->info('Test 4: v2 API Support (Cursor-based Pagination)');\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n\n $startTime = microtime(true);\n\n // Test v2 deals endpoint with cursor pagination\n // Note: The official SDK may or may not have v2 API support\n // This test will determine if v2 is available\n try {\n $apiInstance = new DealsApi(null, $config);\n $updatedSince = Carbon::now()->subDays(30)->toRfc3339String();\n $result = $apiInstance->getDealsSummary();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n\n // Check if response has cursor information (v2 indicator)\n // The response object doesn't have getAdditionalData method\n // Just test if the call works - v2 API support is limited in official SDK v1\n $this->recordResult('v2 API Support', false, \"v1 API works but v2 cursor pagination not available in official SDK v1. Retrieved summary in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('v2 API Support', false, $e->getMessage(), $elapsed);\n }\n } catch (Exception $e) {\n $this->recordResult('v2 API Support', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testErrorHandling(SocialAccount $socialAccount): void\n {\n $this->info('Test 5: Error Handling');\n\n try {\n // Test 5a: Invalid Token\n $this->testInvalidToken();\n\n // Test 5b: Non-existent Resource\n $this->testNonExistentResource($socialAccount);\n } catch (Exception $e) {\n $this->recordResult('Error Handling', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testInvalidToken(): void\n {\n $startTime = microtime(true);\n\n try {\n $config = new Configuration();\n $config->setAccessToken('invalid_token');\n $client = new DealsApi(null, $config);\n\n $client->getDealsSummary();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Invalid Token Error', false, \"Expected error but request succeeded in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Invalid Token Error', true, \"Correctly threw exception: {$e->getMessage()} in {$elapsed}ms\", $elapsed);\n }\n }\n\n private function testNonExistentResource(SocialAccount $socialAccount): void\n {\n $startTime = microtime(true);\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n $client = new DealsApi(null, $config);\n\n // Try to get a deal with a very high ID that likely doesn't exist\n // Note: Official SDK doesn't have a simple getDeal method, so skip this\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Non-existent Resource Error', false, \"Method not available in official SDK - API structure different\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Non-existent Resource Error', true, \"Correctly threw exception: {$e->getMessage()} in {$elapsed}ms\", $elapsed);\n }\n }\n\n private function recordResult(string $testName, bool $success, string $message, float $elapsed): void\n {\n $this->results[] = [\n 'test' => $testName,\n 'success' => $success,\n 'message' => $message,\n 'elapsed' => $elapsed,\n ];\n\n $status = $success ? '✅ PASS' : '❌ FAIL';\n $this->line(\" {$status} - {$testName}: {$message}\");\n }\n\n private function displayResults(): void\n {\n $this->newLine();\n $this->info('=== POC Test Results Summary ===');\n $this->newLine();\n\n $total = count($this->results);\n $passed = count(array_filter($this->results, fn ($r) => $r['success']));\n $failed = $total - $passed;\n\n $this->line(\"Total Tests: {$total}\");\n $this->line(\"Passed: {$passed}\");\n $this->line(\"Failed: {$failed}\");\n $this->newLine();\n\n $totalTime = array_sum(array_column($this->results, 'elapsed'));\n $this->line(\"Total Execution Time: {$totalTime}ms\");\n $this->newLine();\n\n if ($failed > 0) {\n $this->warn('Failed Tests:');\n foreach ($this->results as $result) {\n if (! $result['success']) {\n $this->line(\" - {$result['test']}: {$result['message']}\");\n }\n }\n $this->newLine();\n }\n\n $this->info('=== Detailed Results ===');\n foreach ($this->results as $result) {\n $status = $result['success'] ? '✅' : '❌';\n $this->line(\"{$status} {$result['test']} ({$result['elapsed']}ms): {$result['message']}\");\n }\n\n $this->newLine();\n $this->info('=== Recommendations ===');\n\n if ($passed === $total) {\n $this->info('✅ All tests passed! The official SDK is viable for migration.');\n $this->info('Next steps: Proceed with full migration planning.');\n } else {\n $this->warn('⚠️ Some tests failed. Review the failures above before proceeding.');\n $this->warn('Consider hybrid approach or investigate alternatives if critical operations fail.');\n }\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Console\\Commands\\Crm;\n\nuse Carbon\\Carbon;\nuse Exception;\nuse Illuminate\\Console\\Command;\nuse Jiminny\\Component\\Encryption\\EncryptedTokenManager;\nuse Jiminny\\Models\\SocialAccount;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Pipedrive\\versions\\v1\\Api\\ActivitiesApi;\nuse Pipedrive\\versions\\v1\\Api\\ActivityFieldsApi;\nuse Pipedrive\\versions\\v1\\Api\\DealFieldsApi;\nuse Pipedrive\\versions\\v1\\Api\\DealsApi;\nuse Pipedrive\\versions\\v1\\Api\\PersonFieldsApi;\nuse Pipedrive\\versions\\v1\\Api\\PersonsApi;\nuse Pipedrive\\versions\\v1\\Configuration;\n\nclass TestPipedriveOfficialSdkCommand extends Command\n{\n protected $signature = 'jiminny:test-pipedrive-official-sdk {teamId}';\n\n protected $description = 'Test official Pipedrive SDK (pipedrive/pipedrive) with existing OAuth tokens';\n\n private array $results = [];\n\n private EncryptedTokenManager $tokenManager;\n\n public function __construct(EncryptedTokenManager $tokenManager)\n {\n parent::__construct();\n $this->tokenManager = $tokenManager;\n }\n\n public function handle(): int\n {\n $teamId = (int) $this->argument('teamId');\n\n $this->info(\"Starting Pipedrive Official SDK POC for Team ID: {$teamId}\");\n $this->newLine();\n\n try {\n $this->runTests($teamId);\n } catch (Exception $e) {\n $this->error(\"POC failed with error: {$e->getMessage()}\");\n $this->error($e->getTraceAsString());\n\n return 1;\n }\n\n $this->displayResults();\n\n return 0;\n }\n\n private function runTests(int $teamId): void\n {\n $team = Team::find($teamId);\n if (! $team) {\n throw new Exception(\"Team with ID {$teamId} not found\");\n }\n\n $socialAccount = SocialAccount::where('provider', SocialAccount::PROVIDER_PIPEDRIVE)\n ->whereHas('sociable', function ($query) use ($team) {\n $query->where('team_id', $team->id);\n })\n ->orderByDesc('expires') // Get the most recently expiring token\n ->first();\n\n if (! $socialAccount) {\n throw new Exception(\"No Pipedrive social account found for team {$teamId}\");\n }\n\n $this->info(\"Found Pipedrive account for team: {$team->name}\");\n $this->newLine();\n\n // Test 1: OAuth Initialization\n $this->testOAuthInitialization($socialAccount);\n\n // Test 2: Basic CRUD Operations\n $this->testBasicCrudOperations($socialAccount);\n\n // Test 3: Field Metadata\n $this->testFieldMetadata($socialAccount);\n\n // Test 4: v2 API Support\n $this->testV2ApiSupport($socialAccount);\n\n // Test 5: Error Handling\n $this->testErrorHandling($socialAccount);\n }\n\n private function testOAuthInitialization(SocialAccount $socialAccount): void\n {\n $this->info('Test 1: OAuth Initialization');\n $startTime = microtime(true);\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n\n $client = new DealsApi(null, $config);\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('OAuth Initialization', true, \"Client initialized successfully in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('OAuth Initialization', false, $e->getMessage(), $elapsed);\n }\n\n $this->newLine();\n }\n\n private function testBasicCrudOperations(SocialAccount $socialAccount): void\n {\n $this->info('Test 2: Basic CRUD Operations');\n\n try {\n // Get the raw token from database\n $rawDbToken = $socialAccount->getProviderUserToken();\n $this->info(\"DB token (length: \" . strlen($rawDbToken) . \")\");\n $this->info(\"DB token preview: \" . substr($rawDbToken, 0, 20) . \"...\");\n\n // Check token expiry\n $expires = $socialAccount->expires;\n $this->info(\"Token expires at: \" . ($expires ? Carbon::createFromTimestamp($expires)->toDateTimeString() : 'null'));\n $this->info(\"Token is expired: \" . ($expires && $expires < time() ? 'YES' : 'NO'));\n\n // Test with the DB token directly\n $this->info(\"\\n--- Testing with DB token ---\");\n $config = new Configuration();\n $config->setAccessToken($rawDbToken);\n\n // Test 2a: Get Deals\n $this->testGetDeals($config);\n\n // Test 2b: Get Persons\n $this->testGetPersons($config);\n\n // Test 2c: Get Activities\n $this->testGetActivities($config);\n } catch (Exception $e) {\n $this->recordResult('Basic CRUD Operations', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testCurrentSystemApproach(SocialAccount $socialAccount): void\n {\n $startTime = microtime(true);\n\n try {\n // Simulate how current system makes requests\n $token = $socialAccount->getProviderUserToken();\n $url = 'https://api.pipedrive.com/api/v2/deals';\n\n $headers = [\n 'Authorization' => 'Bearer ' . $token,\n 'Accept' => 'application/json',\n ];\n\n $client = new \\GuzzleHttp\\Client();\n $response = $client->get($url, [\n 'headers' => $headers,\n 'timeout' => 30,\n ]);\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $statusCode = $response->getStatusCode();\n $this->recordResult('Current System v2 API', $statusCode === 200, \"Status: {$statusCode} in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Current System v2 API', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetDeals(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new DealsApi(null, $config);\n // Official SDK uses getDealsSummary instead of getDeals\n $result = $apiInstance->getDealsSummary();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Deals Summary', true, \"Retrieved deals summary in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Deals Summary', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetPersons(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n // Official SDK doesn't have a simple getPersons method\n // Skip this test for now as the API structure is different\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Persons', false, \"Method not available in official SDK - API structure different\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Persons', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetActivities(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n // Official SDK doesn't have a simple getActivities method\n // Skip this test for now as the API structure is different\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Activities', false, \"Method not available in official SDK - API structure different\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Activities', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testFieldMetadata(SocialAccount $socialAccount): void\n {\n $this->info('Test 3: Field Metadata');\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n\n // Test 3a: Get Deal Fields\n $this->testGetDealFields($config);\n\n // Test 3b: Get Person Fields\n $this->testGetPersonFields($config);\n\n // Test 3c: Get Activity Fields\n $this->testGetActivityFields($config);\n } catch (Exception $e) {\n $this->recordResult('Field Metadata', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testGetDealFields(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new DealFieldsApi(null, $config);\n $result = $apiInstance->getDealFields();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $count = $result->getData() ? count($result->getData()) : 0;\n $this->recordResult('Get Deal Fields', true, \"Retrieved {$count} deal fields in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Deal Fields', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetPersonFields(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new PersonFieldsApi(null, $config);\n $result = $apiInstance->getPersonFields();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $count = $result->getData() ? count($result->getData()) : 0;\n $this->recordResult('Get Person Fields', true, \"Retrieved {$count} person fields in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Person Fields', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetActivityFields(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new ActivityFieldsApi(null, $config);\n $result = $apiInstance->getActivityFields();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $count = $result->getData() ? count($result->getData()) : 0;\n $this->recordResult('Get Activity Fields', true, \"Retrieved {$count} activity fields in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Activity Fields', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testV2ApiSupport(SocialAccount $socialAccount): void\n {\n $this->info('Test 4: v2 API Support (Cursor-based Pagination)');\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n\n $startTime = microtime(true);\n\n // Test v2 deals endpoint with cursor pagination\n // Note: The official SDK may or may not have v2 API support\n // This test will determine if v2 is available\n try {\n $apiInstance = new DealsApi(null, $config);\n $updatedSince = Carbon::now()->subDays(30)->toRfc3339String();\n $result = $apiInstance->getDealsSummary();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n\n // Check if response has cursor information (v2 indicator)\n // The response object doesn't have getAdditionalData method\n // Just test if the call works - v2 API support is limited in official SDK v1\n $this->recordResult('v2 API Support', false, \"v1 API works but v2 cursor pagination not available in official SDK v1. Retrieved summary in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('v2 API Support', false, $e->getMessage(), $elapsed);\n }\n } catch (Exception $e) {\n $this->recordResult('v2 API Support', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testErrorHandling(SocialAccount $socialAccount): void\n {\n $this->info('Test 5: Error Handling');\n\n try {\n // Test 5a: Invalid Token\n $this->testInvalidToken();\n\n // Test 5b: Non-existent Resource\n $this->testNonExistentResource($socialAccount);\n } catch (Exception $e) {\n $this->recordResult('Error Handling', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testInvalidToken(): void\n {\n $startTime = microtime(true);\n\n try {\n $config = new Configuration();\n $config->setAccessToken('invalid_token');\n $client = new DealsApi(null, $config);\n\n $client->getDealsSummary();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Invalid Token Error', false, \"Expected error but request succeeded in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Invalid Token Error', true, \"Correctly threw exception: {$e->getMessage()} in {$elapsed}ms\", $elapsed);\n }\n }\n\n private function testNonExistentResource(SocialAccount $socialAccount): void\n {\n $startTime = microtime(true);\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n $client = new DealsApi(null, $config);\n\n // Try to get a deal with a very high ID that likely doesn't exist\n // Note: Official SDK doesn't have a simple getDeal method, so skip this\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Non-existent Resource Error', false, \"Method not available in official SDK - API structure different\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Non-existent Resource Error', true, \"Correctly threw exception: {$e->getMessage()} in {$elapsed}ms\", $elapsed);\n }\n }\n\n private function recordResult(string $testName, bool $success, string $message, float $elapsed): void\n {\n $this->results[] = [\n 'test' => $testName,\n 'success' => $success,\n 'message' => $message,\n 'elapsed' => $elapsed,\n ];\n\n $status = $success ? '✅ PASS' : '❌ FAIL';\n $this->line(\" {$status} - {$testName}: {$message}\");\n }\n\n private function displayResults(): void\n {\n $this->newLine();\n $this->info('=== POC Test Results Summary ===');\n $this->newLine();\n\n $total = count($this->results);\n $passed = count(array_filter($this->results, fn ($r) => $r['success']));\n $failed = $total - $passed;\n\n $this->line(\"Total Tests: {$total}\");\n $this->line(\"Passed: {$passed}\");\n $this->line(\"Failed: {$failed}\");\n $this->newLine();\n\n $totalTime = array_sum(array_column($this->results, 'elapsed'));\n $this->line(\"Total Execution Time: {$totalTime}ms\");\n $this->newLine();\n\n if ($failed > 0) {\n $this->warn('Failed Tests:');\n foreach ($this->results as $result) {\n if (! $result['success']) {\n $this->line(\" - {$result['test']}: {$result['message']}\");\n }\n }\n $this->newLine();\n }\n\n $this->info('=== Detailed Results ===');\n foreach ($this->results as $result) {\n $status = $result['success'] ? '✅' : '❌';\n $this->line(\"{$status} {$result['test']} ({$result['elapsed']}ms): {$result['message']}\");\n }\n\n $this->newLine();\n $this->info('=== Recommendations ===');\n\n if ($passed === $total) {\n $this->info('✅ All tests passed! The official SDK is viable for migration.');\n $this->info('Next steps: Proceed with full migration planning.');\n } else {\n $this->warn('⚠️ Some tests failed. Review the failures above before proceeding.');\n $this->warn('Consider hybrid approach or investigate alternatives if critical operations fail.');\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}]...
|
5365831007722038384
|
61370652661533572
|
visual_change
|
accessibility
|
NULL
|
Project: faVsco.js, menu
pipedrive-sdk-poc, menu
S Project: faVsco.js, menu
pipedrive-sdk-poc, 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\Console\Commands\Crm;
use Jiminny\Console\Commands\Command;
use Jiminny\Console\Commands\Crm\Traits\SyncObjectsCommandTrait;
use Jiminny\Jobs\Crm\SyncObjects as SyncObjectsJob;
use Jiminny\Jobs\Job;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Models\Team;
class SyncObjects extends Command
{
use SyncObjectsCommandTrait;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'crm:sync-objects {team?} {--sync}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Sync remote CRM objects.';
/**
* Execute the console command.
*/
public function handle(): void
{
$teamId = $this->argument('team');
$teams = [];
if ($teamId) {
$team = Team::idOrUuId($teamId);
if ($team && $team->crm?->provider === Configuration::PROVIDER_HUBSPOT) {
$this->error(sprintf(
'Team %s (%s) uses HubSpot. Use crm:sync-hubspot-objects instead.',
$team->getName(),
$team->getUuid()
));
return;
}
if ($team) {
$teams[] = $team;
}
} else {
// Exclude HubSpot teams - handled by crm:sync-hubspot-objects
$teams = Team::where('status', Team::STATUS_ACTIVE)
->whereHas(
'crm',
fn ($q) => $q
->where('provider', '!=', Configuration::PROVIDER_HUBSPOT)
->where('sync_objects', true)
)
->get();
}
$this->dispatchSyncJobsForTeams($teams);
}
protected function getStaggerDelaySeconds(): float
{
return 2;
}
protected function getMaxDelaySeconds(): int
{
// Cap delay at 15 minutes (SQS max delay limit)
// capacity of 450 teams before reach
return 900;
}
protected function getLogPrefix(): string
{
return '';
}
protected function createSyncJob(Team $team): Job
{
return new SyncObjectsJob($team);
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
1
51
1
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Console\Commands\Crm;
use Carbon\Carbon;
use Exception;
use Illuminate\Console\Command;
use Jiminny\Component\Encryption\EncryptedTokenManager;
use Jiminny\Models\SocialAccount;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Pipedrive\versions\v1\Api\ActivitiesApi;
use Pipedrive\versions\v1\Api\ActivityFieldsApi;
use Pipedrive\versions\v1\Api\DealFieldsApi;
use Pipedrive\versions\v1\Api\DealsApi;
use Pipedrive\versions\v1\Api\PersonFieldsApi;
use Pipedrive\versions\v1\Api\PersonsApi;
use Pipedrive\versions\v1\Configuration;
class TestPipedriveOfficialSdkCommand extends Command
{
protected $signature = 'jiminny:test-pipedrive-official-sdk {teamId}';
protected $description = 'Test official Pipedrive SDK (pipedrive/pipedrive) with existing OAuth tokens';
private array $results = [];
private EncryptedTokenManager $tokenManager;
public function __construct(EncryptedTokenManager $tokenManager)
{
parent::__construct();
$this->tokenManager = $tokenManager;
}
public function handle(): int
{
$teamId = (int) $this->argument('teamId');
$this->info("Starting Pipedrive Official SDK POC for Team ID: {$teamId}");
$this->newLine();
try {
$this->runTests($teamId);
} catch (Exception $e) {
$this->error("POC failed with error: {$e->getMessage()}");
$this->error($e->getTraceAsString());
return 1;
}
$this->displayResults();
return 0;
}
private function runTests(int $teamId): void
{
$team = Team::find($teamId);
if (! $team) {
throw new Exception("Team with ID {$teamId} not found");
}
$socialAccount = SocialAccount::where('provider', SocialAccount::PROVIDER_PIPEDRIVE)
->whereHas('sociable', function ($query) use ($team) {
$query->where('team_id', $team->id);
})
->orderByDesc('expires') // Get the most recently expiring token
->first();
if (! $socialAccount) {
throw new Exception("No Pipedrive social account found for team {$teamId}");
}
$this->info("Found Pipedrive account for team: {$team->name}");
$this->newLine();
// Test 1: OAuth Initialization
$this->testOAuthInitialization($socialAccount);
// Test 2: Basic CRUD Operations
$this->testBasicCrudOperations($socialAccount);
// Test 3: Field Metadata
$this->testFieldMetadata($socialAccount);
// Test 4: v2 API Support
$this->testV2ApiSupport($socialAccount);
// Test 5: Error Handling
$this->testErrorHandling($socialAccount);
}
private function testOAuthInitialization(SocialAccount $socialAccount): void
{
$this->info('Test 1: OAuth Initialization');
$startTime = microtime(true);
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
$client = new DealsApi(null, $config);
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('OAuth Initialization', true, "Client initialized successfully in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('OAuth Initialization', false, $e->getMessage(), $elapsed);
}
$this->newLine();
}
private function testBasicCrudOperations(SocialAccount $socialAccount): void
{
$this->info('Test 2: Basic CRUD Operations');
try {
// Get the raw token from database
$rawDbToken = $socialAccount->getProviderUserToken();
$this->info("DB token (length: " . strlen($rawDbToken) . ")");
$this->info("DB token preview: " . substr($rawDbToken, 0, 20) . "...");
// Check token expiry
$expires = $socialAccount->expires;
$this->info("Token expires at: " . ($expires ? Carbon::createFromTimestamp($expires)->toDateTimeString() : 'null'));
$this->info("Token is expired: " . ($expires && $expires < time() ? 'YES' : 'NO'));
// Test with the DB token directly
$this->info("\n--- Testing with DB token ---");
$config = new Configuration();
$config->setAccessToken($rawDbToken);
// Test 2a: Get Deals
$this->testGetDeals($config);
// Test 2b: Get Persons
$this->testGetPersons($config);
// Test 2c: Get Activities
$this->testGetActivities($config);
} catch (Exception $e) {
$this->recordResult('Basic CRUD Operations', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testCurrentSystemApproach(SocialAccount $socialAccount): void
{
$startTime = microtime(true);
try {
// Simulate how current system makes requests
$token = $socialAccount->getProviderUserToken();
$url = 'https://api.pipedrive.com/api/v2/deals';
$headers = [
'Authorization' => 'Bearer ' . $token,
'Accept' => 'application/json',
];
$client = new \GuzzleHttp\Client();
$response = $client->get($url, [
'headers' => $headers,
'timeout' => 30,
]);
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$statusCode = $response->getStatusCode();
$this->recordResult('Current System v2 API', $statusCode === 200, "Status: {$statusCode} in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Current System v2 API', false, $e->getMessage(), $elapsed);
}
}
private function testGetDeals(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new DealsApi(null, $config);
// Official SDK uses getDealsSummary instead of getDeals
$result = $apiInstance->getDealsSummary();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Deals Summary', true, "Retrieved deals summary in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Deals Summary', false, $e->getMessage(), $elapsed);
}
}
private function testGetPersons(Configuration $config): void
{
$startTime = microtime(true);
try {
// Official SDK doesn't have a simple getPersons method
// Skip this test for now as the API structure is different
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Persons', false, "Method not available in official SDK - API structure different", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Persons', false, $e->getMessage(), $elapsed);
}
}
private function testGetActivities(Configuration $config): void
{
$startTime = microtime(true);
try {
// Official SDK doesn't have a simple getActivities method
// Skip this test for now as the API structure is different
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Activities', false, "Method not available in official SDK - API structure different", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Activities', false, $e->getMessage(), $elapsed);
}
}
private function testFieldMetadata(SocialAccount $socialAccount): void
{
$this->info('Test 3: Field Metadata');
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
// Test 3a: Get Deal Fields
$this->testGetDealFields($config);
// Test 3b: Get Person Fields
$this->testGetPersonFields($config);
// Test 3c: Get Activity Fields
$this->testGetActivityFields($config);
} catch (Exception $e) {
$this->recordResult('Field Metadata', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testGetDealFields(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new DealFieldsApi(null, $config);
$result = $apiInstance->getDealFields();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$count = $result->getData() ? count($result->getData()) : 0;
$this->recordResult('Get Deal Fields', true, "Retrieved {$count} deal fields in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Deal Fields', false, $e->getMessage(), $elapsed);
}
}
private function testGetPersonFields(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new PersonFieldsApi(null, $config);
$result = $apiInstance->getPersonFields();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$count = $result->getData() ? count($result->getData()) : 0;
$this->recordResult('Get Person Fields', true, "Retrieved {$count} person fields in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Person Fields', false, $e->getMessage(), $elapsed);
}
}
private function testGetActivityFields(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new ActivityFieldsApi(null, $config);
$result = $apiInstance->getActivityFields();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$count = $result->getData() ? count($result->getData()) : 0;
$this->recordResult('Get Activity Fields', true, "Retrieved {$count} activity fields in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Activity Fields', false, $e->getMessage(), $elapsed);
}
}
private function testV2ApiSupport(SocialAccount $socialAccount): void
{
$this->info('Test 4: v2 API Support (Cursor-based Pagination)');
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
$startTime = microtime(true);
// Test v2 deals endpoint with cursor pagination
// Note: The official SDK may or may not have v2 API support
// This test will determine if v2 is available
try {
$apiInstance = new DealsApi(null, $config);
$updatedSince = Carbon::now()->subDays(30)->toRfc3339String();
$result = $apiInstance->getDealsSummary();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
// Check if response has cursor information (v2 indicator)
// The response object doesn't have getAdditionalData method
// Just test if the call works - v2 API support is limited in official SDK v1
$this->recordResult('v2 API Support', false, "v1 API works but v2 cursor pagination not available in official SDK v1. Retrieved summary in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('v2 API Support', false, $e->getMessage(), $elapsed);
}
} catch (Exception $e) {
$this->recordResult('v2 API Support', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testErrorHandling(SocialAccount $socialAccount): void
{
$this->info('Test 5: Error Handling');
try {
// Test 5a: Invalid Token
$this->testInvalidToken();
// Test 5b: Non-existent Resource
$this->testNonExistentResource($socialAccount);
} catch (Exception $e) {
$this->recordResult('Error Handling', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testInvalidToken(): void
{
$startTime = microtime(true);
try {
$config = new Configuration();
$config->setAccessToken('invalid_token');
$client = new DealsApi(null, $config);
$client->getDealsSummary();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Invalid Token Error', false, "Expected error but request succeeded in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Invalid Token Error', true, "Correctly threw exception: {$e->getMessage()} in {$elapsed}ms", $elapsed);
}
}
private function testNonExistentResource(SocialAccount $socialAccount): void
{
$startTime = microtime(true);
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
$client = new DealsApi(null, $config);
// Try to get a deal with a very high ID that likely doesn't exist
// Note: Official SDK doesn't have a simple getDeal method, so skip this
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Non-existent Resource Error', false, "Method not available in official SDK - API structure different", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Non-existent Resource Error', true, "Correctly threw exception: {$e->getMessage()} in {$elapsed}ms", $elapsed);
}
}
private function recordResult(string $testName, bool $success, string $message, float $elapsed): void
{
$this->results[] = [
'test' => $testName,
'success' => $success,
'message' => $message,
'elapsed' => $elapsed,
];
$status = $success ? '✅ PASS' : '❌ FAIL';
$this->line(" {$status} - {$testName}: {$message}");
}
private function displayResults(): void
{
$this->newLine();
$this->info('=== POC Test Results Summary ===');
$this->newLine();
$total = count($this->results);
$passed = count(array_filter($this->results, fn ($r) => $r['success']));
$failed = $total - $passed;
$this->line("Total Tests: {$total}");
$this->line("Passed: {$passed}");
$this->line("Failed: {$failed}");
$this->newLine();
$totalTime = array_sum(array_column($this->results, 'elapsed'));
$this->line("Total Execution Time: {$totalTime}ms");
$this->newLine();
if ($failed > 0) {
$this->warn('Failed Tests:');
foreach ($this->results as $result) {
if (! $result['success']) {
$this->line(" - {$result['test']}: {$result['message']}");
}
}
$this->newLine();
}
$this->info('=== Detailed Results ===');
foreach ($this->results as $result) {
$status = $result['success'] ? '✅' : '❌';
$this->line("{$status} {$result['test']} ({$result['elapsed']}ms): {$result['message']}");
}
$this->newLine();
$this->info('=== Recommendations ===');
if ($passed === $total) {
$this->info('✅ All tests passed! The official SDK is viable for migration.');
$this->info('Next steps: Proceed with full migration planning.');
} else {
$this->warn('⚠️ Some tests failed. Review the failures above before proceeding.');
$this->warn('Consider hybrid approach or investigate alternatives if critical operations fail.');
}
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
49398
|
NULL
|
NULL
|
NULL
|
|
49402
|
1763
|
26
|
2026-05-18T06:41:47.716461+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779086507716_m2.jpg...
|
PhpStorm
|
faVsco.js – TestPipedriveOfficialSdkCommand.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
pipedrive-sdk-poc, menu
S Project: faVsco.js, menu
pipedrive-sdk-poc, 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\Console\Commands\Crm;
use Jiminny\Console\Commands\Command;
use Jiminny\Console\Commands\Crm\Traits\SyncObjectsCommandTrait;
use Jiminny\Jobs\Crm\SyncObjects as SyncObjectsJob;
use Jiminny\Jobs\Job;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Models\Team;
class SyncObjects extends Command
{
use SyncObjectsCommandTrait;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'crm:sync-objects {team?} {--sync}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Sync remote CRM objects.';
/**
* Execute the console command.
*/
public function handle(): void
{
$teamId = $this->argument('team');
$teams = [];
if ($teamId) {
$team = Team::idOrUuId($teamId);
if ($team && $team->crm?->provider === Configuration::PROVIDER_HUBSPOT) {
$this->error(sprintf(
'Team %s (%s) uses HubSpot. Use crm:sync-hubspot-objects instead.',
$team->getName(),
$team->getUuid()
));
return;
}
if ($team) {
$teams[] = $team;
}
} else {
// Exclude HubSpot teams - handled by crm:sync-hubspot-objects
$teams = Team::where('status', Team::STATUS_ACTIVE)
->whereHas(
'crm',
fn ($q) => $q
->where('provider', '!=', Configuration::PROVIDER_HUBSPOT)
->where('sync_objects', true)
)
->get();
}
$this->dispatchSyncJobsForTeams($teams);
}
protected function getStaggerDelaySeconds(): float
{
return 2;
}
protected function getMaxDelaySeconds(): int
{
// Cap delay at 15 minutes (SQS max delay limit)
// capacity of 450 teams before reach
return 900;
}
protected function getLogPrefix(): string
{
return '';
}
protected function createSyncJob(Team $team): Job
{
return new SyncObjectsJob($team);
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
1
51
1
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Console\Commands\Crm;
use Carbon\Carbon;
use Exception;
use Illuminate\Console\Command;
use Jiminny\Component\Encryption\EncryptedTokenManager;
use Jiminny\Models\SocialAccount;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Pipedrive\versions\v1\Api\ActivitiesApi;
use Pipedrive\versions\v1\Api\ActivityFieldsApi;
use Pipedrive\versions\v1\Api\DealFieldsApi;
use Pipedrive\versions\v1\Api\DealsApi;
use Pipedrive\versions\v1\Api\PersonFieldsApi;
use Pipedrive\versions\v1\Api\PersonsApi;
use Pipedrive\versions\v1\Configuration;
class TestPipedriveOfficialSdkCommand extends Command
{
protected $signature = 'jiminny:test-pipedrive-official-sdk {teamId}';
protected $description = 'Test official Pipedrive SDK (pipedrive/pipedrive) with existing OAuth tokens';
private array $results = [];
private EncryptedTokenManager $tokenManager;
public function __construct(EncryptedTokenManager $tokenManager)
{
parent::__construct();
$this->tokenManager = $tokenManager;
}
public function handle(): int
{
$teamId = (int) $this->argument('teamId');
$this->info("Starting Pipedrive Official SDK POC for Team ID: {$teamId}");
$this->newLine();
try {
$this->runTests($teamId);
} catch (Exception $e) {
$this->error("POC failed with error: {$e->getMessage()}");
$this->error($e->getTraceAsString());
return 1;
}
$this->displayResults();
return 0;
}
private function runTests(int $teamId): void
{
$team = Team::find($teamId);
if (! $team) {
throw new Exception("Team with ID {$teamId} not found");
}
$socialAccount = SocialAccount::where('provider', SocialAccount::PROVIDER_PIPEDRIVE)
->whereHas('sociable', function ($query) use ($team) {
$query->where('team_id', $team->id);
})
->orderByDesc('expires') // Get the most recently expiring token
->first();
if (! $socialAccount) {
throw new Exception("No Pipedrive social account found for team {$teamId}");
}
$this->info("Found Pipedrive account for team: {$team->name}");
$this->newLine();
// Test 1: OAuth Initialization
$this->testOAuthInitialization($socialAccount);
// Test 2: Basic CRUD Operations
$this->testBasicCrudOperations($socialAccount);
// Test 3: Field Metadata
$this->testFieldMetadata($socialAccount);
// Test 4: v2 API Support
$this->testV2ApiSupport($socialAccount);
// Test 5: Error Handling
$this->testErrorHandling($socialAccount);
}
private function testOAuthInitialization(SocialAccount $socialAccount): void
{
$this->info('Test 1: OAuth Initialization');
$startTime = microtime(true);
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
$client = new DealsApi(null, $config);
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('OAuth Initialization', true, "Client initialized successfully in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('OAuth Initialization', false, $e->getMessage(), $elapsed);
}
$this->newLine();
}
private function testBasicCrudOperations(SocialAccount $socialAccount): void
{
$this->info('Test 2: Basic CRUD Operations');
try {
// Get the raw token from database
$rawDbToken = $socialAccount->getProviderUserToken();
$this->info("DB token (length: " . strlen($rawDbToken) . ")");
$this->info("DB token preview: " . substr($rawDbToken, 0, 20) . "...");
// Check token expiry
$expires = $socialAccount->expires;
$this->info("Token expires at: " . ($expires ? Carbon::createFromTimestamp($expires)->toDateTimeString() : 'null'));
$this->info("Token is expired: " . ($expires && $expires < time() ? 'YES' : 'NO'));
// Test with the DB token directly
$this->info("\n--- Testing with DB token ---");
$config = new Configuration();
$config->setAccessToken($rawDbToken);
// Test 2a: Get Deals
$this->testGetDeals($config);
// Test 2b: Get Persons
$this->testGetPersons($config);
// Test 2c: Get Activities
$this->testGetActivities($config);
} catch (Exception $e) {
$this->recordResult('Basic CRUD Operations', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testCurrentSystemApproach(SocialAccount $socialAccount): void
{
$startTime = microtime(true);
try {
// Simulate how current system makes requests
$token = $socialAccount->getProviderUserToken();
$url = 'https://api.pipedrive.com/api/v2/deals';
$headers = [
'Authorization' => 'Bearer ' . $token,
'Accept' => 'application/json',
];
$client = new \GuzzleHttp\Client();
$response = $client->get($url, [
'headers' => $headers,
'timeout' => 30,
]);
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$statusCode = $response->getStatusCode();
$this->recordResult('Current System v2 API', $statusCode === 200, "Status: {$statusCode} in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Current System v2 API', false, $e->getMessage(), $elapsed);
}
}
private function testGetDeals(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new DealsApi(null, $config);
// Official SDK uses getDealsSummary instead of getDeals
$result = $apiInstance->getDealsSummary();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Deals Summary', true, "Retrieved deals summary in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Deals Summary', false, $e->getMessage(), $elapsed);
}
}
private function testGetPersons(Configuration $config): void
{
$startTime = microtime(true);
try {
// Official SDK doesn't have a simple getPersons method
// Skip this test for now as the API structure is different
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Persons', false, "Method not available in official SDK - API structure different", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Persons', false, $e->getMessage(), $elapsed);
}
}
private function testGetActivities(Configuration $config): void
{
$startTime = microtime(true);
try {
// Official SDK doesn't have a simple getActivities method
// Skip this test for now as the API structure is different
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Activities', false, "Method not available in official SDK - API structure different", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Activities', false, $e->getMessage(), $elapsed);
}
}
private function testFieldMetadata(SocialAccount $socialAccount): void
{
$this->info('Test 3: Field Metadata');
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
// Test 3a: Get Deal Fields
$this->testGetDealFields($config);
// Test 3b: Get Person Fields
$this->testGetPersonFields($config);
// Test 3c: Get Activity Fields
$this->testGetActivityFields($config);
} catch (Exception $e) {
$this->recordResult('Field Metadata', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testGetDealFields(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new DealFieldsApi(null, $config);
$result = $apiInstance->getDealFields();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$count = $result->getData() ? count($result->getData()) : 0;
$this->recordResult('Get Deal Fields', true, "Retrieved {$count} deal fields in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Deal Fields', false, $e->getMessage(), $elapsed);
}
}
private function testGetPersonFields(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new PersonFieldsApi(null, $config);
$result = $apiInstance->getPersonFields();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$count = $result->getData() ? count($result->getData()) : 0;
$this->recordResult('Get Person Fields', true, "Retrieved {$count} person fields in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Person Fields', false, $e->getMessage(), $elapsed);
}
}
private function testGetActivityFields(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new ActivityFieldsApi(null, $config);
$result = $apiInstance->getActivityFields();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$count = $result->getData() ? count($result->getData()) : 0;
$this->recordResult('Get Activity Fields', true, "Retrieved {$count} activity fields in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Activity Fields', false, $e->getMessage(), $elapsed);
}
}
private function testV2ApiSupport(SocialAccount $socialAccount): void
{
$this->info('Test 4: v2 API Support (Cursor-based Pagination)');
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
$startTime = microtime(true);
// Test v2 deals endpoint with cursor pagination
// Note: The official SDK may or may not have v2 API support
// This test will determine if v2 is available
try {
$apiInstance = new DealsApi(null, $config);
$updatedSince = Carbon::now()->subDays(30)->toRfc3339String();
$result = $apiInstance->getDealsSummary();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
// Check if response has cursor information (v2 indicator)
// The response object doesn't have getAdditionalData method
// Just test if the call works - v2 API support is limited in official SDK v1
$this->recordResult('v2 API Support', false, "v1 API works but v2 cursor pagination not available in official SDK v1. Retrieved summary in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('v2 API Support', false, $e->getMessage(), $elapsed);
}
} catch (Exception $e) {
$this->recordResult('v2 API Support', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testErrorHandling(SocialAccount $socialAccount): void
{
$this->info('Test 5: Error Handling');
try {
// Test 5a: Invalid Token
$this->testInvalidToken();
// Test 5b: Non-existent Resource
$this->testNonExistentResource($socialAccount);
} catch (Exception $e) {
$this->recordResult('Error Handling', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testInvalidToken(): void
{
$startTime = microtime(true);
try {
$config = new Configuration();
$config->setAccessToken('invalid_token');
$client = new DealsApi(null, $config);
$client->getDealsSummary();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Invalid Token Error', false, "Expected error but request succeeded in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Invalid Token Error', true, "Correctly threw exception: {$e->getMessage()} in {$elapsed}ms", $elapsed);
}
}
private function testNonExistentResource(SocialAccount $socialAccount): void
{
$startTime = microtime(true);
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
$client = new DealsApi(null, $config);
// Try to get a deal with a very high ID that likely doesn't exist
// Note: Official SDK doesn't have a simple getDeal method, so skip this
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Non-existent Resource Error', false, "Method not available in official SDK - API structure different", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Non-existent Resource Error', true, "Correctly threw exception: {$e->getMessage()} in {$elapsed}ms", $elapsed);
}
}
private function recordResult(string $testName, bool $success, string $message, float $elapsed): void
{
$this->results[] = [
'test' => $testName,
'success' => $success,
'message' => $message,
'elapsed' => $elapsed,
];
$status = $success ? '✅ PASS' : '❌ FAIL';
$this->line(" {$status} - {$testName}: {$message}");
}
private function displayResults(): void
{
$this->newLine();
$this->info('=== POC Test Results Summary ===');
$this->newLine();
$total = count($this->results);
$passed = count(array_filter($this->results, fn ($r) => $r['success']));
$failed = $total - $passed;
$this->line("Total Tests: {$total}");
$this->line("Passed: {$passed}");
$this->line("Failed: {$failed}");
$this->newLine();
$totalTime = array_sum(array_column($this->results, 'elapsed'));
$this->line("Total Execution Time: {$totalTime}ms");
$this->newLine();
if ($failed > 0) {
$this->warn('Failed Tests:');
foreach ($this->results as $result) {
if (! $result['success']) {
$this->line(" - {$result['test']}: {$result['message']}");
}
}
$this->newLine();
}
$this->info('=== Detailed Results ===');
foreach ($this->results as $result) {
$status = $result['success'] ? '✅' : '❌';
$this->line("{$status} {$result['test']} ({$result['elapsed']}ms): {$result['message']}");
}
$this->newLine();
$this->info('=== Recommendations ===');
if ($passed === $total) {
$this->info('✅ All tests passed! The official SDK is viable for migration.');
$this->info('Next steps: Proceed with full migration planning.');
} else {
$this->warn('⚠️ Some tests failed. Review the failures above before proceeding.');
$this->warn('Consider hybrid approach or investigate alternatives if critical operations fail.');
}
}
}
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":"pipedrive-sdk-poc, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.05618351,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: pipedrive-sdk-poc","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\\Console\\Commands\\Crm;\n\nuse Jiminny\\Console\\Commands\\Command;\nuse Jiminny\\Console\\Commands\\Crm\\Traits\\SyncObjectsCommandTrait;\nuse Jiminny\\Jobs\\Crm\\SyncObjects as SyncObjectsJob;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Models\\Team;\n\nclass SyncObjects extends Command\n{\n use SyncObjectsCommandTrait;\n\n /**\n * The name and signature of the console command.\n *\n * @var string\n */\n protected $signature = 'crm:sync-objects {team?} {--sync}';\n\n /**\n * The console command description.\n *\n * @var string\n */\n protected $description = 'Sync remote CRM objects.';\n\n /**\n * Execute the console command.\n */\n public function handle(): void\n {\n $teamId = $this->argument('team');\n $teams = [];\n\n if ($teamId) {\n $team = Team::idOrUuId($teamId);\n if ($team && $team->crm?->provider === Configuration::PROVIDER_HUBSPOT) {\n $this->error(sprintf(\n 'Team %s (%s) uses HubSpot. Use crm:sync-hubspot-objects instead.',\n $team->getName(),\n $team->getUuid()\n ));\n\n return;\n }\n if ($team) {\n $teams[] = $team;\n }\n } else {\n // Exclude HubSpot teams - handled by crm:sync-hubspot-objects\n $teams = Team::where('status', Team::STATUS_ACTIVE)\n ->whereHas(\n 'crm',\n fn ($q) => $q\n ->where('provider', '!=', Configuration::PROVIDER_HUBSPOT)\n ->where('sync_objects', true)\n )\n ->get();\n }\n\n $this->dispatchSyncJobsForTeams($teams);\n }\n\n protected function getStaggerDelaySeconds(): float\n {\n return 2;\n }\n\n protected function getMaxDelaySeconds(): int\n {\n // Cap delay at 15 minutes (SQS max delay limit)\n // capacity of 450 teams before reach\n return 900;\n }\n\n protected function getLogPrefix(): string\n {\n return '';\n }\n\n protected function createSyncJob(Team $team): Job\n {\n return new SyncObjectsJob($team);\n }\n}","depth":4,"bounds":{"left":0.15724733,"top":0.2952913,"width":0.21476063,"height":0.7047087},"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Console\\Commands\\Crm;\n\nuse Jiminny\\Console\\Commands\\Command;\nuse Jiminny\\Console\\Commands\\Crm\\Traits\\SyncObjectsCommandTrait;\nuse Jiminny\\Jobs\\Crm\\SyncObjects as SyncObjectsJob;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Models\\Team;\n\nclass SyncObjects extends Command\n{\n use SyncObjectsCommandTrait;\n\n /**\n * The name and signature of the console command.\n *\n * @var string\n */\n protected $signature = 'crm:sync-objects {team?} {--sync}';\n\n /**\n * The console command description.\n *\n * @var string\n */\n protected $description = 'Sync remote CRM objects.';\n\n /**\n * Execute the console command.\n */\n public function handle(): void\n {\n $teamId = $this->argument('team');\n $teams = [];\n\n if ($teamId) {\n $team = Team::idOrUuId($teamId);\n if ($team && $team->crm?->provider === Configuration::PROVIDER_HUBSPOT) {\n $this->error(sprintf(\n 'Team %s (%s) uses HubSpot. Use crm:sync-hubspot-objects instead.',\n $team->getName(),\n $team->getUuid()\n ));\n\n return;\n }\n if ($team) {\n $teams[] = $team;\n }\n } else {\n // Exclude HubSpot teams - handled by crm:sync-hubspot-objects\n $teams = Team::where('status', Team::STATUS_ACTIVE)\n ->whereHas(\n 'crm',\n fn ($q) => $q\n ->where('provider', '!=', Configuration::PROVIDER_HUBSPOT)\n ->where('sync_objects', true)\n )\n ->get();\n }\n\n $this->dispatchSyncJobsForTeams($teams);\n }\n\n protected function getStaggerDelaySeconds(): float\n {\n return 2;\n }\n\n protected function getMaxDelaySeconds(): int\n {\n // Cap delay at 15 minutes (SQS max delay limit)\n // capacity of 450 teams before reach\n return 900;\n }\n\n protected function getLogPrefix(): string\n {\n return '';\n }\n\n protected function createSyncJob(Team $team): Job\n {\n return new SyncObjectsJob($team);\n }\n}","role_description":"text entry area","is_enabled":true,"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":"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":"AXStaticText","text":"1","depth":4,"bounds":{"left":0.57912236,"top":0.10055866,"width":0.00731383,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"51","depth":4,"bounds":{"left":0.5884308,"top":0.10055866,"width":0.009640957,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"1","depth":4,"bounds":{"left":0.6000665,"top":0.10055866,"width":0.00731383,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.6090425,"top":0.09896249,"width":0.00731383,"height":0.018355945},"on_screen":true,"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.6163564,"top":0.09896249,"width":0.006981383,"height":0.018355945},"on_screen":true,"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\\Console\\Commands\\Crm;\n\nuse Carbon\\Carbon;\nuse Exception;\nuse Illuminate\\Console\\Command;\nuse Jiminny\\Component\\Encryption\\EncryptedTokenManager;\nuse Jiminny\\Models\\SocialAccount;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Pipedrive\\versions\\v1\\Api\\ActivitiesApi;\nuse Pipedrive\\versions\\v1\\Api\\ActivityFieldsApi;\nuse Pipedrive\\versions\\v1\\Api\\DealFieldsApi;\nuse Pipedrive\\versions\\v1\\Api\\DealsApi;\nuse Pipedrive\\versions\\v1\\Api\\PersonFieldsApi;\nuse Pipedrive\\versions\\v1\\Api\\PersonsApi;\nuse Pipedrive\\versions\\v1\\Configuration;\n\nclass TestPipedriveOfficialSdkCommand extends Command\n{\n protected $signature = 'jiminny:test-pipedrive-official-sdk {teamId}';\n\n protected $description = 'Test official Pipedrive SDK (pipedrive/pipedrive) with existing OAuth tokens';\n\n private array $results = [];\n\n private EncryptedTokenManager $tokenManager;\n\n public function __construct(EncryptedTokenManager $tokenManager)\n {\n parent::__construct();\n $this->tokenManager = $tokenManager;\n }\n\n public function handle(): int\n {\n $teamId = (int) $this->argument('teamId');\n\n $this->info(\"Starting Pipedrive Official SDK POC for Team ID: {$teamId}\");\n $this->newLine();\n\n try {\n $this->runTests($teamId);\n } catch (Exception $e) {\n $this->error(\"POC failed with error: {$e->getMessage()}\");\n $this->error($e->getTraceAsString());\n\n return 1;\n }\n\n $this->displayResults();\n\n return 0;\n }\n\n private function runTests(int $teamId): void\n {\n $team = Team::find($teamId);\n if (! $team) {\n throw new Exception(\"Team with ID {$teamId} not found\");\n }\n\n $socialAccount = SocialAccount::where('provider', SocialAccount::PROVIDER_PIPEDRIVE)\n ->whereHas('sociable', function ($query) use ($team) {\n $query->where('team_id', $team->id);\n })\n ->orderByDesc('expires') // Get the most recently expiring token\n ->first();\n\n if (! $socialAccount) {\n throw new Exception(\"No Pipedrive social account found for team {$teamId}\");\n }\n\n $this->info(\"Found Pipedrive account for team: {$team->name}\");\n $this->newLine();\n\n // Test 1: OAuth Initialization\n $this->testOAuthInitialization($socialAccount);\n\n // Test 2: Basic CRUD Operations\n $this->testBasicCrudOperations($socialAccount);\n\n // Test 3: Field Metadata\n $this->testFieldMetadata($socialAccount);\n\n // Test 4: v2 API Support\n $this->testV2ApiSupport($socialAccount);\n\n // Test 5: Error Handling\n $this->testErrorHandling($socialAccount);\n }\n\n private function testOAuthInitialization(SocialAccount $socialAccount): void\n {\n $this->info('Test 1: OAuth Initialization');\n $startTime = microtime(true);\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n\n $client = new DealsApi(null, $config);\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('OAuth Initialization', true, \"Client initialized successfully in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('OAuth Initialization', false, $e->getMessage(), $elapsed);\n }\n\n $this->newLine();\n }\n\n private function testBasicCrudOperations(SocialAccount $socialAccount): void\n {\n $this->info('Test 2: Basic CRUD Operations');\n\n try {\n // Get the raw token from database\n $rawDbToken = $socialAccount->getProviderUserToken();\n $this->info(\"DB token (length: \" . strlen($rawDbToken) . \")\");\n $this->info(\"DB token preview: \" . substr($rawDbToken, 0, 20) . \"...\");\n\n // Check token expiry\n $expires = $socialAccount->expires;\n $this->info(\"Token expires at: \" . ($expires ? Carbon::createFromTimestamp($expires)->toDateTimeString() : 'null'));\n $this->info(\"Token is expired: \" . ($expires && $expires < time() ? 'YES' : 'NO'));\n\n // Test with the DB token directly\n $this->info(\"\\n--- Testing with DB token ---\");\n $config = new Configuration();\n $config->setAccessToken($rawDbToken);\n\n // Test 2a: Get Deals\n $this->testGetDeals($config);\n\n // Test 2b: Get Persons\n $this->testGetPersons($config);\n\n // Test 2c: Get Activities\n $this->testGetActivities($config);\n } catch (Exception $e) {\n $this->recordResult('Basic CRUD Operations', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testCurrentSystemApproach(SocialAccount $socialAccount): void\n {\n $startTime = microtime(true);\n\n try {\n // Simulate how current system makes requests\n $token = $socialAccount->getProviderUserToken();\n $url = 'https://api.pipedrive.com/api/v2/deals';\n\n $headers = [\n 'Authorization' => 'Bearer ' . $token,\n 'Accept' => 'application/json',\n ];\n\n $client = new \\GuzzleHttp\\Client();\n $response = $client->get($url, [\n 'headers' => $headers,\n 'timeout' => 30,\n ]);\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $statusCode = $response->getStatusCode();\n $this->recordResult('Current System v2 API', $statusCode === 200, \"Status: {$statusCode} in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Current System v2 API', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetDeals(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new DealsApi(null, $config);\n // Official SDK uses getDealsSummary instead of getDeals\n $result = $apiInstance->getDealsSummary();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Deals Summary', true, \"Retrieved deals summary in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Deals Summary', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetPersons(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n // Official SDK doesn't have a simple getPersons method\n // Skip this test for now as the API structure is different\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Persons', false, \"Method not available in official SDK - API structure different\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Persons', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetActivities(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n // Official SDK doesn't have a simple getActivities method\n // Skip this test for now as the API structure is different\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Activities', false, \"Method not available in official SDK - API structure different\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Activities', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testFieldMetadata(SocialAccount $socialAccount): void\n {\n $this->info('Test 3: Field Metadata');\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n\n // Test 3a: Get Deal Fields\n $this->testGetDealFields($config);\n\n // Test 3b: Get Person Fields\n $this->testGetPersonFields($config);\n\n // Test 3c: Get Activity Fields\n $this->testGetActivityFields($config);\n } catch (Exception $e) {\n $this->recordResult('Field Metadata', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testGetDealFields(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new DealFieldsApi(null, $config);\n $result = $apiInstance->getDealFields();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $count = $result->getData() ? count($result->getData()) : 0;\n $this->recordResult('Get Deal Fields', true, \"Retrieved {$count} deal fields in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Deal Fields', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetPersonFields(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new PersonFieldsApi(null, $config);\n $result = $apiInstance->getPersonFields();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $count = $result->getData() ? count($result->getData()) : 0;\n $this->recordResult('Get Person Fields', true, \"Retrieved {$count} person fields in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Person Fields', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetActivityFields(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new ActivityFieldsApi(null, $config);\n $result = $apiInstance->getActivityFields();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $count = $result->getData() ? count($result->getData()) : 0;\n $this->recordResult('Get Activity Fields', true, \"Retrieved {$count} activity fields in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Activity Fields', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testV2ApiSupport(SocialAccount $socialAccount): void\n {\n $this->info('Test 4: v2 API Support (Cursor-based Pagination)');\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n\n $startTime = microtime(true);\n\n // Test v2 deals endpoint with cursor pagination\n // Note: The official SDK may or may not have v2 API support\n // This test will determine if v2 is available\n try {\n $apiInstance = new DealsApi(null, $config);\n $updatedSince = Carbon::now()->subDays(30)->toRfc3339String();\n $result = $apiInstance->getDealsSummary();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n\n // Check if response has cursor information (v2 indicator)\n // The response object doesn't have getAdditionalData method\n // Just test if the call works - v2 API support is limited in official SDK v1\n $this->recordResult('v2 API Support', false, \"v1 API works but v2 cursor pagination not available in official SDK v1. Retrieved summary in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('v2 API Support', false, $e->getMessage(), $elapsed);\n }\n } catch (Exception $e) {\n $this->recordResult('v2 API Support', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testErrorHandling(SocialAccount $socialAccount): void\n {\n $this->info('Test 5: Error Handling');\n\n try {\n // Test 5a: Invalid Token\n $this->testInvalidToken();\n\n // Test 5b: Non-existent Resource\n $this->testNonExistentResource($socialAccount);\n } catch (Exception $e) {\n $this->recordResult('Error Handling', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testInvalidToken(): void\n {\n $startTime = microtime(true);\n\n try {\n $config = new Configuration();\n $config->setAccessToken('invalid_token');\n $client = new DealsApi(null, $config);\n\n $client->getDealsSummary();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Invalid Token Error', false, \"Expected error but request succeeded in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Invalid Token Error', true, \"Correctly threw exception: {$e->getMessage()} in {$elapsed}ms\", $elapsed);\n }\n }\n\n private function testNonExistentResource(SocialAccount $socialAccount): void\n {\n $startTime = microtime(true);\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n $client = new DealsApi(null, $config);\n\n // Try to get a deal with a very high ID that likely doesn't exist\n // Note: Official SDK doesn't have a simple getDeal method, so skip this\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Non-existent Resource Error', false, \"Method not available in official SDK - API structure different\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Non-existent Resource Error', true, \"Correctly threw exception: {$e->getMessage()} in {$elapsed}ms\", $elapsed);\n }\n }\n\n private function recordResult(string $testName, bool $success, string $message, float $elapsed): void\n {\n $this->results[] = [\n 'test' => $testName,\n 'success' => $success,\n 'message' => $message,\n 'elapsed' => $elapsed,\n ];\n\n $status = $success ? '✅ PASS' : '❌ FAIL';\n $this->line(\" {$status} - {$testName}: {$message}\");\n }\n\n private function displayResults(): void\n {\n $this->newLine();\n $this->info('=== POC Test Results Summary ===');\n $this->newLine();\n\n $total = count($this->results);\n $passed = count(array_filter($this->results, fn ($r) => $r['success']));\n $failed = $total - $passed;\n\n $this->line(\"Total Tests: {$total}\");\n $this->line(\"Passed: {$passed}\");\n $this->line(\"Failed: {$failed}\");\n $this->newLine();\n\n $totalTime = array_sum(array_column($this->results, 'elapsed'));\n $this->line(\"Total Execution Time: {$totalTime}ms\");\n $this->newLine();\n\n if ($failed > 0) {\n $this->warn('Failed Tests:');\n foreach ($this->results as $result) {\n if (! $result['success']) {\n $this->line(\" - {$result['test']}: {$result['message']}\");\n }\n }\n $this->newLine();\n }\n\n $this->info('=== Detailed Results ===');\n foreach ($this->results as $result) {\n $status = $result['success'] ? '✅' : '❌';\n $this->line(\"{$status} {$result['test']} ({$result['elapsed']}ms): {$result['message']}\");\n }\n\n $this->newLine();\n $this->info('=== Recommendations ===');\n\n if ($passed === $total) {\n $this->info('✅ All tests passed! The official SDK is viable for migration.');\n $this->info('Next steps: Proceed with full migration planning.');\n } else {\n $this->warn('⚠️ Some tests failed. Review the failures above before proceeding.');\n $this->warn('Consider hybrid approach or investigate alternatives if critical operations fail.');\n }\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Console\\Commands\\Crm;\n\nuse Carbon\\Carbon;\nuse Exception;\nuse Illuminate\\Console\\Command;\nuse Jiminny\\Component\\Encryption\\EncryptedTokenManager;\nuse Jiminny\\Models\\SocialAccount;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Pipedrive\\versions\\v1\\Api\\ActivitiesApi;\nuse Pipedrive\\versions\\v1\\Api\\ActivityFieldsApi;\nuse Pipedrive\\versions\\v1\\Api\\DealFieldsApi;\nuse Pipedrive\\versions\\v1\\Api\\DealsApi;\nuse Pipedrive\\versions\\v1\\Api\\PersonFieldsApi;\nuse Pipedrive\\versions\\v1\\Api\\PersonsApi;\nuse Pipedrive\\versions\\v1\\Configuration;\n\nclass TestPipedriveOfficialSdkCommand extends Command\n{\n protected $signature = 'jiminny:test-pipedrive-official-sdk {teamId}';\n\n protected $description = 'Test official Pipedrive SDK (pipedrive/pipedrive) with existing OAuth tokens';\n\n private array $results = [];\n\n private EncryptedTokenManager $tokenManager;\n\n public function __construct(EncryptedTokenManager $tokenManager)\n {\n parent::__construct();\n $this->tokenManager = $tokenManager;\n }\n\n public function handle(): int\n {\n $teamId = (int) $this->argument('teamId');\n\n $this->info(\"Starting Pipedrive Official SDK POC for Team ID: {$teamId}\");\n $this->newLine();\n\n try {\n $this->runTests($teamId);\n } catch (Exception $e) {\n $this->error(\"POC failed with error: {$e->getMessage()}\");\n $this->error($e->getTraceAsString());\n\n return 1;\n }\n\n $this->displayResults();\n\n return 0;\n }\n\n private function runTests(int $teamId): void\n {\n $team = Team::find($teamId);\n if (! $team) {\n throw new Exception(\"Team with ID {$teamId} not found\");\n }\n\n $socialAccount = SocialAccount::where('provider', SocialAccount::PROVIDER_PIPEDRIVE)\n ->whereHas('sociable', function ($query) use ($team) {\n $query->where('team_id', $team->id);\n })\n ->orderByDesc('expires') // Get the most recently expiring token\n ->first();\n\n if (! $socialAccount) {\n throw new Exception(\"No Pipedrive social account found for team {$teamId}\");\n }\n\n $this->info(\"Found Pipedrive account for team: {$team->name}\");\n $this->newLine();\n\n // Test 1: OAuth Initialization\n $this->testOAuthInitialization($socialAccount);\n\n // Test 2: Basic CRUD Operations\n $this->testBasicCrudOperations($socialAccount);\n\n // Test 3: Field Metadata\n $this->testFieldMetadata($socialAccount);\n\n // Test 4: v2 API Support\n $this->testV2ApiSupport($socialAccount);\n\n // Test 5: Error Handling\n $this->testErrorHandling($socialAccount);\n }\n\n private function testOAuthInitialization(SocialAccount $socialAccount): void\n {\n $this->info('Test 1: OAuth Initialization');\n $startTime = microtime(true);\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n\n $client = new DealsApi(null, $config);\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('OAuth Initialization', true, \"Client initialized successfully in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('OAuth Initialization', false, $e->getMessage(), $elapsed);\n }\n\n $this->newLine();\n }\n\n private function testBasicCrudOperations(SocialAccount $socialAccount): void\n {\n $this->info('Test 2: Basic CRUD Operations');\n\n try {\n // Get the raw token from database\n $rawDbToken = $socialAccount->getProviderUserToken();\n $this->info(\"DB token (length: \" . strlen($rawDbToken) . \")\");\n $this->info(\"DB token preview: \" . substr($rawDbToken, 0, 20) . \"...\");\n\n // Check token expiry\n $expires = $socialAccount->expires;\n $this->info(\"Token expires at: \" . ($expires ? Carbon::createFromTimestamp($expires)->toDateTimeString() : 'null'));\n $this->info(\"Token is expired: \" . ($expires && $expires < time() ? 'YES' : 'NO'));\n\n // Test with the DB token directly\n $this->info(\"\\n--- Testing with DB token ---\");\n $config = new Configuration();\n $config->setAccessToken($rawDbToken);\n\n // Test 2a: Get Deals\n $this->testGetDeals($config);\n\n // Test 2b: Get Persons\n $this->testGetPersons($config);\n\n // Test 2c: Get Activities\n $this->testGetActivities($config);\n } catch (Exception $e) {\n $this->recordResult('Basic CRUD Operations', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testCurrentSystemApproach(SocialAccount $socialAccount): void\n {\n $startTime = microtime(true);\n\n try {\n // Simulate how current system makes requests\n $token = $socialAccount->getProviderUserToken();\n $url = 'https://api.pipedrive.com/api/v2/deals';\n\n $headers = [\n 'Authorization' => 'Bearer ' . $token,\n 'Accept' => 'application/json',\n ];\n\n $client = new \\GuzzleHttp\\Client();\n $response = $client->get($url, [\n 'headers' => $headers,\n 'timeout' => 30,\n ]);\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $statusCode = $response->getStatusCode();\n $this->recordResult('Current System v2 API', $statusCode === 200, \"Status: {$statusCode} in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Current System v2 API', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetDeals(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new DealsApi(null, $config);\n // Official SDK uses getDealsSummary instead of getDeals\n $result = $apiInstance->getDealsSummary();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Deals Summary', true, \"Retrieved deals summary in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Deals Summary', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetPersons(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n // Official SDK doesn't have a simple getPersons method\n // Skip this test for now as the API structure is different\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Persons', false, \"Method not available in official SDK - API structure different\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Persons', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetActivities(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n // Official SDK doesn't have a simple getActivities method\n // Skip this test for now as the API structure is different\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Activities', false, \"Method not available in official SDK - API structure different\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Activities', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testFieldMetadata(SocialAccount $socialAccount): void\n {\n $this->info('Test 3: Field Metadata');\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n\n // Test 3a: Get Deal Fields\n $this->testGetDealFields($config);\n\n // Test 3b: Get Person Fields\n $this->testGetPersonFields($config);\n\n // Test 3c: Get Activity Fields\n $this->testGetActivityFields($config);\n } catch (Exception $e) {\n $this->recordResult('Field Metadata', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testGetDealFields(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new DealFieldsApi(null, $config);\n $result = $apiInstance->getDealFields();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $count = $result->getData() ? count($result->getData()) : 0;\n $this->recordResult('Get Deal Fields', true, \"Retrieved {$count} deal fields in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Deal Fields', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetPersonFields(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new PersonFieldsApi(null, $config);\n $result = $apiInstance->getPersonFields();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $count = $result->getData() ? count($result->getData()) : 0;\n $this->recordResult('Get Person Fields', true, \"Retrieved {$count} person fields in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Person Fields', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetActivityFields(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new ActivityFieldsApi(null, $config);\n $result = $apiInstance->getActivityFields();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $count = $result->getData() ? count($result->getData()) : 0;\n $this->recordResult('Get Activity Fields', true, \"Retrieved {$count} activity fields in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Activity Fields', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testV2ApiSupport(SocialAccount $socialAccount): void\n {\n $this->info('Test 4: v2 API Support (Cursor-based Pagination)');\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n\n $startTime = microtime(true);\n\n // Test v2 deals endpoint with cursor pagination\n // Note: The official SDK may or may not have v2 API support\n // This test will determine if v2 is available\n try {\n $apiInstance = new DealsApi(null, $config);\n $updatedSince = Carbon::now()->subDays(30)->toRfc3339String();\n $result = $apiInstance->getDealsSummary();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n\n // Check if response has cursor information (v2 indicator)\n // The response object doesn't have getAdditionalData method\n // Just test if the call works - v2 API support is limited in official SDK v1\n $this->recordResult('v2 API Support', false, \"v1 API works but v2 cursor pagination not available in official SDK v1. Retrieved summary in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('v2 API Support', false, $e->getMessage(), $elapsed);\n }\n } catch (Exception $e) {\n $this->recordResult('v2 API Support', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testErrorHandling(SocialAccount $socialAccount): void\n {\n $this->info('Test 5: Error Handling');\n\n try {\n // Test 5a: Invalid Token\n $this->testInvalidToken();\n\n // Test 5b: Non-existent Resource\n $this->testNonExistentResource($socialAccount);\n } catch (Exception $e) {\n $this->recordResult('Error Handling', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testInvalidToken(): void\n {\n $startTime = microtime(true);\n\n try {\n $config = new Configuration();\n $config->setAccessToken('invalid_token');\n $client = new DealsApi(null, $config);\n\n $client->getDealsSummary();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Invalid Token Error', false, \"Expected error but request succeeded in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Invalid Token Error', true, \"Correctly threw exception: {$e->getMessage()} in {$elapsed}ms\", $elapsed);\n }\n }\n\n private function testNonExistentResource(SocialAccount $socialAccount): void\n {\n $startTime = microtime(true);\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n $client = new DealsApi(null, $config);\n\n // Try to get a deal with a very high ID that likely doesn't exist\n // Note: Official SDK doesn't have a simple getDeal method, so skip this\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Non-existent Resource Error', false, \"Method not available in official SDK - API structure different\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Non-existent Resource Error', true, \"Correctly threw exception: {$e->getMessage()} in {$elapsed}ms\", $elapsed);\n }\n }\n\n private function recordResult(string $testName, bool $success, string $message, float $elapsed): void\n {\n $this->results[] = [\n 'test' => $testName,\n 'success' => $success,\n 'message' => $message,\n 'elapsed' => $elapsed,\n ];\n\n $status = $success ? '✅ PASS' : '❌ FAIL';\n $this->line(\" {$status} - {$testName}: {$message}\");\n }\n\n private function displayResults(): void\n {\n $this->newLine();\n $this->info('=== POC Test Results Summary ===');\n $this->newLine();\n\n $total = count($this->results);\n $passed = count(array_filter($this->results, fn ($r) => $r['success']));\n $failed = $total - $passed;\n\n $this->line(\"Total Tests: {$total}\");\n $this->line(\"Passed: {$passed}\");\n $this->line(\"Failed: {$failed}\");\n $this->newLine();\n\n $totalTime = array_sum(array_column($this->results, 'elapsed'));\n $this->line(\"Total Execution Time: {$totalTime}ms\");\n $this->newLine();\n\n if ($failed > 0) {\n $this->warn('Failed Tests:');\n foreach ($this->results as $result) {\n if (! $result['success']) {\n $this->line(\" - {$result['test']}: {$result['message']}\");\n }\n }\n $this->newLine();\n }\n\n $this->info('=== Detailed Results ===');\n foreach ($this->results as $result) {\n $status = $result['success'] ? '✅' : '❌';\n $this->line(\"{$status} {$result['test']} ({$result['elapsed']}ms): {$result['message']}\");\n }\n\n $this->newLine();\n $this->info('=== Recommendations ===');\n\n if ($passed === $total) {\n $this->info('✅ All tests passed! The official SDK is viable for migration.');\n $this->info('Next steps: Proceed with full migration planning.');\n } else {\n $this->warn('⚠️ Some tests failed. Review the failures above before proceeding.');\n $this->warn('Consider hybrid approach or investigate alternatives if critical operations fail.');\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}]...
|
5365831007722038384
|
61370652661533572
|
idle
|
accessibility
|
NULL
|
Project: faVsco.js, menu
pipedrive-sdk-poc, menu
S Project: faVsco.js, menu
pipedrive-sdk-poc, 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\Console\Commands\Crm;
use Jiminny\Console\Commands\Command;
use Jiminny\Console\Commands\Crm\Traits\SyncObjectsCommandTrait;
use Jiminny\Jobs\Crm\SyncObjects as SyncObjectsJob;
use Jiminny\Jobs\Job;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Models\Team;
class SyncObjects extends Command
{
use SyncObjectsCommandTrait;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'crm:sync-objects {team?} {--sync}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Sync remote CRM objects.';
/**
* Execute the console command.
*/
public function handle(): void
{
$teamId = $this->argument('team');
$teams = [];
if ($teamId) {
$team = Team::idOrUuId($teamId);
if ($team && $team->crm?->provider === Configuration::PROVIDER_HUBSPOT) {
$this->error(sprintf(
'Team %s (%s) uses HubSpot. Use crm:sync-hubspot-objects instead.',
$team->getName(),
$team->getUuid()
));
return;
}
if ($team) {
$teams[] = $team;
}
} else {
// Exclude HubSpot teams - handled by crm:sync-hubspot-objects
$teams = Team::where('status', Team::STATUS_ACTIVE)
->whereHas(
'crm',
fn ($q) => $q
->where('provider', '!=', Configuration::PROVIDER_HUBSPOT)
->where('sync_objects', true)
)
->get();
}
$this->dispatchSyncJobsForTeams($teams);
}
protected function getStaggerDelaySeconds(): float
{
return 2;
}
protected function getMaxDelaySeconds(): int
{
// Cap delay at 15 minutes (SQS max delay limit)
// capacity of 450 teams before reach
return 900;
}
protected function getLogPrefix(): string
{
return '';
}
protected function createSyncJob(Team $team): Job
{
return new SyncObjectsJob($team);
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
1
51
1
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Console\Commands\Crm;
use Carbon\Carbon;
use Exception;
use Illuminate\Console\Command;
use Jiminny\Component\Encryption\EncryptedTokenManager;
use Jiminny\Models\SocialAccount;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Pipedrive\versions\v1\Api\ActivitiesApi;
use Pipedrive\versions\v1\Api\ActivityFieldsApi;
use Pipedrive\versions\v1\Api\DealFieldsApi;
use Pipedrive\versions\v1\Api\DealsApi;
use Pipedrive\versions\v1\Api\PersonFieldsApi;
use Pipedrive\versions\v1\Api\PersonsApi;
use Pipedrive\versions\v1\Configuration;
class TestPipedriveOfficialSdkCommand extends Command
{
protected $signature = 'jiminny:test-pipedrive-official-sdk {teamId}';
protected $description = 'Test official Pipedrive SDK (pipedrive/pipedrive) with existing OAuth tokens';
private array $results = [];
private EncryptedTokenManager $tokenManager;
public function __construct(EncryptedTokenManager $tokenManager)
{
parent::__construct();
$this->tokenManager = $tokenManager;
}
public function handle(): int
{
$teamId = (int) $this->argument('teamId');
$this->info("Starting Pipedrive Official SDK POC for Team ID: {$teamId}");
$this->newLine();
try {
$this->runTests($teamId);
} catch (Exception $e) {
$this->error("POC failed with error: {$e->getMessage()}");
$this->error($e->getTraceAsString());
return 1;
}
$this->displayResults();
return 0;
}
private function runTests(int $teamId): void
{
$team = Team::find($teamId);
if (! $team) {
throw new Exception("Team with ID {$teamId} not found");
}
$socialAccount = SocialAccount::where('provider', SocialAccount::PROVIDER_PIPEDRIVE)
->whereHas('sociable', function ($query) use ($team) {
$query->where('team_id', $team->id);
})
->orderByDesc('expires') // Get the most recently expiring token
->first();
if (! $socialAccount) {
throw new Exception("No Pipedrive social account found for team {$teamId}");
}
$this->info("Found Pipedrive account for team: {$team->name}");
$this->newLine();
// Test 1: OAuth Initialization
$this->testOAuthInitialization($socialAccount);
// Test 2: Basic CRUD Operations
$this->testBasicCrudOperations($socialAccount);
// Test 3: Field Metadata
$this->testFieldMetadata($socialAccount);
// Test 4: v2 API Support
$this->testV2ApiSupport($socialAccount);
// Test 5: Error Handling
$this->testErrorHandling($socialAccount);
}
private function testOAuthInitialization(SocialAccount $socialAccount): void
{
$this->info('Test 1: OAuth Initialization');
$startTime = microtime(true);
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
$client = new DealsApi(null, $config);
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('OAuth Initialization', true, "Client initialized successfully in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('OAuth Initialization', false, $e->getMessage(), $elapsed);
}
$this->newLine();
}
private function testBasicCrudOperations(SocialAccount $socialAccount): void
{
$this->info('Test 2: Basic CRUD Operations');
try {
// Get the raw token from database
$rawDbToken = $socialAccount->getProviderUserToken();
$this->info("DB token (length: " . strlen($rawDbToken) . ")");
$this->info("DB token preview: " . substr($rawDbToken, 0, 20) . "...");
// Check token expiry
$expires = $socialAccount->expires;
$this->info("Token expires at: " . ($expires ? Carbon::createFromTimestamp($expires)->toDateTimeString() : 'null'));
$this->info("Token is expired: " . ($expires && $expires < time() ? 'YES' : 'NO'));
// Test with the DB token directly
$this->info("\n--- Testing with DB token ---");
$config = new Configuration();
$config->setAccessToken($rawDbToken);
// Test 2a: Get Deals
$this->testGetDeals($config);
// Test 2b: Get Persons
$this->testGetPersons($config);
// Test 2c: Get Activities
$this->testGetActivities($config);
} catch (Exception $e) {
$this->recordResult('Basic CRUD Operations', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testCurrentSystemApproach(SocialAccount $socialAccount): void
{
$startTime = microtime(true);
try {
// Simulate how current system makes requests
$token = $socialAccount->getProviderUserToken();
$url = 'https://api.pipedrive.com/api/v2/deals';
$headers = [
'Authorization' => 'Bearer ' . $token,
'Accept' => 'application/json',
];
$client = new \GuzzleHttp\Client();
$response = $client->get($url, [
'headers' => $headers,
'timeout' => 30,
]);
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$statusCode = $response->getStatusCode();
$this->recordResult('Current System v2 API', $statusCode === 200, "Status: {$statusCode} in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Current System v2 API', false, $e->getMessage(), $elapsed);
}
}
private function testGetDeals(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new DealsApi(null, $config);
// Official SDK uses getDealsSummary instead of getDeals
$result = $apiInstance->getDealsSummary();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Deals Summary', true, "Retrieved deals summary in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Deals Summary', false, $e->getMessage(), $elapsed);
}
}
private function testGetPersons(Configuration $config): void
{
$startTime = microtime(true);
try {
// Official SDK doesn't have a simple getPersons method
// Skip this test for now as the API structure is different
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Persons', false, "Method not available in official SDK - API structure different", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Persons', false, $e->getMessage(), $elapsed);
}
}
private function testGetActivities(Configuration $config): void
{
$startTime = microtime(true);
try {
// Official SDK doesn't have a simple getActivities method
// Skip this test for now as the API structure is different
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Activities', false, "Method not available in official SDK - API structure different", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Activities', false, $e->getMessage(), $elapsed);
}
}
private function testFieldMetadata(SocialAccount $socialAccount): void
{
$this->info('Test 3: Field Metadata');
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
// Test 3a: Get Deal Fields
$this->testGetDealFields($config);
// Test 3b: Get Person Fields
$this->testGetPersonFields($config);
// Test 3c: Get Activity Fields
$this->testGetActivityFields($config);
} catch (Exception $e) {
$this->recordResult('Field Metadata', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testGetDealFields(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new DealFieldsApi(null, $config);
$result = $apiInstance->getDealFields();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$count = $result->getData() ? count($result->getData()) : 0;
$this->recordResult('Get Deal Fields', true, "Retrieved {$count} deal fields in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Deal Fields', false, $e->getMessage(), $elapsed);
}
}
private function testGetPersonFields(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new PersonFieldsApi(null, $config);
$result = $apiInstance->getPersonFields();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$count = $result->getData() ? count($result->getData()) : 0;
$this->recordResult('Get Person Fields', true, "Retrieved {$count} person fields in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Person Fields', false, $e->getMessage(), $elapsed);
}
}
private function testGetActivityFields(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new ActivityFieldsApi(null, $config);
$result = $apiInstance->getActivityFields();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$count = $result->getData() ? count($result->getData()) : 0;
$this->recordResult('Get Activity Fields', true, "Retrieved {$count} activity fields in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Activity Fields', false, $e->getMessage(), $elapsed);
}
}
private function testV2ApiSupport(SocialAccount $socialAccount): void
{
$this->info('Test 4: v2 API Support (Cursor-based Pagination)');
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
$startTime = microtime(true);
// Test v2 deals endpoint with cursor pagination
// Note: The official SDK may or may not have v2 API support
// This test will determine if v2 is available
try {
$apiInstance = new DealsApi(null, $config);
$updatedSince = Carbon::now()->subDays(30)->toRfc3339String();
$result = $apiInstance->getDealsSummary();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
// Check if response has cursor information (v2 indicator)
// The response object doesn't have getAdditionalData method
// Just test if the call works - v2 API support is limited in official SDK v1
$this->recordResult('v2 API Support', false, "v1 API works but v2 cursor pagination not available in official SDK v1. Retrieved summary in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('v2 API Support', false, $e->getMessage(), $elapsed);
}
} catch (Exception $e) {
$this->recordResult('v2 API Support', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testErrorHandling(SocialAccount $socialAccount): void
{
$this->info('Test 5: Error Handling');
try {
// Test 5a: Invalid Token
$this->testInvalidToken();
// Test 5b: Non-existent Resource
$this->testNonExistentResource($socialAccount);
} catch (Exception $e) {
$this->recordResult('Error Handling', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testInvalidToken(): void
{
$startTime = microtime(true);
try {
$config = new Configuration();
$config->setAccessToken('invalid_token');
$client = new DealsApi(null, $config);
$client->getDealsSummary();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Invalid Token Error', false, "Expected error but request succeeded in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Invalid Token Error', true, "Correctly threw exception: {$e->getMessage()} in {$elapsed}ms", $elapsed);
}
}
private function testNonExistentResource(SocialAccount $socialAccount): void
{
$startTime = microtime(true);
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
$client = new DealsApi(null, $config);
// Try to get a deal with a very high ID that likely doesn't exist
// Note: Official SDK doesn't have a simple getDeal method, so skip this
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Non-existent Resource Error', false, "Method not available in official SDK - API structure different", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Non-existent Resource Error', true, "Correctly threw exception: {$e->getMessage()} in {$elapsed}ms", $elapsed);
}
}
private function recordResult(string $testName, bool $success, string $message, float $elapsed): void
{
$this->results[] = [
'test' => $testName,
'success' => $success,
'message' => $message,
'elapsed' => $elapsed,
];
$status = $success ? '✅ PASS' : '❌ FAIL';
$this->line(" {$status} - {$testName}: {$message}");
}
private function displayResults(): void
{
$this->newLine();
$this->info('=== POC Test Results Summary ===');
$this->newLine();
$total = count($this->results);
$passed = count(array_filter($this->results, fn ($r) => $r['success']));
$failed = $total - $passed;
$this->line("Total Tests: {$total}");
$this->line("Passed: {$passed}");
$this->line("Failed: {$failed}");
$this->newLine();
$totalTime = array_sum(array_column($this->results, 'elapsed'));
$this->line("Total Execution Time: {$totalTime}ms");
$this->newLine();
if ($failed > 0) {
$this->warn('Failed Tests:');
foreach ($this->results as $result) {
if (! $result['success']) {
$this->line(" - {$result['test']}: {$result['message']}");
}
}
$this->newLine();
}
$this->info('=== Detailed Results ===');
foreach ($this->results as $result) {
$status = $result['success'] ? '✅' : '❌';
$this->line("{$status} {$result['test']} ({$result['elapsed']}ms): {$result['message']}");
}
$this->newLine();
$this->info('=== Recommendations ===');
if ($passed === $total) {
$this->info('✅ All tests passed! The official SDK is viable for migration.');
$this->info('Next steps: Proceed with full migration planning.');
} else {
$this->warn('⚠️ Some tests failed. Review the failures above before proceeding.');
$this->warn('Consider hybrid approach or investigate alternatives if critical operations fail.');
}
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
49394
|
NULL
|
NULL
|
NULL
|
|
49401
|
1762
|
23
|
2026-05-18T06:41:22.045484+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779086482045_m1.jpg...
|
PhpStorm
|
faVsco.js – TestPipedriveOfficialSdkCommand.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
pipedrive-sdk-poc, menu
S Project: faVsco.js, menu
pipedrive-sdk-poc, 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\Console\Commands\Crm;
use Jiminny\Console\Commands\Command;
use Jiminny\Console\Commands\Crm\Traits\SyncObjectsCommandTrait;
use Jiminny\Jobs\Crm\SyncObjects as SyncObjectsJob;
use Jiminny\Jobs\Job;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Models\Team;
class SyncObjects extends Command
{
use SyncObjectsCommandTrait;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'crm:sync-objects {team?} {--sync}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Sync remote CRM objects.';
/**
* Execute the console command.
*/
public function handle(): void
{
$teamId = $this->argument('team');
$teams = [];
if ($teamId) {
$team = Team::idOrUuId($teamId);
if ($team && $team->crm?->provider === Configuration::PROVIDER_HUBSPOT) {
$this->error(sprintf(
'Team %s (%s) uses HubSpot. Use crm:sync-hubspot-objects instead.',
$team->getName(),
$team->getUuid()
));
return;
}
if ($team) {
$teams[] = $team;
}
} else {
// Exclude HubSpot teams - handled by crm:sync-hubspot-objects
$teams = Team::where('status', Team::STATUS_ACTIVE)
->whereHas(
'crm',
fn ($q) => $q
->where('provider', '!=', Configuration::PROVIDER_HUBSPOT)
->where('sync_objects', true)
)
->get();
}
$this->dispatchSyncJobsForTeams($teams);
}
protected function getStaggerDelaySeconds(): float
{
return 2;
}
protected function getMaxDelaySeconds(): int
{
// Cap delay at 15 minutes (SQS max delay limit)
// capacity of 450 teams before reach
return 900;
}
protected function getLogPrefix(): string
{
return '';
}
protected function createSyncJob(Team $team): Job
{
return new SyncObjectsJob($team);
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
1
51
1
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Console\Commands\Crm;
use Carbon\Carbon;
use Exception;
use Illuminate\Console\Command;
use Jiminny\Component\Encryption\EncryptedTokenManager;
use Jiminny\Models\SocialAccount;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Pipedrive\versions\v1\Api\ActivitiesApi;
use Pipedrive\versions\v1\Api\ActivityFieldsApi;
use Pipedrive\versions\v1\Api\DealFieldsApi;
use Pipedrive\versions\v1\Api\DealsApi;
use Pipedrive\versions\v1\Api\PersonFieldsApi;
use Pipedrive\versions\v1\Api\PersonsApi;
use Pipedrive\versions\v1\Configuration;
class TestPipedriveOfficialSdkCommand extends Command
{
protected $signature = 'jiminny:test-pipedrive-official-sdk {teamId}';
protected $description = 'Test official Pipedrive SDK (pipedrive/pipedrive) with existing OAuth tokens';
private array $results = [];
private EncryptedTokenManager $tokenManager;
public function __construct(EncryptedTokenManager $tokenManager)
{
parent::__construct();
$this->tokenManager = $tokenManager;
}
public function handle(): int
{
$teamId = (int) $this->argument('teamId');
$this->info("Starting Pipedrive Official SDK POC for Team ID: {$teamId}");
$this->newLine();
try {
$this->runTests($teamId);
} catch (Exception $e) {
$this->error("POC failed with error: {$e->getMessage()}");
$this->error($e->getTraceAsString());
return 1;
}
$this->displayResults();
return 0;
}
private function runTests(int $teamId): void
{
$team = Team::find($teamId);
if (! $team) {
throw new Exception("Team with ID {$teamId} not found");
}
$socialAccount = SocialAccount::where('provider', SocialAccount::PROVIDER_PIPEDRIVE)
->whereHas('sociable', function ($query) use ($team) {
$query->where('team_id', $team->id);
})
->orderByDesc('expires') // Get the most recently expiring token
->first();
if (! $socialAccount) {
throw new Exception("No Pipedrive social account found for team {$teamId}");
}
$this->info("Found Pipedrive account for team: {$team->name}");
$this->newLine();
// Test 1: OAuth Initialization
$this->testOAuthInitialization($socialAccount);
// Test 2: Basic CRUD Operations
$this->testBasicCrudOperations($socialAccount);
// Test 3: Field Metadata
$this->testFieldMetadata($socialAccount);
// Test 4: v2 API Support
$this->testV2ApiSupport($socialAccount);
// Test 5: Error Handling
$this->testErrorHandling($socialAccount);
}
private function testOAuthInitialization(SocialAccount $socialAccount): void
{
$this->info('Test 1: OAuth Initialization');
$startTime = microtime(true);
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
$client = new DealsApi(null, $config);
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('OAuth Initialization', true, "Client initialized successfully in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('OAuth Initialization', false, $e->getMessage(), $elapsed);
}
$this->newLine();
}
private function testBasicCrudOperations(SocialAccount $socialAccount): void
{
$this->info('Test 2: Basic CRUD Operations');
try {
// Get the raw token from database
$rawDbToken = $socialAccount->getProviderUserToken();
$this->info("DB token (length: " . strlen($rawDbToken) . ")");
$this->info("DB token preview: " . substr($rawDbToken, 0, 20) . "...");
// Check token expiry
$expires = $socialAccount->expires;
$this->info("Token expires at: " . ($expires ? Carbon::createFromTimestamp($expires)->toDateTimeString() : 'null'));
$this->info("Token is expired: " . ($expires && $expires < time() ? 'YES' : 'NO'));
// Test with the DB token directly
$this->info("\n--- Testing with DB token ---");
$config = new Configuration();
$config->setAccessToken($rawDbToken);
// Test 2a: Get Deals
$this->testGetDeals($config);
// Test 2b: Get Persons
$this->testGetPersons($config);
// Test 2c: Get Activities
$this->testGetActivities($config);
} catch (Exception $e) {
$this->recordResult('Basic CRUD Operations', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testCurrentSystemApproach(SocialAccount $socialAccount): void
{
$startTime = microtime(true);
try {
// Simulate how current system makes requests
$token = $socialAccount->getProviderUserToken();
$url = 'https://api.pipedrive.com/api/v2/deals';
$headers = [
'Authorization' => 'Bearer ' . $token,
'Accept' => 'application/json',
];
$client = new \GuzzleHttp\Client();
$response = $client->get($url, [
'headers' => $headers,
'timeout' => 30,
]);
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$statusCode = $response->getStatusCode();
$this->recordResult('Current System v2 API', $statusCode === 200, "Status: {$statusCode} in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Current System v2 API', false, $e->getMessage(), $elapsed);
}
}
private function testGetDeals(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new DealsApi(null, $config);
// Official SDK uses getDealsSummary instead of getDeals
$result = $apiInstance->getDealsSummary();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Deals Summary', true, "Retrieved deals summary in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Deals Summary', false, $e->getMessage(), $elapsed);
}
}
private function testGetPersons(Configuration $config): void
{
$startTime = microtime(true);
try {
// Official SDK doesn't have a simple getPersons method
// Skip this test for now as the API structure is different
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Persons', false, "Method not available in official SDK - API structure different", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Persons', false, $e->getMessage(), $elapsed);
}
}
private function testGetActivities(Configuration $config): void
{
$startTime = microtime(true);
try {
// Official SDK doesn't have a simple getActivities method
// Skip this test for now as the API structure is different
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Activities', false, "Method not available in official SDK - API structure different", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Activities', false, $e->getMessage(), $elapsed);
}
}
private function testFieldMetadata(SocialAccount $socialAccount): void
{
$this->info('Test 3: Field Metadata');
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
// Test 3a: Get Deal Fields
$this->testGetDealFields($config);
// Test 3b: Get Person Fields
$this->testGetPersonFields($config);
// Test 3c: Get Activity Fields
$this->testGetActivityFields($config);
} catch (Exception $e) {
$this->recordResult('Field Metadata', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testGetDealFields(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new DealFieldsApi(null, $config);
$result = $apiInstance->getDealFields();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$count = $result->getData() ? count($result->getData()) : 0;
$this->recordResult('Get Deal Fields', true, "Retrieved {$count} deal fields in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Deal Fields', false, $e->getMessage(), $elapsed);
}
}
private function testGetPersonFields(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new PersonFieldsApi(null, $config);
$result = $apiInstance->getPersonFields();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$count = $result->getData() ? count($result->getData()) : 0;
$this->recordResult('Get Person Fields', true, "Retrieved {$count} person fields in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Person Fields', false, $e->getMessage(), $elapsed);
}
}
private function testGetActivityFields(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new ActivityFieldsApi(null, $config);
$result = $apiInstance->getActivityFields();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$count = $result->getData() ? count($result->getData()) : 0;
$this->recordResult('Get Activity Fields', true, "Retrieved {$count} activity fields in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Activity Fields', false, $e->getMessage(), $elapsed);
}
}
private function testV2ApiSupport(SocialAccount $socialAccount): void
{
$this->info('Test 4: v2 API Support (Cursor-based Pagination)');
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
$startTime = microtime(true);
// Test v2 deals endpoint with cursor pagination
// Note: The official SDK may or may not have v2 API support
// This test will determine if v2 is available
try {
$apiInstance = new DealsApi(null, $config);
$updatedSince = Carbon::now()->subDays(30)->toRfc3339String();
$result = $apiInstance->getDealsSummary();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
// Check if response has cursor information (v2 indicator)
// The response object doesn't have getAdditionalData method
// Just test if the call works - v2 API support is limited in official SDK v1
$this->recordResult('v2 API Support', false, "v1 API works but v2 cursor pagination not available in official SDK v1. Retrieved summary in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('v2 API Support', false, $e->getMessage(), $elapsed);
}
} catch (Exception $e) {
$this->recordResult('v2 API Support', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testErrorHandling(SocialAccount $socialAccount): void
{
$this->info('Test 5: Error Handling');
try {
// Test 5a: Invalid Token
$this->testInvalidToken();
// Test 5b: Non-existent Resource
$this->testNonExistentResource($socialAccount);
} catch (Exception $e) {
$this->recordResult('Error Handling', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testInvalidToken(): void
{
$startTime = microtime(true);
try {
$config = new Configuration();
$config->setAccessToken('invalid_token');
$client = new DealsApi(null, $config);
$client->getDealsSummary();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Invalid Token Error', false, "Expected error but request succeeded in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Invalid Token Error', true, "Correctly threw exception: {$e->getMessage()} in {$elapsed}ms", $elapsed);
}
}
private function testNonExistentResource(SocialAccount $socialAccount): void
{
$startTime = microtime(true);
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
$client = new DealsApi(null, $config);
// Try to get a deal with a very high ID that likely doesn't exist
// Note: Official SDK doesn't have a simple getDeal method, so skip this
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Non-existent Resource Error', false, "Method not available in official SDK - API structure different", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Non-existent Resource Error', true, "Correctly threw exception: {$e->getMessage()} in {$elapsed}ms", $elapsed);
}
}
private function recordResult(string $testName, bool $success, string $message, float $elapsed): void
{
$this->results[] = [
'test' => $testName,
'success' => $success,
'message' => $message,
'elapsed' => $elapsed,
];
$status = $success ? '✅ PASS' : '❌ FAIL';
$this->line(" {$status} - {$testName}: {$message}");
}
private function displayResults(): void
{
$this->newLine();
$this->info('=== POC Test Results Summary ===');
$this->newLine();
$total = count($this->results);
$passed = count(array_filter($this->results, fn ($r) => $r['success']));
$failed = $total - $passed;
$this->line("Total Tests: {$total}");
$this->line("Passed: {$passed}");
$this->line("Failed: {$failed}");
$this->newLine();
$totalTime = array_sum(array_column($this->results, 'elapsed'));
$this->line("Total Execution Time: {$totalTime}ms");
$this->newLine();
if ($failed > 0) {
$this->warn('Failed Tests:');
foreach ($this->results as $result) {
if (! $result['success']) {
$this->line(" - {$result['test']}: {$result['message']}");
}
}
$this->newLine();
}
$this->info('=== Detailed Results ===');
foreach ($this->results as $result) {
$status = $result['success'] ? '✅' : '❌';
$this->line("{$status} {$result['test']} ({$result['elapsed']}ms): {$result['message']}");
}
$this->newLine();
$this->info('=== Recommendations ===');
if ($passed === $total) {
$this->info('✅ All tests passed! The official SDK is viable for migration.');
$this->info('Next steps: Proceed with full migration planning.');
} else {
$this->warn('⚠️ Some tests failed. Review the failures above before proceeding.');
$this->warn('Consider hybrid approach or investigate alternatives if critical operations fail.');
}
}
}
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":"pipedrive-sdk-poc, menu","depth":5,"on_screen":true,"help_text":"Git Branch: pipedrive-sdk-poc","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\\Console\\Commands\\Crm;\n\nuse Jiminny\\Console\\Commands\\Command;\nuse Jiminny\\Console\\Commands\\Crm\\Traits\\SyncObjectsCommandTrait;\nuse Jiminny\\Jobs\\Crm\\SyncObjects as SyncObjectsJob;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Models\\Team;\n\nclass SyncObjects extends Command\n{\n use SyncObjectsCommandTrait;\n\n /**\n * The name and signature of the console command.\n *\n * @var string\n */\n protected $signature = 'crm:sync-objects {team?} {--sync}';\n\n /**\n * The console command description.\n *\n * @var string\n */\n protected $description = 'Sync remote CRM objects.';\n\n /**\n * Execute the console command.\n */\n public function handle(): void\n {\n $teamId = $this->argument('team');\n $teams = [];\n\n if ($teamId) {\n $team = Team::idOrUuId($teamId);\n if ($team && $team->crm?->provider === Configuration::PROVIDER_HUBSPOT) {\n $this->error(sprintf(\n 'Team %s (%s) uses HubSpot. Use crm:sync-hubspot-objects instead.',\n $team->getName(),\n $team->getUuid()\n ));\n\n return;\n }\n if ($team) {\n $teams[] = $team;\n }\n } else {\n // Exclude HubSpot teams - handled by crm:sync-hubspot-objects\n $teams = Team::where('status', Team::STATUS_ACTIVE)\n ->whereHas(\n 'crm',\n fn ($q) => $q\n ->where('provider', '!=', Configuration::PROVIDER_HUBSPOT)\n ->where('sync_objects', true)\n )\n ->get();\n }\n\n $this->dispatchSyncJobsForTeams($teams);\n }\n\n protected function getStaggerDelaySeconds(): float\n {\n return 2;\n }\n\n protected function getMaxDelaySeconds(): int\n {\n // Cap delay at 15 minutes (SQS max delay limit)\n // capacity of 450 teams before reach\n return 900;\n }\n\n protected function getLogPrefix(): string\n {\n return '';\n }\n\n protected function createSyncJob(Team $team): Job\n {\n return new SyncObjectsJob($team);\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Console\\Commands\\Crm;\n\nuse Jiminny\\Console\\Commands\\Command;\nuse Jiminny\\Console\\Commands\\Crm\\Traits\\SyncObjectsCommandTrait;\nuse Jiminny\\Jobs\\Crm\\SyncObjects as SyncObjectsJob;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Models\\Team;\n\nclass SyncObjects extends Command\n{\n use SyncObjectsCommandTrait;\n\n /**\n * The name and signature of the console command.\n *\n * @var string\n */\n protected $signature = 'crm:sync-objects {team?} {--sync}';\n\n /**\n * The console command description.\n *\n * @var string\n */\n protected $description = 'Sync remote CRM objects.';\n\n /**\n * Execute the console command.\n */\n public function handle(): void\n {\n $teamId = $this->argument('team');\n $teams = [];\n\n if ($teamId) {\n $team = Team::idOrUuId($teamId);\n if ($team && $team->crm?->provider === Configuration::PROVIDER_HUBSPOT) {\n $this->error(sprintf(\n 'Team %s (%s) uses HubSpot. Use crm:sync-hubspot-objects instead.',\n $team->getName(),\n $team->getUuid()\n ));\n\n return;\n }\n if ($team) {\n $teams[] = $team;\n }\n } else {\n // Exclude HubSpot teams - handled by crm:sync-hubspot-objects\n $teams = Team::where('status', Team::STATUS_ACTIVE)\n ->whereHas(\n 'crm',\n fn ($q) => $q\n ->where('provider', '!=', Configuration::PROVIDER_HUBSPOT)\n ->where('sync_objects', true)\n )\n ->get();\n }\n\n $this->dispatchSyncJobsForTeams($teams);\n }\n\n protected function getStaggerDelaySeconds(): float\n {\n return 2;\n }\n\n protected function getMaxDelaySeconds(): int\n {\n // Cap delay at 15 minutes (SQS max delay limit)\n // capacity of 450 teams before reach\n return 900;\n }\n\n protected function getLogPrefix(): string\n {\n return '';\n }\n\n protected function createSyncJob(Team $team): Job\n {\n return new SyncObjectsJob($team);\n }\n}","role_description":"text entry area","is_enabled":true,"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":"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":"AXStaticText","text":"1","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"51","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"1","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"on_screen":true,"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\\Console\\Commands\\Crm;\n\nuse Carbon\\Carbon;\nuse Exception;\nuse Illuminate\\Console\\Command;\nuse Jiminny\\Component\\Encryption\\EncryptedTokenManager;\nuse Jiminny\\Models\\SocialAccount;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Pipedrive\\versions\\v1\\Api\\ActivitiesApi;\nuse Pipedrive\\versions\\v1\\Api\\ActivityFieldsApi;\nuse Pipedrive\\versions\\v1\\Api\\DealFieldsApi;\nuse Pipedrive\\versions\\v1\\Api\\DealsApi;\nuse Pipedrive\\versions\\v1\\Api\\PersonFieldsApi;\nuse Pipedrive\\versions\\v1\\Api\\PersonsApi;\nuse Pipedrive\\versions\\v1\\Configuration;\n\nclass TestPipedriveOfficialSdkCommand extends Command\n{\n protected $signature = 'jiminny:test-pipedrive-official-sdk {teamId}';\n\n protected $description = 'Test official Pipedrive SDK (pipedrive/pipedrive) with existing OAuth tokens';\n\n private array $results = [];\n\n private EncryptedTokenManager $tokenManager;\n\n public function __construct(EncryptedTokenManager $tokenManager)\n {\n parent::__construct();\n $this->tokenManager = $tokenManager;\n }\n\n public function handle(): int\n {\n $teamId = (int) $this->argument('teamId');\n\n $this->info(\"Starting Pipedrive Official SDK POC for Team ID: {$teamId}\");\n $this->newLine();\n\n try {\n $this->runTests($teamId);\n } catch (Exception $e) {\n $this->error(\"POC failed with error: {$e->getMessage()}\");\n $this->error($e->getTraceAsString());\n\n return 1;\n }\n\n $this->displayResults();\n\n return 0;\n }\n\n private function runTests(int $teamId): void\n {\n $team = Team::find($teamId);\n if (! $team) {\n throw new Exception(\"Team with ID {$teamId} not found\");\n }\n\n $socialAccount = SocialAccount::where('provider', SocialAccount::PROVIDER_PIPEDRIVE)\n ->whereHas('sociable', function ($query) use ($team) {\n $query->where('team_id', $team->id);\n })\n ->orderByDesc('expires') // Get the most recently expiring token\n ->first();\n\n if (! $socialAccount) {\n throw new Exception(\"No Pipedrive social account found for team {$teamId}\");\n }\n\n $this->info(\"Found Pipedrive account for team: {$team->name}\");\n $this->newLine();\n\n // Test 1: OAuth Initialization\n $this->testOAuthInitialization($socialAccount);\n\n // Test 2: Basic CRUD Operations\n $this->testBasicCrudOperations($socialAccount);\n\n // Test 3: Field Metadata\n $this->testFieldMetadata($socialAccount);\n\n // Test 4: v2 API Support\n $this->testV2ApiSupport($socialAccount);\n\n // Test 5: Error Handling\n $this->testErrorHandling($socialAccount);\n }\n\n private function testOAuthInitialization(SocialAccount $socialAccount): void\n {\n $this->info('Test 1: OAuth Initialization');\n $startTime = microtime(true);\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n\n $client = new DealsApi(null, $config);\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('OAuth Initialization', true, \"Client initialized successfully in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('OAuth Initialization', false, $e->getMessage(), $elapsed);\n }\n\n $this->newLine();\n }\n\n private function testBasicCrudOperations(SocialAccount $socialAccount): void\n {\n $this->info('Test 2: Basic CRUD Operations');\n\n try {\n // Get the raw token from database\n $rawDbToken = $socialAccount->getProviderUserToken();\n $this->info(\"DB token (length: \" . strlen($rawDbToken) . \")\");\n $this->info(\"DB token preview: \" . substr($rawDbToken, 0, 20) . \"...\");\n\n // Check token expiry\n $expires = $socialAccount->expires;\n $this->info(\"Token expires at: \" . ($expires ? Carbon::createFromTimestamp($expires)->toDateTimeString() : 'null'));\n $this->info(\"Token is expired: \" . ($expires && $expires < time() ? 'YES' : 'NO'));\n\n // Test with the DB token directly\n $this->info(\"\\n--- Testing with DB token ---\");\n $config = new Configuration();\n $config->setAccessToken($rawDbToken);\n\n // Test 2a: Get Deals\n $this->testGetDeals($config);\n\n // Test 2b: Get Persons\n $this->testGetPersons($config);\n\n // Test 2c: Get Activities\n $this->testGetActivities($config);\n } catch (Exception $e) {\n $this->recordResult('Basic CRUD Operations', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testCurrentSystemApproach(SocialAccount $socialAccount): void\n {\n $startTime = microtime(true);\n\n try {\n // Simulate how current system makes requests\n $token = $socialAccount->getProviderUserToken();\n $url = 'https://api.pipedrive.com/api/v2/deals';\n\n $headers = [\n 'Authorization' => 'Bearer ' . $token,\n 'Accept' => 'application/json',\n ];\n\n $client = new \\GuzzleHttp\\Client();\n $response = $client->get($url, [\n 'headers' => $headers,\n 'timeout' => 30,\n ]);\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $statusCode = $response->getStatusCode();\n $this->recordResult('Current System v2 API', $statusCode === 200, \"Status: {$statusCode} in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Current System v2 API', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetDeals(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new DealsApi(null, $config);\n // Official SDK uses getDealsSummary instead of getDeals\n $result = $apiInstance->getDealsSummary();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Deals Summary', true, \"Retrieved deals summary in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Deals Summary', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetPersons(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n // Official SDK doesn't have a simple getPersons method\n // Skip this test for now as the API structure is different\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Persons', false, \"Method not available in official SDK - API structure different\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Persons', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetActivities(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n // Official SDK doesn't have a simple getActivities method\n // Skip this test for now as the API structure is different\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Activities', false, \"Method not available in official SDK - API structure different\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Activities', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testFieldMetadata(SocialAccount $socialAccount): void\n {\n $this->info('Test 3: Field Metadata');\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n\n // Test 3a: Get Deal Fields\n $this->testGetDealFields($config);\n\n // Test 3b: Get Person Fields\n $this->testGetPersonFields($config);\n\n // Test 3c: Get Activity Fields\n $this->testGetActivityFields($config);\n } catch (Exception $e) {\n $this->recordResult('Field Metadata', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testGetDealFields(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new DealFieldsApi(null, $config);\n $result = $apiInstance->getDealFields();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $count = $result->getData() ? count($result->getData()) : 0;\n $this->recordResult('Get Deal Fields', true, \"Retrieved {$count} deal fields in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Deal Fields', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetPersonFields(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new PersonFieldsApi(null, $config);\n $result = $apiInstance->getPersonFields();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $count = $result->getData() ? count($result->getData()) : 0;\n $this->recordResult('Get Person Fields', true, \"Retrieved {$count} person fields in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Person Fields', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetActivityFields(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new ActivityFieldsApi(null, $config);\n $result = $apiInstance->getActivityFields();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $count = $result->getData() ? count($result->getData()) : 0;\n $this->recordResult('Get Activity Fields', true, \"Retrieved {$count} activity fields in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Activity Fields', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testV2ApiSupport(SocialAccount $socialAccount): void\n {\n $this->info('Test 4: v2 API Support (Cursor-based Pagination)');\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n\n $startTime = microtime(true);\n\n // Test v2 deals endpoint with cursor pagination\n // Note: The official SDK may or may not have v2 API support\n // This test will determine if v2 is available\n try {\n $apiInstance = new DealsApi(null, $config);\n $updatedSince = Carbon::now()->subDays(30)->toRfc3339String();\n $result = $apiInstance->getDealsSummary();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n\n // Check if response has cursor information (v2 indicator)\n // The response object doesn't have getAdditionalData method\n // Just test if the call works - v2 API support is limited in official SDK v1\n $this->recordResult('v2 API Support', false, \"v1 API works but v2 cursor pagination not available in official SDK v1. Retrieved summary in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('v2 API Support', false, $e->getMessage(), $elapsed);\n }\n } catch (Exception $e) {\n $this->recordResult('v2 API Support', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testErrorHandling(SocialAccount $socialAccount): void\n {\n $this->info('Test 5: Error Handling');\n\n try {\n // Test 5a: Invalid Token\n $this->testInvalidToken();\n\n // Test 5b: Non-existent Resource\n $this->testNonExistentResource($socialAccount);\n } catch (Exception $e) {\n $this->recordResult('Error Handling', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testInvalidToken(): void\n {\n $startTime = microtime(true);\n\n try {\n $config = new Configuration();\n $config->setAccessToken('invalid_token');\n $client = new DealsApi(null, $config);\n\n $client->getDealsSummary();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Invalid Token Error', false, \"Expected error but request succeeded in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Invalid Token Error', true, \"Correctly threw exception: {$e->getMessage()} in {$elapsed}ms\", $elapsed);\n }\n }\n\n private function testNonExistentResource(SocialAccount $socialAccount): void\n {\n $startTime = microtime(true);\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n $client = new DealsApi(null, $config);\n\n // Try to get a deal with a very high ID that likely doesn't exist\n // Note: Official SDK doesn't have a simple getDeal method, so skip this\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Non-existent Resource Error', false, \"Method not available in official SDK - API structure different\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Non-existent Resource Error', true, \"Correctly threw exception: {$e->getMessage()} in {$elapsed}ms\", $elapsed);\n }\n }\n\n private function recordResult(string $testName, bool $success, string $message, float $elapsed): void\n {\n $this->results[] = [\n 'test' => $testName,\n 'success' => $success,\n 'message' => $message,\n 'elapsed' => $elapsed,\n ];\n\n $status = $success ? '✅ PASS' : '❌ FAIL';\n $this->line(\" {$status} - {$testName}: {$message}\");\n }\n\n private function displayResults(): void\n {\n $this->newLine();\n $this->info('=== POC Test Results Summary ===');\n $this->newLine();\n\n $total = count($this->results);\n $passed = count(array_filter($this->results, fn ($r) => $r['success']));\n $failed = $total - $passed;\n\n $this->line(\"Total Tests: {$total}\");\n $this->line(\"Passed: {$passed}\");\n $this->line(\"Failed: {$failed}\");\n $this->newLine();\n\n $totalTime = array_sum(array_column($this->results, 'elapsed'));\n $this->line(\"Total Execution Time: {$totalTime}ms\");\n $this->newLine();\n\n if ($failed > 0) {\n $this->warn('Failed Tests:');\n foreach ($this->results as $result) {\n if (! $result['success']) {\n $this->line(\" - {$result['test']}: {$result['message']}\");\n }\n }\n $this->newLine();\n }\n\n $this->info('=== Detailed Results ===');\n foreach ($this->results as $result) {\n $status = $result['success'] ? '✅' : '❌';\n $this->line(\"{$status} {$result['test']} ({$result['elapsed']}ms): {$result['message']}\");\n }\n\n $this->newLine();\n $this->info('=== Recommendations ===');\n\n if ($passed === $total) {\n $this->info('✅ All tests passed! The official SDK is viable for migration.');\n $this->info('Next steps: Proceed with full migration planning.');\n } else {\n $this->warn('⚠️ Some tests failed. Review the failures above before proceeding.');\n $this->warn('Consider hybrid approach or investigate alternatives if critical operations fail.');\n }\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Console\\Commands\\Crm;\n\nuse Carbon\\Carbon;\nuse Exception;\nuse Illuminate\\Console\\Command;\nuse Jiminny\\Component\\Encryption\\EncryptedTokenManager;\nuse Jiminny\\Models\\SocialAccount;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Pipedrive\\versions\\v1\\Api\\ActivitiesApi;\nuse Pipedrive\\versions\\v1\\Api\\ActivityFieldsApi;\nuse Pipedrive\\versions\\v1\\Api\\DealFieldsApi;\nuse Pipedrive\\versions\\v1\\Api\\DealsApi;\nuse Pipedrive\\versions\\v1\\Api\\PersonFieldsApi;\nuse Pipedrive\\versions\\v1\\Api\\PersonsApi;\nuse Pipedrive\\versions\\v1\\Configuration;\n\nclass TestPipedriveOfficialSdkCommand extends Command\n{\n protected $signature = 'jiminny:test-pipedrive-official-sdk {teamId}';\n\n protected $description = 'Test official Pipedrive SDK (pipedrive/pipedrive) with existing OAuth tokens';\n\n private array $results = [];\n\n private EncryptedTokenManager $tokenManager;\n\n public function __construct(EncryptedTokenManager $tokenManager)\n {\n parent::__construct();\n $this->tokenManager = $tokenManager;\n }\n\n public function handle(): int\n {\n $teamId = (int) $this->argument('teamId');\n\n $this->info(\"Starting Pipedrive Official SDK POC for Team ID: {$teamId}\");\n $this->newLine();\n\n try {\n $this->runTests($teamId);\n } catch (Exception $e) {\n $this->error(\"POC failed with error: {$e->getMessage()}\");\n $this->error($e->getTraceAsString());\n\n return 1;\n }\n\n $this->displayResults();\n\n return 0;\n }\n\n private function runTests(int $teamId): void\n {\n $team = Team::find($teamId);\n if (! $team) {\n throw new Exception(\"Team with ID {$teamId} not found\");\n }\n\n $socialAccount = SocialAccount::where('provider', SocialAccount::PROVIDER_PIPEDRIVE)\n ->whereHas('sociable', function ($query) use ($team) {\n $query->where('team_id', $team->id);\n })\n ->orderByDesc('expires') // Get the most recently expiring token\n ->first();\n\n if (! $socialAccount) {\n throw new Exception(\"No Pipedrive social account found for team {$teamId}\");\n }\n\n $this->info(\"Found Pipedrive account for team: {$team->name}\");\n $this->newLine();\n\n // Test 1: OAuth Initialization\n $this->testOAuthInitialization($socialAccount);\n\n // Test 2: Basic CRUD Operations\n $this->testBasicCrudOperations($socialAccount);\n\n // Test 3: Field Metadata\n $this->testFieldMetadata($socialAccount);\n\n // Test 4: v2 API Support\n $this->testV2ApiSupport($socialAccount);\n\n // Test 5: Error Handling\n $this->testErrorHandling($socialAccount);\n }\n\n private function testOAuthInitialization(SocialAccount $socialAccount): void\n {\n $this->info('Test 1: OAuth Initialization');\n $startTime = microtime(true);\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n\n $client = new DealsApi(null, $config);\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('OAuth Initialization', true, \"Client initialized successfully in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('OAuth Initialization', false, $e->getMessage(), $elapsed);\n }\n\n $this->newLine();\n }\n\n private function testBasicCrudOperations(SocialAccount $socialAccount): void\n {\n $this->info('Test 2: Basic CRUD Operations');\n\n try {\n // Get the raw token from database\n $rawDbToken = $socialAccount->getProviderUserToken();\n $this->info(\"DB token (length: \" . strlen($rawDbToken) . \")\");\n $this->info(\"DB token preview: \" . substr($rawDbToken, 0, 20) . \"...\");\n\n // Check token expiry\n $expires = $socialAccount->expires;\n $this->info(\"Token expires at: \" . ($expires ? Carbon::createFromTimestamp($expires)->toDateTimeString() : 'null'));\n $this->info(\"Token is expired: \" . ($expires && $expires < time() ? 'YES' : 'NO'));\n\n // Test with the DB token directly\n $this->info(\"\\n--- Testing with DB token ---\");\n $config = new Configuration();\n $config->setAccessToken($rawDbToken);\n\n // Test 2a: Get Deals\n $this->testGetDeals($config);\n\n // Test 2b: Get Persons\n $this->testGetPersons($config);\n\n // Test 2c: Get Activities\n $this->testGetActivities($config);\n } catch (Exception $e) {\n $this->recordResult('Basic CRUD Operations', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testCurrentSystemApproach(SocialAccount $socialAccount): void\n {\n $startTime = microtime(true);\n\n try {\n // Simulate how current system makes requests\n $token = $socialAccount->getProviderUserToken();\n $url = 'https://api.pipedrive.com/api/v2/deals';\n\n $headers = [\n 'Authorization' => 'Bearer ' . $token,\n 'Accept' => 'application/json',\n ];\n\n $client = new \\GuzzleHttp\\Client();\n $response = $client->get($url, [\n 'headers' => $headers,\n 'timeout' => 30,\n ]);\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $statusCode = $response->getStatusCode();\n $this->recordResult('Current System v2 API', $statusCode === 200, \"Status: {$statusCode} in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Current System v2 API', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetDeals(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new DealsApi(null, $config);\n // Official SDK uses getDealsSummary instead of getDeals\n $result = $apiInstance->getDealsSummary();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Deals Summary', true, \"Retrieved deals summary in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Deals Summary', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetPersons(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n // Official SDK doesn't have a simple getPersons method\n // Skip this test for now as the API structure is different\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Persons', false, \"Method not available in official SDK - API structure different\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Persons', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetActivities(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n // Official SDK doesn't have a simple getActivities method\n // Skip this test for now as the API structure is different\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Activities', false, \"Method not available in official SDK - API structure different\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Activities', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testFieldMetadata(SocialAccount $socialAccount): void\n {\n $this->info('Test 3: Field Metadata');\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n\n // Test 3a: Get Deal Fields\n $this->testGetDealFields($config);\n\n // Test 3b: Get Person Fields\n $this->testGetPersonFields($config);\n\n // Test 3c: Get Activity Fields\n $this->testGetActivityFields($config);\n } catch (Exception $e) {\n $this->recordResult('Field Metadata', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testGetDealFields(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new DealFieldsApi(null, $config);\n $result = $apiInstance->getDealFields();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $count = $result->getData() ? count($result->getData()) : 0;\n $this->recordResult('Get Deal Fields', true, \"Retrieved {$count} deal fields in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Deal Fields', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetPersonFields(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new PersonFieldsApi(null, $config);\n $result = $apiInstance->getPersonFields();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $count = $result->getData() ? count($result->getData()) : 0;\n $this->recordResult('Get Person Fields', true, \"Retrieved {$count} person fields in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Person Fields', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetActivityFields(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new ActivityFieldsApi(null, $config);\n $result = $apiInstance->getActivityFields();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $count = $result->getData() ? count($result->getData()) : 0;\n $this->recordResult('Get Activity Fields', true, \"Retrieved {$count} activity fields in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Activity Fields', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testV2ApiSupport(SocialAccount $socialAccount): void\n {\n $this->info('Test 4: v2 API Support (Cursor-based Pagination)');\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n\n $startTime = microtime(true);\n\n // Test v2 deals endpoint with cursor pagination\n // Note: The official SDK may or may not have v2 API support\n // This test will determine if v2 is available\n try {\n $apiInstance = new DealsApi(null, $config);\n $updatedSince = Carbon::now()->subDays(30)->toRfc3339String();\n $result = $apiInstance->getDealsSummary();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n\n // Check if response has cursor information (v2 indicator)\n // The response object doesn't have getAdditionalData method\n // Just test if the call works - v2 API support is limited in official SDK v1\n $this->recordResult('v2 API Support', false, \"v1 API works but v2 cursor pagination not available in official SDK v1. Retrieved summary in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('v2 API Support', false, $e->getMessage(), $elapsed);\n }\n } catch (Exception $e) {\n $this->recordResult('v2 API Support', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testErrorHandling(SocialAccount $socialAccount): void\n {\n $this->info('Test 5: Error Handling');\n\n try {\n // Test 5a: Invalid Token\n $this->testInvalidToken();\n\n // Test 5b: Non-existent Resource\n $this->testNonExistentResource($socialAccount);\n } catch (Exception $e) {\n $this->recordResult('Error Handling', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testInvalidToken(): void\n {\n $startTime = microtime(true);\n\n try {\n $config = new Configuration();\n $config->setAccessToken('invalid_token');\n $client = new DealsApi(null, $config);\n\n $client->getDealsSummary();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Invalid Token Error', false, \"Expected error but request succeeded in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Invalid Token Error', true, \"Correctly threw exception: {$e->getMessage()} in {$elapsed}ms\", $elapsed);\n }\n }\n\n private function testNonExistentResource(SocialAccount $socialAccount): void\n {\n $startTime = microtime(true);\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n $client = new DealsApi(null, $config);\n\n // Try to get a deal with a very high ID that likely doesn't exist\n // Note: Official SDK doesn't have a simple getDeal method, so skip this\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Non-existent Resource Error', false, \"Method not available in official SDK - API structure different\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Non-existent Resource Error', true, \"Correctly threw exception: {$e->getMessage()} in {$elapsed}ms\", $elapsed);\n }\n }\n\n private function recordResult(string $testName, bool $success, string $message, float $elapsed): void\n {\n $this->results[] = [\n 'test' => $testName,\n 'success' => $success,\n 'message' => $message,\n 'elapsed' => $elapsed,\n ];\n\n $status = $success ? '✅ PASS' : '❌ FAIL';\n $this->line(\" {$status} - {$testName}: {$message}\");\n }\n\n private function displayResults(): void\n {\n $this->newLine();\n $this->info('=== POC Test Results Summary ===');\n $this->newLine();\n\n $total = count($this->results);\n $passed = count(array_filter($this->results, fn ($r) => $r['success']));\n $failed = $total - $passed;\n\n $this->line(\"Total Tests: {$total}\");\n $this->line(\"Passed: {$passed}\");\n $this->line(\"Failed: {$failed}\");\n $this->newLine();\n\n $totalTime = array_sum(array_column($this->results, 'elapsed'));\n $this->line(\"Total Execution Time: {$totalTime}ms\");\n $this->newLine();\n\n if ($failed > 0) {\n $this->warn('Failed Tests:');\n foreach ($this->results as $result) {\n if (! $result['success']) {\n $this->line(\" - {$result['test']}: {$result['message']}\");\n }\n }\n $this->newLine();\n }\n\n $this->info('=== Detailed Results ===');\n foreach ($this->results as $result) {\n $status = $result['success'] ? '✅' : '❌';\n $this->line(\"{$status} {$result['test']} ({$result['elapsed']}ms): {$result['message']}\");\n }\n\n $this->newLine();\n $this->info('=== Recommendations ===');\n\n if ($passed === $total) {\n $this->info('✅ All tests passed! The official SDK is viable for migration.');\n $this->info('Next steps: Proceed with full migration planning.');\n } else {\n $this->warn('⚠️ Some tests failed. Review the failures above before proceeding.');\n $this->warn('Consider hybrid approach or investigate alternatives if critical operations fail.');\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}]...
|
5365831007722038384
|
61370652661533572
|
visual_change
|
accessibility
|
NULL
|
Project: faVsco.js, menu
pipedrive-sdk-poc, menu
S Project: faVsco.js, menu
pipedrive-sdk-poc, 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\Console\Commands\Crm;
use Jiminny\Console\Commands\Command;
use Jiminny\Console\Commands\Crm\Traits\SyncObjectsCommandTrait;
use Jiminny\Jobs\Crm\SyncObjects as SyncObjectsJob;
use Jiminny\Jobs\Job;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Models\Team;
class SyncObjects extends Command
{
use SyncObjectsCommandTrait;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'crm:sync-objects {team?} {--sync}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Sync remote CRM objects.';
/**
* Execute the console command.
*/
public function handle(): void
{
$teamId = $this->argument('team');
$teams = [];
if ($teamId) {
$team = Team::idOrUuId($teamId);
if ($team && $team->crm?->provider === Configuration::PROVIDER_HUBSPOT) {
$this->error(sprintf(
'Team %s (%s) uses HubSpot. Use crm:sync-hubspot-objects instead.',
$team->getName(),
$team->getUuid()
));
return;
}
if ($team) {
$teams[] = $team;
}
} else {
// Exclude HubSpot teams - handled by crm:sync-hubspot-objects
$teams = Team::where('status', Team::STATUS_ACTIVE)
->whereHas(
'crm',
fn ($q) => $q
->where('provider', '!=', Configuration::PROVIDER_HUBSPOT)
->where('sync_objects', true)
)
->get();
}
$this->dispatchSyncJobsForTeams($teams);
}
protected function getStaggerDelaySeconds(): float
{
return 2;
}
protected function getMaxDelaySeconds(): int
{
// Cap delay at 15 minutes (SQS max delay limit)
// capacity of 450 teams before reach
return 900;
}
protected function getLogPrefix(): string
{
return '';
}
protected function createSyncJob(Team $team): Job
{
return new SyncObjectsJob($team);
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
1
51
1
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Console\Commands\Crm;
use Carbon\Carbon;
use Exception;
use Illuminate\Console\Command;
use Jiminny\Component\Encryption\EncryptedTokenManager;
use Jiminny\Models\SocialAccount;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Pipedrive\versions\v1\Api\ActivitiesApi;
use Pipedrive\versions\v1\Api\ActivityFieldsApi;
use Pipedrive\versions\v1\Api\DealFieldsApi;
use Pipedrive\versions\v1\Api\DealsApi;
use Pipedrive\versions\v1\Api\PersonFieldsApi;
use Pipedrive\versions\v1\Api\PersonsApi;
use Pipedrive\versions\v1\Configuration;
class TestPipedriveOfficialSdkCommand extends Command
{
protected $signature = 'jiminny:test-pipedrive-official-sdk {teamId}';
protected $description = 'Test official Pipedrive SDK (pipedrive/pipedrive) with existing OAuth tokens';
private array $results = [];
private EncryptedTokenManager $tokenManager;
public function __construct(EncryptedTokenManager $tokenManager)
{
parent::__construct();
$this->tokenManager = $tokenManager;
}
public function handle(): int
{
$teamId = (int) $this->argument('teamId');
$this->info("Starting Pipedrive Official SDK POC for Team ID: {$teamId}");
$this->newLine();
try {
$this->runTests($teamId);
} catch (Exception $e) {
$this->error("POC failed with error: {$e->getMessage()}");
$this->error($e->getTraceAsString());
return 1;
}
$this->displayResults();
return 0;
}
private function runTests(int $teamId): void
{
$team = Team::find($teamId);
if (! $team) {
throw new Exception("Team with ID {$teamId} not found");
}
$socialAccount = SocialAccount::where('provider', SocialAccount::PROVIDER_PIPEDRIVE)
->whereHas('sociable', function ($query) use ($team) {
$query->where('team_id', $team->id);
})
->orderByDesc('expires') // Get the most recently expiring token
->first();
if (! $socialAccount) {
throw new Exception("No Pipedrive social account found for team {$teamId}");
}
$this->info("Found Pipedrive account for team: {$team->name}");
$this->newLine();
// Test 1: OAuth Initialization
$this->testOAuthInitialization($socialAccount);
// Test 2: Basic CRUD Operations
$this->testBasicCrudOperations($socialAccount);
// Test 3: Field Metadata
$this->testFieldMetadata($socialAccount);
// Test 4: v2 API Support
$this->testV2ApiSupport($socialAccount);
// Test 5: Error Handling
$this->testErrorHandling($socialAccount);
}
private function testOAuthInitialization(SocialAccount $socialAccount): void
{
$this->info('Test 1: OAuth Initialization');
$startTime = microtime(true);
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
$client = new DealsApi(null, $config);
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('OAuth Initialization', true, "Client initialized successfully in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('OAuth Initialization', false, $e->getMessage(), $elapsed);
}
$this->newLine();
}
private function testBasicCrudOperations(SocialAccount $socialAccount): void
{
$this->info('Test 2: Basic CRUD Operations');
try {
// Get the raw token from database
$rawDbToken = $socialAccount->getProviderUserToken();
$this->info("DB token (length: " . strlen($rawDbToken) . ")");
$this->info("DB token preview: " . substr($rawDbToken, 0, 20) . "...");
// Check token expiry
$expires = $socialAccount->expires;
$this->info("Token expires at: " . ($expires ? Carbon::createFromTimestamp($expires)->toDateTimeString() : 'null'));
$this->info("Token is expired: " . ($expires && $expires < time() ? 'YES' : 'NO'));
// Test with the DB token directly
$this->info("\n--- Testing with DB token ---");
$config = new Configuration();
$config->setAccessToken($rawDbToken);
// Test 2a: Get Deals
$this->testGetDeals($config);
// Test 2b: Get Persons
$this->testGetPersons($config);
// Test 2c: Get Activities
$this->testGetActivities($config);
} catch (Exception $e) {
$this->recordResult('Basic CRUD Operations', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testCurrentSystemApproach(SocialAccount $socialAccount): void
{
$startTime = microtime(true);
try {
// Simulate how current system makes requests
$token = $socialAccount->getProviderUserToken();
$url = 'https://api.pipedrive.com/api/v2/deals';
$headers = [
'Authorization' => 'Bearer ' . $token,
'Accept' => 'application/json',
];
$client = new \GuzzleHttp\Client();
$response = $client->get($url, [
'headers' => $headers,
'timeout' => 30,
]);
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$statusCode = $response->getStatusCode();
$this->recordResult('Current System v2 API', $statusCode === 200, "Status: {$statusCode} in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Current System v2 API', false, $e->getMessage(), $elapsed);
}
}
private function testGetDeals(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new DealsApi(null, $config);
// Official SDK uses getDealsSummary instead of getDeals
$result = $apiInstance->getDealsSummary();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Deals Summary', true, "Retrieved deals summary in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Deals Summary', false, $e->getMessage(), $elapsed);
}
}
private function testGetPersons(Configuration $config): void
{
$startTime = microtime(true);
try {
// Official SDK doesn't have a simple getPersons method
// Skip this test for now as the API structure is different
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Persons', false, "Method not available in official SDK - API structure different", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Persons', false, $e->getMessage(), $elapsed);
}
}
private function testGetActivities(Configuration $config): void
{
$startTime = microtime(true);
try {
// Official SDK doesn't have a simple getActivities method
// Skip this test for now as the API structure is different
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Activities', false, "Method not available in official SDK - API structure different", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Activities', false, $e->getMessage(), $elapsed);
}
}
private function testFieldMetadata(SocialAccount $socialAccount): void
{
$this->info('Test 3: Field Metadata');
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
// Test 3a: Get Deal Fields
$this->testGetDealFields($config);
// Test 3b: Get Person Fields
$this->testGetPersonFields($config);
// Test 3c: Get Activity Fields
$this->testGetActivityFields($config);
} catch (Exception $e) {
$this->recordResult('Field Metadata', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testGetDealFields(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new DealFieldsApi(null, $config);
$result = $apiInstance->getDealFields();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$count = $result->getData() ? count($result->getData()) : 0;
$this->recordResult('Get Deal Fields', true, "Retrieved {$count} deal fields in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Deal Fields', false, $e->getMessage(), $elapsed);
}
}
private function testGetPersonFields(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new PersonFieldsApi(null, $config);
$result = $apiInstance->getPersonFields();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$count = $result->getData() ? count($result->getData()) : 0;
$this->recordResult('Get Person Fields', true, "Retrieved {$count} person fields in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Person Fields', false, $e->getMessage(), $elapsed);
}
}
private function testGetActivityFields(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new ActivityFieldsApi(null, $config);
$result = $apiInstance->getActivityFields();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$count = $result->getData() ? count($result->getData()) : 0;
$this->recordResult('Get Activity Fields', true, "Retrieved {$count} activity fields in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Activity Fields', false, $e->getMessage(), $elapsed);
}
}
private function testV2ApiSupport(SocialAccount $socialAccount): void
{
$this->info('Test 4: v2 API Support (Cursor-based Pagination)');
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
$startTime = microtime(true);
// Test v2 deals endpoint with cursor pagination
// Note: The official SDK may or may not have v2 API support
// This test will determine if v2 is available
try {
$apiInstance = new DealsApi(null, $config);
$updatedSince = Carbon::now()->subDays(30)->toRfc3339String();
$result = $apiInstance->getDealsSummary();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
// Check if response has cursor information (v2 indicator)
// The response object doesn't have getAdditionalData method
// Just test if the call works - v2 API support is limited in official SDK v1
$this->recordResult('v2 API Support', false, "v1 API works but v2 cursor pagination not available in official SDK v1. Retrieved summary in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('v2 API Support', false, $e->getMessage(), $elapsed);
}
} catch (Exception $e) {
$this->recordResult('v2 API Support', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testErrorHandling(SocialAccount $socialAccount): void
{
$this->info('Test 5: Error Handling');
try {
// Test 5a: Invalid Token
$this->testInvalidToken();
// Test 5b: Non-existent Resource
$this->testNonExistentResource($socialAccount);
} catch (Exception $e) {
$this->recordResult('Error Handling', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testInvalidToken(): void
{
$startTime = microtime(true);
try {
$config = new Configuration();
$config->setAccessToken('invalid_token');
$client = new DealsApi(null, $config);
$client->getDealsSummary();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Invalid Token Error', false, "Expected error but request succeeded in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Invalid Token Error', true, "Correctly threw exception: {$e->getMessage()} in {$elapsed}ms", $elapsed);
}
}
private function testNonExistentResource(SocialAccount $socialAccount): void
{
$startTime = microtime(true);
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
$client = new DealsApi(null, $config);
// Try to get a deal with a very high ID that likely doesn't exist
// Note: Official SDK doesn't have a simple getDeal method, so skip this
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Non-existent Resource Error', false, "Method not available in official SDK - API structure different", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Non-existent Resource Error', true, "Correctly threw exception: {$e->getMessage()} in {$elapsed}ms", $elapsed);
}
}
private function recordResult(string $testName, bool $success, string $message, float $elapsed): void
{
$this->results[] = [
'test' => $testName,
'success' => $success,
'message' => $message,
'elapsed' => $elapsed,
];
$status = $success ? '✅ PASS' : '❌ FAIL';
$this->line(" {$status} - {$testName}: {$message}");
}
private function displayResults(): void
{
$this->newLine();
$this->info('=== POC Test Results Summary ===');
$this->newLine();
$total = count($this->results);
$passed = count(array_filter($this->results, fn ($r) => $r['success']));
$failed = $total - $passed;
$this->line("Total Tests: {$total}");
$this->line("Passed: {$passed}");
$this->line("Failed: {$failed}");
$this->newLine();
$totalTime = array_sum(array_column($this->results, 'elapsed'));
$this->line("Total Execution Time: {$totalTime}ms");
$this->newLine();
if ($failed > 0) {
$this->warn('Failed Tests:');
foreach ($this->results as $result) {
if (! $result['success']) {
$this->line(" - {$result['test']}: {$result['message']}");
}
}
$this->newLine();
}
$this->info('=== Detailed Results ===');
foreach ($this->results as $result) {
$status = $result['success'] ? '✅' : '❌';
$this->line("{$status} {$result['test']} ({$result['elapsed']}ms): {$result['message']}");
}
$this->newLine();
$this->info('=== Recommendations ===');
if ($passed === $total) {
$this->info('✅ All tests passed! The official SDK is viable for migration.');
$this->info('Next steps: Proceed with full migration planning.');
} else {
$this->warn('⚠️ Some tests failed. Review the failures above before proceeding.');
$this->warn('Consider hybrid approach or investigate alternatives if critical operations fail.');
}
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
49398
|
NULL
|
NULL
|
NULL
|
|
49400
|
1763
|
25
|
2026-05-18T06:41:17.367410+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779086477367_m2.jpg...
|
PhpStorm
|
faVsco.js – TestPipedriveOfficialSdkCommand.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
pipedrive-sdk-poc, menu
S Project: faVsco.js, menu
pipedrive-sdk-poc, 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\Console\Commands\Crm;
use Jiminny\Console\Commands\Command;
use Jiminny\Console\Commands\Crm\Traits\SyncObjectsCommandTrait;
use Jiminny\Jobs\Crm\SyncObjects as SyncObjectsJob;
use Jiminny\Jobs\Job;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Models\Team;
class SyncObjects extends Command
{
use SyncObjectsCommandTrait;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'crm:sync-objects {team?} {--sync}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Sync remote CRM objects.';
/**
* Execute the console command.
*/
public function handle(): void
{
$teamId = $this->argument('team');
$teams = [];
if ($teamId) {
$team = Team::idOrUuId($teamId);
if ($team && $team->crm?->provider === Configuration::PROVIDER_HUBSPOT) {
$this->error(sprintf(
'Team %s (%s) uses HubSpot. Use crm:sync-hubspot-objects instead.',
$team->getName(),
$team->getUuid()
));
return;
}
if ($team) {
$teams[] = $team;
}
} else {
// Exclude HubSpot teams - handled by crm:sync-hubspot-objects
$teams = Team::where('status', Team::STATUS_ACTIVE)
->whereHas(
'crm',
fn ($q) => $q
->where('provider', '!=', Configuration::PROVIDER_HUBSPOT)
->where('sync_objects', true)
)
->get();
}
$this->dispatchSyncJobsForTeams($teams);
}
protected function getStaggerDelaySeconds(): float
{
return 2;
}
protected function getMaxDelaySeconds(): int
{
// Cap delay at 15 minutes (SQS max delay limit)
// capacity of 450 teams before reach
return 900;
}
protected function getLogPrefix(): string
{
return '';
}
protected function createSyncJob(Team $team): Job
{
return new SyncObjectsJob($team);
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
1
51
1
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Console\Commands\Crm;
use Carbon\Carbon;
use Exception;
use Illuminate\Console\Command;
use Jiminny\Component\Encryption\EncryptedTokenManager;
use Jiminny\Models\SocialAccount;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Pipedrive\versions\v1\Api\ActivitiesApi;
use Pipedrive\versions\v1\Api\ActivityFieldsApi;
use Pipedrive\versions\v1\Api\DealFieldsApi;
use Pipedrive\versions\v1\Api\DealsApi;
use Pipedrive\versions\v1\Api\PersonFieldsApi;
use Pipedrive\versions\v1\Api\PersonsApi;
use Pipedrive\versions\v1\Configuration;
class TestPipedriveOfficialSdkCommand extends Command
{
protected $signature = 'jiminny:test-pipedrive-official-sdk {teamId}';
protected $description = 'Test official Pipedrive SDK (pipedrive/pipedrive) with existing OAuth tokens';
private array $results = [];
private EncryptedTokenManager $tokenManager;
public function __construct(EncryptedTokenManager $tokenManager)
{
parent::__construct();
$this->tokenManager = $tokenManager;
}
public function handle(): int
{
$teamId = (int) $this->argument('teamId');
$this->info("Starting Pipedrive Official SDK POC for Team ID: {$teamId}");
$this->newLine();
try {
$this->runTests($teamId);
} catch (Exception $e) {
$this->error("POC failed with error: {$e->getMessage()}");
$this->error($e->getTraceAsString());
return 1;
}
$this->displayResults();
return 0;
}
private function runTests(int $teamId): void
{
$team = Team::find($teamId);
if (! $team) {
throw new Exception("Team with ID {$teamId} not found");
}
$socialAccount = SocialAccount::where('provider', SocialAccount::PROVIDER_PIPEDRIVE)
->whereHas('sociable', function ($query) use ($team) {
$query->where('team_id', $team->id);
})
->orderByDesc('expires') // Get the most recently expiring token
->first();
if (! $socialAccount) {
throw new Exception("No Pipedrive social account found for team {$teamId}");
}
$this->info("Found Pipedrive account for team: {$team->name}");
$this->newLine();
// Test 1: OAuth Initialization
$this->testOAuthInitialization($socialAccount);
// Test 2: Basic CRUD Operations
$this->testBasicCrudOperations($socialAccount);
// Test 3: Field Metadata
$this->testFieldMetadata($socialAccount);
// Test 4: v2 API Support
$this->testV2ApiSupport($socialAccount);
// Test 5: Error Handling
$this->testErrorHandling($socialAccount);
}
private function testOAuthInitialization(SocialAccount $socialAccount): void
{
$this->info('Test 1: OAuth Initialization');
$startTime = microtime(true);
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
$client = new DealsApi(null, $config);
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('OAuth Initialization', true, "Client initialized successfully in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('OAuth Initialization', false, $e->getMessage(), $elapsed);
}
$this->newLine();
}
private function testBasicCrudOperations(SocialAccount $socialAccount): void
{
$this->info('Test 2: Basic CRUD Operations');
try {
// Get the raw token from database
$rawDbToken = $socialAccount->getProviderUserToken();
$this->info("DB token (length: " . strlen($rawDbToken) . ")");
$this->info("DB token preview: " . substr($rawDbToken, 0, 20) . "...");
// Check token expiry
$expires = $socialAccount->expires;
$this->info("Token expires at: " . ($expires ? Carbon::createFromTimestamp($expires)->toDateTimeString() : 'null'));
$this->info("Token is expired: " . ($expires && $expires < time() ? 'YES' : 'NO'));
// Test with the DB token directly
$this->info("\n--- Testing with DB token ---");
$config = new Configuration();
$config->setAccessToken($rawDbToken);
// Test 2a: Get Deals
$this->testGetDeals($config);
// Test 2b: Get Persons
$this->testGetPersons($config);
// Test 2c: Get Activities
$this->testGetActivities($config);
} catch (Exception $e) {
$this->recordResult('Basic CRUD Operations', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testCurrentSystemApproach(SocialAccount $socialAccount): void
{
$startTime = microtime(true);
try {
// Simulate how current system makes requests
$token = $socialAccount->getProviderUserToken();
$url = 'https://api.pipedrive.com/api/v2/deals';
$headers = [
'Authorization' => 'Bearer ' . $token,
'Accept' => 'application/json',
];
$client = new \GuzzleHttp\Client();
$response = $client->get($url, [
'headers' => $headers,
'timeout' => 30,
]);
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$statusCode = $response->getStatusCode();
$this->recordResult('Current System v2 API', $statusCode === 200, "Status: {$statusCode} in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Current System v2 API', false, $e->getMessage(), $elapsed);
}
}
private function testGetDeals(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new DealsApi(null, $config);
// Official SDK uses getDealsSummary instead of getDeals
$result = $apiInstance->getDealsSummary();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Deals Summary', true, "Retrieved deals summary in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Deals Summary', false, $e->getMessage(), $elapsed);
}
}
private function testGetPersons(Configuration $config): void
{
$startTime = microtime(true);
try {
// Official SDK doesn't have a simple getPersons method
// Skip this test for now as the API structure is different
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Persons', false, "Method not available in official SDK - API structure different", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Persons', false, $e->getMessage(), $elapsed);
}
}
private function testGetActivities(Configuration $config): void
{
$startTime = microtime(true);
try {
// Official SDK doesn't have a simple getActivities method
// Skip this test for now as the API structure is different
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Activities', false, "Method not available in official SDK - API structure different", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Activities', false, $e->getMessage(), $elapsed);
}
}
private function testFieldMetadata(SocialAccount $socialAccount): void
{
$this->info('Test 3: Field Metadata');
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
// Test 3a: Get Deal Fields
$this->testGetDealFields($config);
// Test 3b: Get Person Fields
$this->testGetPersonFields($config);
// Test 3c: Get Activity Fields
$this->testGetActivityFields($config);
} catch (Exception $e) {
$this->recordResult('Field Metadata', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testGetDealFields(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new DealFieldsApi(null, $config);
$result = $apiInstance->getDealFields();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$count = $result->getData() ? count($result->getData()) : 0;
$this->recordResult('Get Deal Fields', true, "Retrieved {$count} deal fields in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Deal Fields', false, $e->getMessage(), $elapsed);
}
}
private function testGetPersonFields(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new PersonFieldsApi(null, $config);
$result = $apiInstance->getPersonFields();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$count = $result->getData() ? count($result->getData()) : 0;
$this->recordResult('Get Person Fields', true, "Retrieved {$count} person fields in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Person Fields', false, $e->getMessage(), $elapsed);
}
}
private function testGetActivityFields(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new ActivityFieldsApi(null, $config);
$result = $apiInstance->getActivityFields();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$count = $result->getData() ? count($result->getData()) : 0;
$this->recordResult('Get Activity Fields', true, "Retrieved {$count} activity fields in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Activity Fields', false, $e->getMessage(), $elapsed);
}
}
private function testV2ApiSupport(SocialAccount $socialAccount): void
{
$this->info('Test 4: v2 API Support (Cursor-based Pagination)');
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
$startTime = microtime(true);
// Test v2 deals endpoint with cursor pagination
// Note: The official SDK may or may not have v2 API support
// This test will determine if v2 is available
try {
$apiInstance = new DealsApi(null, $config);
$updatedSince = Carbon::now()->subDays(30)->toRfc3339String();
$result = $apiInstance->getDealsSummary();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
// Check if response has cursor information (v2 indicator)
// The response object doesn't have getAdditionalData method
// Just test if the call works - v2 API support is limited in official SDK v1
$this->recordResult('v2 API Support', false, "v1 API works but v2 cursor pagination not available in official SDK v1. Retrieved summary in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('v2 API Support', false, $e->getMessage(), $elapsed);
}
} catch (Exception $e) {
$this->recordResult('v2 API Support', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testErrorHandling(SocialAccount $socialAccount): void
{
$this->info('Test 5: Error Handling');
try {
// Test 5a: Invalid Token
$this->testInvalidToken();
// Test 5b: Non-existent Resource
$this->testNonExistentResource($socialAccount);
} catch (Exception $e) {
$this->recordResult('Error Handling', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testInvalidToken(): void
{
$startTime = microtime(true);
try {
$config = new Configuration();
$config->setAccessToken('invalid_token');
$client = new DealsApi(null, $config);
$client->getDealsSummary();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Invalid Token Error', false, "Expected error but request succeeded in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Invalid Token Error', true, "Correctly threw exception: {$e->getMessage()} in {$elapsed}ms", $elapsed);
}
}
private function testNonExistentResource(SocialAccount $socialAccount): void
{
$startTime = microtime(true);
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
$client = new DealsApi(null, $config);
// Try to get a deal with a very high ID that likely doesn't exist
// Note: Official SDK doesn't have a simple getDeal method, so skip this
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Non-existent Resource Error', false, "Method not available in official SDK - API structure different", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Non-existent Resource Error', true, "Correctly threw exception: {$e->getMessage()} in {$elapsed}ms", $elapsed);
}
}
private function recordResult(string $testName, bool $success, string $message, float $elapsed): void
{
$this->results[] = [
'test' => $testName,
'success' => $success,
'message' => $message,
'elapsed' => $elapsed,
];
$status = $success ? '✅ PASS' : '❌ FAIL';
$this->line(" {$status} - {$testName}: {$message}");
}
private function displayResults(): void
{
$this->newLine();
$this->info('=== POC Test Results Summary ===');
$this->newLine();
$total = count($this->results);
$passed = count(array_filter($this->results, fn ($r) => $r['success']));
$failed = $total - $passed;
$this->line("Total Tests: {$total}");
$this->line("Passed: {$passed}");
$this->line("Failed: {$failed}");
$this->newLine();
$totalTime = array_sum(array_column($this->results, 'elapsed'));
$this->line("Total Execution Time: {$totalTime}ms");
$this->newLine();
if ($failed > 0) {
$this->warn('Failed Tests:');
foreach ($this->results as $result) {
if (! $result['success']) {
$this->line(" - {$result['test']}: {$result['message']}");
}
}
$this->newLine();
}
$this->info('=== Detailed Results ===');
foreach ($this->results as $result) {
$status = $result['success'] ? '✅' : '❌';
$this->line("{$status} {$result['test']} ({$result['elapsed']}ms): {$result['message']}");
}
$this->newLine();
$this->info('=== Recommendations ===');
if ($passed === $total) {
$this->info('✅ All tests passed! The official SDK is viable for migration.');
$this->info('Next steps: Proceed with full migration planning.');
} else {
$this->warn('⚠️ Some tests failed. Review the failures above before proceeding.');
$this->warn('Consider hybrid approach or investigate alternatives if critical operations fail.');
}
}
}
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":"pipedrive-sdk-poc, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.05618351,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: pipedrive-sdk-poc","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\\Console\\Commands\\Crm;\n\nuse Jiminny\\Console\\Commands\\Command;\nuse Jiminny\\Console\\Commands\\Crm\\Traits\\SyncObjectsCommandTrait;\nuse Jiminny\\Jobs\\Crm\\SyncObjects as SyncObjectsJob;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Models\\Team;\n\nclass SyncObjects extends Command\n{\n use SyncObjectsCommandTrait;\n\n /**\n * The name and signature of the console command.\n *\n * @var string\n */\n protected $signature = 'crm:sync-objects {team?} {--sync}';\n\n /**\n * The console command description.\n *\n * @var string\n */\n protected $description = 'Sync remote CRM objects.';\n\n /**\n * Execute the console command.\n */\n public function handle(): void\n {\n $teamId = $this->argument('team');\n $teams = [];\n\n if ($teamId) {\n $team = Team::idOrUuId($teamId);\n if ($team && $team->crm?->provider === Configuration::PROVIDER_HUBSPOT) {\n $this->error(sprintf(\n 'Team %s (%s) uses HubSpot. Use crm:sync-hubspot-objects instead.',\n $team->getName(),\n $team->getUuid()\n ));\n\n return;\n }\n if ($team) {\n $teams[] = $team;\n }\n } else {\n // Exclude HubSpot teams - handled by crm:sync-hubspot-objects\n $teams = Team::where('status', Team::STATUS_ACTIVE)\n ->whereHas(\n 'crm',\n fn ($q) => $q\n ->where('provider', '!=', Configuration::PROVIDER_HUBSPOT)\n ->where('sync_objects', true)\n )\n ->get();\n }\n\n $this->dispatchSyncJobsForTeams($teams);\n }\n\n protected function getStaggerDelaySeconds(): float\n {\n return 2;\n }\n\n protected function getMaxDelaySeconds(): int\n {\n // Cap delay at 15 minutes (SQS max delay limit)\n // capacity of 450 teams before reach\n return 900;\n }\n\n protected function getLogPrefix(): string\n {\n return '';\n }\n\n protected function createSyncJob(Team $team): Job\n {\n return new SyncObjectsJob($team);\n }\n}","depth":4,"bounds":{"left":0.15724733,"top":0.2952913,"width":0.21476063,"height":0.7047087},"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Console\\Commands\\Crm;\n\nuse Jiminny\\Console\\Commands\\Command;\nuse Jiminny\\Console\\Commands\\Crm\\Traits\\SyncObjectsCommandTrait;\nuse Jiminny\\Jobs\\Crm\\SyncObjects as SyncObjectsJob;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Models\\Team;\n\nclass SyncObjects extends Command\n{\n use SyncObjectsCommandTrait;\n\n /**\n * The name and signature of the console command.\n *\n * @var string\n */\n protected $signature = 'crm:sync-objects {team?} {--sync}';\n\n /**\n * The console command description.\n *\n * @var string\n */\n protected $description = 'Sync remote CRM objects.';\n\n /**\n * Execute the console command.\n */\n public function handle(): void\n {\n $teamId = $this->argument('team');\n $teams = [];\n\n if ($teamId) {\n $team = Team::idOrUuId($teamId);\n if ($team && $team->crm?->provider === Configuration::PROVIDER_HUBSPOT) {\n $this->error(sprintf(\n 'Team %s (%s) uses HubSpot. Use crm:sync-hubspot-objects instead.',\n $team->getName(),\n $team->getUuid()\n ));\n\n return;\n }\n if ($team) {\n $teams[] = $team;\n }\n } else {\n // Exclude HubSpot teams - handled by crm:sync-hubspot-objects\n $teams = Team::where('status', Team::STATUS_ACTIVE)\n ->whereHas(\n 'crm',\n fn ($q) => $q\n ->where('provider', '!=', Configuration::PROVIDER_HUBSPOT)\n ->where('sync_objects', true)\n )\n ->get();\n }\n\n $this->dispatchSyncJobsForTeams($teams);\n }\n\n protected function getStaggerDelaySeconds(): float\n {\n return 2;\n }\n\n protected function getMaxDelaySeconds(): int\n {\n // Cap delay at 15 minutes (SQS max delay limit)\n // capacity of 450 teams before reach\n return 900;\n }\n\n protected function getLogPrefix(): string\n {\n return '';\n }\n\n protected function createSyncJob(Team $team): Job\n {\n return new SyncObjectsJob($team);\n }\n}","role_description":"text entry area","is_enabled":true,"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":"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":"AXStaticText","text":"1","depth":4,"bounds":{"left":0.57912236,"top":0.10055866,"width":0.00731383,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"51","depth":4,"bounds":{"left":0.5884308,"top":0.10055866,"width":0.009640957,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"1","depth":4,"bounds":{"left":0.6000665,"top":0.10055866,"width":0.00731383,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.6090425,"top":0.09896249,"width":0.00731383,"height":0.018355945},"on_screen":true,"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.6163564,"top":0.09896249,"width":0.006981383,"height":0.018355945},"on_screen":true,"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\\Console\\Commands\\Crm;\n\nuse Carbon\\Carbon;\nuse Exception;\nuse Illuminate\\Console\\Command;\nuse Jiminny\\Component\\Encryption\\EncryptedTokenManager;\nuse Jiminny\\Models\\SocialAccount;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Pipedrive\\versions\\v1\\Api\\ActivitiesApi;\nuse Pipedrive\\versions\\v1\\Api\\ActivityFieldsApi;\nuse Pipedrive\\versions\\v1\\Api\\DealFieldsApi;\nuse Pipedrive\\versions\\v1\\Api\\DealsApi;\nuse Pipedrive\\versions\\v1\\Api\\PersonFieldsApi;\nuse Pipedrive\\versions\\v1\\Api\\PersonsApi;\nuse Pipedrive\\versions\\v1\\Configuration;\n\nclass TestPipedriveOfficialSdkCommand extends Command\n{\n protected $signature = 'jiminny:test-pipedrive-official-sdk {teamId}';\n\n protected $description = 'Test official Pipedrive SDK (pipedrive/pipedrive) with existing OAuth tokens';\n\n private array $results = [];\n\n private EncryptedTokenManager $tokenManager;\n\n public function __construct(EncryptedTokenManager $tokenManager)\n {\n parent::__construct();\n $this->tokenManager = $tokenManager;\n }\n\n public function handle(): int\n {\n $teamId = (int) $this->argument('teamId');\n\n $this->info(\"Starting Pipedrive Official SDK POC for Team ID: {$teamId}\");\n $this->newLine();\n\n try {\n $this->runTests($teamId);\n } catch (Exception $e) {\n $this->error(\"POC failed with error: {$e->getMessage()}\");\n $this->error($e->getTraceAsString());\n\n return 1;\n }\n\n $this->displayResults();\n\n return 0;\n }\n\n private function runTests(int $teamId): void\n {\n $team = Team::find($teamId);\n if (! $team) {\n throw new Exception(\"Team with ID {$teamId} not found\");\n }\n\n $socialAccount = SocialAccount::where('provider', SocialAccount::PROVIDER_PIPEDRIVE)\n ->whereHas('sociable', function ($query) use ($team) {\n $query->where('team_id', $team->id);\n })\n ->orderByDesc('expires') // Get the most recently expiring token\n ->first();\n\n if (! $socialAccount) {\n throw new Exception(\"No Pipedrive social account found for team {$teamId}\");\n }\n\n $this->info(\"Found Pipedrive account for team: {$team->name}\");\n $this->newLine();\n\n // Test 1: OAuth Initialization\n $this->testOAuthInitialization($socialAccount);\n\n // Test 2: Basic CRUD Operations\n $this->testBasicCrudOperations($socialAccount);\n\n // Test 3: Field Metadata\n $this->testFieldMetadata($socialAccount);\n\n // Test 4: v2 API Support\n $this->testV2ApiSupport($socialAccount);\n\n // Test 5: Error Handling\n $this->testErrorHandling($socialAccount);\n }\n\n private function testOAuthInitialization(SocialAccount $socialAccount): void\n {\n $this->info('Test 1: OAuth Initialization');\n $startTime = microtime(true);\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n\n $client = new DealsApi(null, $config);\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('OAuth Initialization', true, \"Client initialized successfully in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('OAuth Initialization', false, $e->getMessage(), $elapsed);\n }\n\n $this->newLine();\n }\n\n private function testBasicCrudOperations(SocialAccount $socialAccount): void\n {\n $this->info('Test 2: Basic CRUD Operations');\n\n try {\n // Get the raw token from database\n $rawDbToken = $socialAccount->getProviderUserToken();\n $this->info(\"DB token (length: \" . strlen($rawDbToken) . \")\");\n $this->info(\"DB token preview: \" . substr($rawDbToken, 0, 20) . \"...\");\n\n // Check token expiry\n $expires = $socialAccount->expires;\n $this->info(\"Token expires at: \" . ($expires ? Carbon::createFromTimestamp($expires)->toDateTimeString() : 'null'));\n $this->info(\"Token is expired: \" . ($expires && $expires < time() ? 'YES' : 'NO'));\n\n // Test with the DB token directly\n $this->info(\"\\n--- Testing with DB token ---\");\n $config = new Configuration();\n $config->setAccessToken($rawDbToken);\n\n // Test 2a: Get Deals\n $this->testGetDeals($config);\n\n // Test 2b: Get Persons\n $this->testGetPersons($config);\n\n // Test 2c: Get Activities\n $this->testGetActivities($config);\n } catch (Exception $e) {\n $this->recordResult('Basic CRUD Operations', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testCurrentSystemApproach(SocialAccount $socialAccount): void\n {\n $startTime = microtime(true);\n\n try {\n // Simulate how current system makes requests\n $token = $socialAccount->getProviderUserToken();\n $url = 'https://api.pipedrive.com/api/v2/deals';\n\n $headers = [\n 'Authorization' => 'Bearer ' . $token,\n 'Accept' => 'application/json',\n ];\n\n $client = new \\GuzzleHttp\\Client();\n $response = $client->get($url, [\n 'headers' => $headers,\n 'timeout' => 30,\n ]);\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $statusCode = $response->getStatusCode();\n $this->recordResult('Current System v2 API', $statusCode === 200, \"Status: {$statusCode} in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Current System v2 API', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetDeals(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new DealsApi(null, $config);\n // Official SDK uses getDealsSummary instead of getDeals\n $result = $apiInstance->getDealsSummary();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Deals Summary', true, \"Retrieved deals summary in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Deals Summary', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetPersons(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n // Official SDK doesn't have a simple getPersons method\n // Skip this test for now as the API structure is different\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Persons', false, \"Method not available in official SDK - API structure different\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Persons', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetActivities(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n // Official SDK doesn't have a simple getActivities method\n // Skip this test for now as the API structure is different\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Activities', false, \"Method not available in official SDK - API structure different\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Activities', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testFieldMetadata(SocialAccount $socialAccount): void\n {\n $this->info('Test 3: Field Metadata');\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n\n // Test 3a: Get Deal Fields\n $this->testGetDealFields($config);\n\n // Test 3b: Get Person Fields\n $this->testGetPersonFields($config);\n\n // Test 3c: Get Activity Fields\n $this->testGetActivityFields($config);\n } catch (Exception $e) {\n $this->recordResult('Field Metadata', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testGetDealFields(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new DealFieldsApi(null, $config);\n $result = $apiInstance->getDealFields();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $count = $result->getData() ? count($result->getData()) : 0;\n $this->recordResult('Get Deal Fields', true, \"Retrieved {$count} deal fields in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Deal Fields', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetPersonFields(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new PersonFieldsApi(null, $config);\n $result = $apiInstance->getPersonFields();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $count = $result->getData() ? count($result->getData()) : 0;\n $this->recordResult('Get Person Fields', true, \"Retrieved {$count} person fields in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Person Fields', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetActivityFields(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new ActivityFieldsApi(null, $config);\n $result = $apiInstance->getActivityFields();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $count = $result->getData() ? count($result->getData()) : 0;\n $this->recordResult('Get Activity Fields', true, \"Retrieved {$count} activity fields in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Activity Fields', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testV2ApiSupport(SocialAccount $socialAccount): void\n {\n $this->info('Test 4: v2 API Support (Cursor-based Pagination)');\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n\n $startTime = microtime(true);\n\n // Test v2 deals endpoint with cursor pagination\n // Note: The official SDK may or may not have v2 API support\n // This test will determine if v2 is available\n try {\n $apiInstance = new DealsApi(null, $config);\n $updatedSince = Carbon::now()->subDays(30)->toRfc3339String();\n $result = $apiInstance->getDealsSummary();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n\n // Check if response has cursor information (v2 indicator)\n // The response object doesn't have getAdditionalData method\n // Just test if the call works - v2 API support is limited in official SDK v1\n $this->recordResult('v2 API Support', false, \"v1 API works but v2 cursor pagination not available in official SDK v1. Retrieved summary in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('v2 API Support', false, $e->getMessage(), $elapsed);\n }\n } catch (Exception $e) {\n $this->recordResult('v2 API Support', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testErrorHandling(SocialAccount $socialAccount): void\n {\n $this->info('Test 5: Error Handling');\n\n try {\n // Test 5a: Invalid Token\n $this->testInvalidToken();\n\n // Test 5b: Non-existent Resource\n $this->testNonExistentResource($socialAccount);\n } catch (Exception $e) {\n $this->recordResult('Error Handling', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testInvalidToken(): void\n {\n $startTime = microtime(true);\n\n try {\n $config = new Configuration();\n $config->setAccessToken('invalid_token');\n $client = new DealsApi(null, $config);\n\n $client->getDealsSummary();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Invalid Token Error', false, \"Expected error but request succeeded in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Invalid Token Error', true, \"Correctly threw exception: {$e->getMessage()} in {$elapsed}ms\", $elapsed);\n }\n }\n\n private function testNonExistentResource(SocialAccount $socialAccount): void\n {\n $startTime = microtime(true);\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n $client = new DealsApi(null, $config);\n\n // Try to get a deal with a very high ID that likely doesn't exist\n // Note: Official SDK doesn't have a simple getDeal method, so skip this\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Non-existent Resource Error', false, \"Method not available in official SDK - API structure different\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Non-existent Resource Error', true, \"Correctly threw exception: {$e->getMessage()} in {$elapsed}ms\", $elapsed);\n }\n }\n\n private function recordResult(string $testName, bool $success, string $message, float $elapsed): void\n {\n $this->results[] = [\n 'test' => $testName,\n 'success' => $success,\n 'message' => $message,\n 'elapsed' => $elapsed,\n ];\n\n $status = $success ? '✅ PASS' : '❌ FAIL';\n $this->line(\" {$status} - {$testName}: {$message}\");\n }\n\n private function displayResults(): void\n {\n $this->newLine();\n $this->info('=== POC Test Results Summary ===');\n $this->newLine();\n\n $total = count($this->results);\n $passed = count(array_filter($this->results, fn ($r) => $r['success']));\n $failed = $total - $passed;\n\n $this->line(\"Total Tests: {$total}\");\n $this->line(\"Passed: {$passed}\");\n $this->line(\"Failed: {$failed}\");\n $this->newLine();\n\n $totalTime = array_sum(array_column($this->results, 'elapsed'));\n $this->line(\"Total Execution Time: {$totalTime}ms\");\n $this->newLine();\n\n if ($failed > 0) {\n $this->warn('Failed Tests:');\n foreach ($this->results as $result) {\n if (! $result['success']) {\n $this->line(\" - {$result['test']}: {$result['message']}\");\n }\n }\n $this->newLine();\n }\n\n $this->info('=== Detailed Results ===');\n foreach ($this->results as $result) {\n $status = $result['success'] ? '✅' : '❌';\n $this->line(\"{$status} {$result['test']} ({$result['elapsed']}ms): {$result['message']}\");\n }\n\n $this->newLine();\n $this->info('=== Recommendations ===');\n\n if ($passed === $total) {\n $this->info('✅ All tests passed! The official SDK is viable for migration.');\n $this->info('Next steps: Proceed with full migration planning.');\n } else {\n $this->warn('⚠️ Some tests failed. Review the failures above before proceeding.');\n $this->warn('Consider hybrid approach or investigate alternatives if critical operations fail.');\n }\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Console\\Commands\\Crm;\n\nuse Carbon\\Carbon;\nuse Exception;\nuse Illuminate\\Console\\Command;\nuse Jiminny\\Component\\Encryption\\EncryptedTokenManager;\nuse Jiminny\\Models\\SocialAccount;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Pipedrive\\versions\\v1\\Api\\ActivitiesApi;\nuse Pipedrive\\versions\\v1\\Api\\ActivityFieldsApi;\nuse Pipedrive\\versions\\v1\\Api\\DealFieldsApi;\nuse Pipedrive\\versions\\v1\\Api\\DealsApi;\nuse Pipedrive\\versions\\v1\\Api\\PersonFieldsApi;\nuse Pipedrive\\versions\\v1\\Api\\PersonsApi;\nuse Pipedrive\\versions\\v1\\Configuration;\n\nclass TestPipedriveOfficialSdkCommand extends Command\n{\n protected $signature = 'jiminny:test-pipedrive-official-sdk {teamId}';\n\n protected $description = 'Test official Pipedrive SDK (pipedrive/pipedrive) with existing OAuth tokens';\n\n private array $results = [];\n\n private EncryptedTokenManager $tokenManager;\n\n public function __construct(EncryptedTokenManager $tokenManager)\n {\n parent::__construct();\n $this->tokenManager = $tokenManager;\n }\n\n public function handle(): int\n {\n $teamId = (int) $this->argument('teamId');\n\n $this->info(\"Starting Pipedrive Official SDK POC for Team ID: {$teamId}\");\n $this->newLine();\n\n try {\n $this->runTests($teamId);\n } catch (Exception $e) {\n $this->error(\"POC failed with error: {$e->getMessage()}\");\n $this->error($e->getTraceAsString());\n\n return 1;\n }\n\n $this->displayResults();\n\n return 0;\n }\n\n private function runTests(int $teamId): void\n {\n $team = Team::find($teamId);\n if (! $team) {\n throw new Exception(\"Team with ID {$teamId} not found\");\n }\n\n $socialAccount = SocialAccount::where('provider', SocialAccount::PROVIDER_PIPEDRIVE)\n ->whereHas('sociable', function ($query) use ($team) {\n $query->where('team_id', $team->id);\n })\n ->orderByDesc('expires') // Get the most recently expiring token\n ->first();\n\n if (! $socialAccount) {\n throw new Exception(\"No Pipedrive social account found for team {$teamId}\");\n }\n\n $this->info(\"Found Pipedrive account for team: {$team->name}\");\n $this->newLine();\n\n // Test 1: OAuth Initialization\n $this->testOAuthInitialization($socialAccount);\n\n // Test 2: Basic CRUD Operations\n $this->testBasicCrudOperations($socialAccount);\n\n // Test 3: Field Metadata\n $this->testFieldMetadata($socialAccount);\n\n // Test 4: v2 API Support\n $this->testV2ApiSupport($socialAccount);\n\n // Test 5: Error Handling\n $this->testErrorHandling($socialAccount);\n }\n\n private function testOAuthInitialization(SocialAccount $socialAccount): void\n {\n $this->info('Test 1: OAuth Initialization');\n $startTime = microtime(true);\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n\n $client = new DealsApi(null, $config);\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('OAuth Initialization', true, \"Client initialized successfully in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('OAuth Initialization', false, $e->getMessage(), $elapsed);\n }\n\n $this->newLine();\n }\n\n private function testBasicCrudOperations(SocialAccount $socialAccount): void\n {\n $this->info('Test 2: Basic CRUD Operations');\n\n try {\n // Get the raw token from database\n $rawDbToken = $socialAccount->getProviderUserToken();\n $this->info(\"DB token (length: \" . strlen($rawDbToken) . \")\");\n $this->info(\"DB token preview: \" . substr($rawDbToken, 0, 20) . \"...\");\n\n // Check token expiry\n $expires = $socialAccount->expires;\n $this->info(\"Token expires at: \" . ($expires ? Carbon::createFromTimestamp($expires)->toDateTimeString() : 'null'));\n $this->info(\"Token is expired: \" . ($expires && $expires < time() ? 'YES' : 'NO'));\n\n // Test with the DB token directly\n $this->info(\"\\n--- Testing with DB token ---\");\n $config = new Configuration();\n $config->setAccessToken($rawDbToken);\n\n // Test 2a: Get Deals\n $this->testGetDeals($config);\n\n // Test 2b: Get Persons\n $this->testGetPersons($config);\n\n // Test 2c: Get Activities\n $this->testGetActivities($config);\n } catch (Exception $e) {\n $this->recordResult('Basic CRUD Operations', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testCurrentSystemApproach(SocialAccount $socialAccount): void\n {\n $startTime = microtime(true);\n\n try {\n // Simulate how current system makes requests\n $token = $socialAccount->getProviderUserToken();\n $url = 'https://api.pipedrive.com/api/v2/deals';\n\n $headers = [\n 'Authorization' => 'Bearer ' . $token,\n 'Accept' => 'application/json',\n ];\n\n $client = new \\GuzzleHttp\\Client();\n $response = $client->get($url, [\n 'headers' => $headers,\n 'timeout' => 30,\n ]);\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $statusCode = $response->getStatusCode();\n $this->recordResult('Current System v2 API', $statusCode === 200, \"Status: {$statusCode} in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Current System v2 API', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetDeals(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new DealsApi(null, $config);\n // Official SDK uses getDealsSummary instead of getDeals\n $result = $apiInstance->getDealsSummary();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Deals Summary', true, \"Retrieved deals summary in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Deals Summary', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetPersons(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n // Official SDK doesn't have a simple getPersons method\n // Skip this test for now as the API structure is different\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Persons', false, \"Method not available in official SDK - API structure different\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Persons', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetActivities(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n // Official SDK doesn't have a simple getActivities method\n // Skip this test for now as the API structure is different\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Activities', false, \"Method not available in official SDK - API structure different\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Activities', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testFieldMetadata(SocialAccount $socialAccount): void\n {\n $this->info('Test 3: Field Metadata');\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n\n // Test 3a: Get Deal Fields\n $this->testGetDealFields($config);\n\n // Test 3b: Get Person Fields\n $this->testGetPersonFields($config);\n\n // Test 3c: Get Activity Fields\n $this->testGetActivityFields($config);\n } catch (Exception $e) {\n $this->recordResult('Field Metadata', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testGetDealFields(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new DealFieldsApi(null, $config);\n $result = $apiInstance->getDealFields();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $count = $result->getData() ? count($result->getData()) : 0;\n $this->recordResult('Get Deal Fields', true, \"Retrieved {$count} deal fields in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Deal Fields', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetPersonFields(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new PersonFieldsApi(null, $config);\n $result = $apiInstance->getPersonFields();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $count = $result->getData() ? count($result->getData()) : 0;\n $this->recordResult('Get Person Fields', true, \"Retrieved {$count} person fields in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Person Fields', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetActivityFields(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new ActivityFieldsApi(null, $config);\n $result = $apiInstance->getActivityFields();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $count = $result->getData() ? count($result->getData()) : 0;\n $this->recordResult('Get Activity Fields', true, \"Retrieved {$count} activity fields in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Activity Fields', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testV2ApiSupport(SocialAccount $socialAccount): void\n {\n $this->info('Test 4: v2 API Support (Cursor-based Pagination)');\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n\n $startTime = microtime(true);\n\n // Test v2 deals endpoint with cursor pagination\n // Note: The official SDK may or may not have v2 API support\n // This test will determine if v2 is available\n try {\n $apiInstance = new DealsApi(null, $config);\n $updatedSince = Carbon::now()->subDays(30)->toRfc3339String();\n $result = $apiInstance->getDealsSummary();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n\n // Check if response has cursor information (v2 indicator)\n // The response object doesn't have getAdditionalData method\n // Just test if the call works - v2 API support is limited in official SDK v1\n $this->recordResult('v2 API Support', false, \"v1 API works but v2 cursor pagination not available in official SDK v1. Retrieved summary in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('v2 API Support', false, $e->getMessage(), $elapsed);\n }\n } catch (Exception $e) {\n $this->recordResult('v2 API Support', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testErrorHandling(SocialAccount $socialAccount): void\n {\n $this->info('Test 5: Error Handling');\n\n try {\n // Test 5a: Invalid Token\n $this->testInvalidToken();\n\n // Test 5b: Non-existent Resource\n $this->testNonExistentResource($socialAccount);\n } catch (Exception $e) {\n $this->recordResult('Error Handling', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testInvalidToken(): void\n {\n $startTime = microtime(true);\n\n try {\n $config = new Configuration();\n $config->setAccessToken('invalid_token');\n $client = new DealsApi(null, $config);\n\n $client->getDealsSummary();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Invalid Token Error', false, \"Expected error but request succeeded in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Invalid Token Error', true, \"Correctly threw exception: {$e->getMessage()} in {$elapsed}ms\", $elapsed);\n }\n }\n\n private function testNonExistentResource(SocialAccount $socialAccount): void\n {\n $startTime = microtime(true);\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n $client = new DealsApi(null, $config);\n\n // Try to get a deal with a very high ID that likely doesn't exist\n // Note: Official SDK doesn't have a simple getDeal method, so skip this\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Non-existent Resource Error', false, \"Method not available in official SDK - API structure different\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Non-existent Resource Error', true, \"Correctly threw exception: {$e->getMessage()} in {$elapsed}ms\", $elapsed);\n }\n }\n\n private function recordResult(string $testName, bool $success, string $message, float $elapsed): void\n {\n $this->results[] = [\n 'test' => $testName,\n 'success' => $success,\n 'message' => $message,\n 'elapsed' => $elapsed,\n ];\n\n $status = $success ? '✅ PASS' : '❌ FAIL';\n $this->line(\" {$status} - {$testName}: {$message}\");\n }\n\n private function displayResults(): void\n {\n $this->newLine();\n $this->info('=== POC Test Results Summary ===');\n $this->newLine();\n\n $total = count($this->results);\n $passed = count(array_filter($this->results, fn ($r) => $r['success']));\n $failed = $total - $passed;\n\n $this->line(\"Total Tests: {$total}\");\n $this->line(\"Passed: {$passed}\");\n $this->line(\"Failed: {$failed}\");\n $this->newLine();\n\n $totalTime = array_sum(array_column($this->results, 'elapsed'));\n $this->line(\"Total Execution Time: {$totalTime}ms\");\n $this->newLine();\n\n if ($failed > 0) {\n $this->warn('Failed Tests:');\n foreach ($this->results as $result) {\n if (! $result['success']) {\n $this->line(\" - {$result['test']}: {$result['message']}\");\n }\n }\n $this->newLine();\n }\n\n $this->info('=== Detailed Results ===');\n foreach ($this->results as $result) {\n $status = $result['success'] ? '✅' : '❌';\n $this->line(\"{$status} {$result['test']} ({$result['elapsed']}ms): {$result['message']}\");\n }\n\n $this->newLine();\n $this->info('=== Recommendations ===');\n\n if ($passed === $total) {\n $this->info('✅ All tests passed! The official SDK is viable for migration.');\n $this->info('Next steps: Proceed with full migration planning.');\n } else {\n $this->warn('⚠️ Some tests failed. Review the failures above before proceeding.');\n $this->warn('Consider hybrid approach or investigate alternatives if critical operations fail.');\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}]...
|
5365831007722038384
|
61370652661533572
|
idle
|
accessibility
|
NULL
|
Project: faVsco.js, menu
pipedrive-sdk-poc, menu
S Project: faVsco.js, menu
pipedrive-sdk-poc, 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\Console\Commands\Crm;
use Jiminny\Console\Commands\Command;
use Jiminny\Console\Commands\Crm\Traits\SyncObjectsCommandTrait;
use Jiminny\Jobs\Crm\SyncObjects as SyncObjectsJob;
use Jiminny\Jobs\Job;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Models\Team;
class SyncObjects extends Command
{
use SyncObjectsCommandTrait;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'crm:sync-objects {team?} {--sync}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Sync remote CRM objects.';
/**
* Execute the console command.
*/
public function handle(): void
{
$teamId = $this->argument('team');
$teams = [];
if ($teamId) {
$team = Team::idOrUuId($teamId);
if ($team && $team->crm?->provider === Configuration::PROVIDER_HUBSPOT) {
$this->error(sprintf(
'Team %s (%s) uses HubSpot. Use crm:sync-hubspot-objects instead.',
$team->getName(),
$team->getUuid()
));
return;
}
if ($team) {
$teams[] = $team;
}
} else {
// Exclude HubSpot teams - handled by crm:sync-hubspot-objects
$teams = Team::where('status', Team::STATUS_ACTIVE)
->whereHas(
'crm',
fn ($q) => $q
->where('provider', '!=', Configuration::PROVIDER_HUBSPOT)
->where('sync_objects', true)
)
->get();
}
$this->dispatchSyncJobsForTeams($teams);
}
protected function getStaggerDelaySeconds(): float
{
return 2;
}
protected function getMaxDelaySeconds(): int
{
// Cap delay at 15 minutes (SQS max delay limit)
// capacity of 450 teams before reach
return 900;
}
protected function getLogPrefix(): string
{
return '';
}
protected function createSyncJob(Team $team): Job
{
return new SyncObjectsJob($team);
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
1
51
1
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Console\Commands\Crm;
use Carbon\Carbon;
use Exception;
use Illuminate\Console\Command;
use Jiminny\Component\Encryption\EncryptedTokenManager;
use Jiminny\Models\SocialAccount;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Pipedrive\versions\v1\Api\ActivitiesApi;
use Pipedrive\versions\v1\Api\ActivityFieldsApi;
use Pipedrive\versions\v1\Api\DealFieldsApi;
use Pipedrive\versions\v1\Api\DealsApi;
use Pipedrive\versions\v1\Api\PersonFieldsApi;
use Pipedrive\versions\v1\Api\PersonsApi;
use Pipedrive\versions\v1\Configuration;
class TestPipedriveOfficialSdkCommand extends Command
{
protected $signature = 'jiminny:test-pipedrive-official-sdk {teamId}';
protected $description = 'Test official Pipedrive SDK (pipedrive/pipedrive) with existing OAuth tokens';
private array $results = [];
private EncryptedTokenManager $tokenManager;
public function __construct(EncryptedTokenManager $tokenManager)
{
parent::__construct();
$this->tokenManager = $tokenManager;
}
public function handle(): int
{
$teamId = (int) $this->argument('teamId');
$this->info("Starting Pipedrive Official SDK POC for Team ID: {$teamId}");
$this->newLine();
try {
$this->runTests($teamId);
} catch (Exception $e) {
$this->error("POC failed with error: {$e->getMessage()}");
$this->error($e->getTraceAsString());
return 1;
}
$this->displayResults();
return 0;
}
private function runTests(int $teamId): void
{
$team = Team::find($teamId);
if (! $team) {
throw new Exception("Team with ID {$teamId} not found");
}
$socialAccount = SocialAccount::where('provider', SocialAccount::PROVIDER_PIPEDRIVE)
->whereHas('sociable', function ($query) use ($team) {
$query->where('team_id', $team->id);
})
->orderByDesc('expires') // Get the most recently expiring token
->first();
if (! $socialAccount) {
throw new Exception("No Pipedrive social account found for team {$teamId}");
}
$this->info("Found Pipedrive account for team: {$team->name}");
$this->newLine();
// Test 1: OAuth Initialization
$this->testOAuthInitialization($socialAccount);
// Test 2: Basic CRUD Operations
$this->testBasicCrudOperations($socialAccount);
// Test 3: Field Metadata
$this->testFieldMetadata($socialAccount);
// Test 4: v2 API Support
$this->testV2ApiSupport($socialAccount);
// Test 5: Error Handling
$this->testErrorHandling($socialAccount);
}
private function testOAuthInitialization(SocialAccount $socialAccount): void
{
$this->info('Test 1: OAuth Initialization');
$startTime = microtime(true);
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
$client = new DealsApi(null, $config);
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('OAuth Initialization', true, "Client initialized successfully in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('OAuth Initialization', false, $e->getMessage(), $elapsed);
}
$this->newLine();
}
private function testBasicCrudOperations(SocialAccount $socialAccount): void
{
$this->info('Test 2: Basic CRUD Operations');
try {
// Get the raw token from database
$rawDbToken = $socialAccount->getProviderUserToken();
$this->info("DB token (length: " . strlen($rawDbToken) . ")");
$this->info("DB token preview: " . substr($rawDbToken, 0, 20) . "...");
// Check token expiry
$expires = $socialAccount->expires;
$this->info("Token expires at: " . ($expires ? Carbon::createFromTimestamp($expires)->toDateTimeString() : 'null'));
$this->info("Token is expired: " . ($expires && $expires < time() ? 'YES' : 'NO'));
// Test with the DB token directly
$this->info("\n--- Testing with DB token ---");
$config = new Configuration();
$config->setAccessToken($rawDbToken);
// Test 2a: Get Deals
$this->testGetDeals($config);
// Test 2b: Get Persons
$this->testGetPersons($config);
// Test 2c: Get Activities
$this->testGetActivities($config);
} catch (Exception $e) {
$this->recordResult('Basic CRUD Operations', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testCurrentSystemApproach(SocialAccount $socialAccount): void
{
$startTime = microtime(true);
try {
// Simulate how current system makes requests
$token = $socialAccount->getProviderUserToken();
$url = 'https://api.pipedrive.com/api/v2/deals';
$headers = [
'Authorization' => 'Bearer ' . $token,
'Accept' => 'application/json',
];
$client = new \GuzzleHttp\Client();
$response = $client->get($url, [
'headers' => $headers,
'timeout' => 30,
]);
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$statusCode = $response->getStatusCode();
$this->recordResult('Current System v2 API', $statusCode === 200, "Status: {$statusCode} in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Current System v2 API', false, $e->getMessage(), $elapsed);
}
}
private function testGetDeals(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new DealsApi(null, $config);
// Official SDK uses getDealsSummary instead of getDeals
$result = $apiInstance->getDealsSummary();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Deals Summary', true, "Retrieved deals summary in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Deals Summary', false, $e->getMessage(), $elapsed);
}
}
private function testGetPersons(Configuration $config): void
{
$startTime = microtime(true);
try {
// Official SDK doesn't have a simple getPersons method
// Skip this test for now as the API structure is different
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Persons', false, "Method not available in official SDK - API structure different", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Persons', false, $e->getMessage(), $elapsed);
}
}
private function testGetActivities(Configuration $config): void
{
$startTime = microtime(true);
try {
// Official SDK doesn't have a simple getActivities method
// Skip this test for now as the API structure is different
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Activities', false, "Method not available in official SDK - API structure different", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Activities', false, $e->getMessage(), $elapsed);
}
}
private function testFieldMetadata(SocialAccount $socialAccount): void
{
$this->info('Test 3: Field Metadata');
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
// Test 3a: Get Deal Fields
$this->testGetDealFields($config);
// Test 3b: Get Person Fields
$this->testGetPersonFields($config);
// Test 3c: Get Activity Fields
$this->testGetActivityFields($config);
} catch (Exception $e) {
$this->recordResult('Field Metadata', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testGetDealFields(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new DealFieldsApi(null, $config);
$result = $apiInstance->getDealFields();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$count = $result->getData() ? count($result->getData()) : 0;
$this->recordResult('Get Deal Fields', true, "Retrieved {$count} deal fields in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Deal Fields', false, $e->getMessage(), $elapsed);
}
}
private function testGetPersonFields(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new PersonFieldsApi(null, $config);
$result = $apiInstance->getPersonFields();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$count = $result->getData() ? count($result->getData()) : 0;
$this->recordResult('Get Person Fields', true, "Retrieved {$count} person fields in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Person Fields', false, $e->getMessage(), $elapsed);
}
}
private function testGetActivityFields(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new ActivityFieldsApi(null, $config);
$result = $apiInstance->getActivityFields();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$count = $result->getData() ? count($result->getData()) : 0;
$this->recordResult('Get Activity Fields', true, "Retrieved {$count} activity fields in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Activity Fields', false, $e->getMessage(), $elapsed);
}
}
private function testV2ApiSupport(SocialAccount $socialAccount): void
{
$this->info('Test 4: v2 API Support (Cursor-based Pagination)');
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
$startTime = microtime(true);
// Test v2 deals endpoint with cursor pagination
// Note: The official SDK may or may not have v2 API support
// This test will determine if v2 is available
try {
$apiInstance = new DealsApi(null, $config);
$updatedSince = Carbon::now()->subDays(30)->toRfc3339String();
$result = $apiInstance->getDealsSummary();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
// Check if response has cursor information (v2 indicator)
// The response object doesn't have getAdditionalData method
// Just test if the call works - v2 API support is limited in official SDK v1
$this->recordResult('v2 API Support', false, "v1 API works but v2 cursor pagination not available in official SDK v1. Retrieved summary in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('v2 API Support', false, $e->getMessage(), $elapsed);
}
} catch (Exception $e) {
$this->recordResult('v2 API Support', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testErrorHandling(SocialAccount $socialAccount): void
{
$this->info('Test 5: Error Handling');
try {
// Test 5a: Invalid Token
$this->testInvalidToken();
// Test 5b: Non-existent Resource
$this->testNonExistentResource($socialAccount);
} catch (Exception $e) {
$this->recordResult('Error Handling', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testInvalidToken(): void
{
$startTime = microtime(true);
try {
$config = new Configuration();
$config->setAccessToken('invalid_token');
$client = new DealsApi(null, $config);
$client->getDealsSummary();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Invalid Token Error', false, "Expected error but request succeeded in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Invalid Token Error', true, "Correctly threw exception: {$e->getMessage()} in {$elapsed}ms", $elapsed);
}
}
private function testNonExistentResource(SocialAccount $socialAccount): void
{
$startTime = microtime(true);
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
$client = new DealsApi(null, $config);
// Try to get a deal with a very high ID that likely doesn't exist
// Note: Official SDK doesn't have a simple getDeal method, so skip this
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Non-existent Resource Error', false, "Method not available in official SDK - API structure different", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Non-existent Resource Error', true, "Correctly threw exception: {$e->getMessage()} in {$elapsed}ms", $elapsed);
}
}
private function recordResult(string $testName, bool $success, string $message, float $elapsed): void
{
$this->results[] = [
'test' => $testName,
'success' => $success,
'message' => $message,
'elapsed' => $elapsed,
];
$status = $success ? '✅ PASS' : '❌ FAIL';
$this->line(" {$status} - {$testName}: {$message}");
}
private function displayResults(): void
{
$this->newLine();
$this->info('=== POC Test Results Summary ===');
$this->newLine();
$total = count($this->results);
$passed = count(array_filter($this->results, fn ($r) => $r['success']));
$failed = $total - $passed;
$this->line("Total Tests: {$total}");
$this->line("Passed: {$passed}");
$this->line("Failed: {$failed}");
$this->newLine();
$totalTime = array_sum(array_column($this->results, 'elapsed'));
$this->line("Total Execution Time: {$totalTime}ms");
$this->newLine();
if ($failed > 0) {
$this->warn('Failed Tests:');
foreach ($this->results as $result) {
if (! $result['success']) {
$this->line(" - {$result['test']}: {$result['message']}");
}
}
$this->newLine();
}
$this->info('=== Detailed Results ===');
foreach ($this->results as $result) {
$status = $result['success'] ? '✅' : '❌';
$this->line("{$status} {$result['test']} ({$result['elapsed']}ms): {$result['message']}");
}
$this->newLine();
$this->info('=== Recommendations ===');
if ($passed === $total) {
$this->info('✅ All tests passed! The official SDK is viable for migration.');
$this->info('Next steps: Proceed with full migration planning.');
} else {
$this->warn('⚠️ Some tests failed. Review the failures above before proceeding.');
$this->warn('Consider hybrid approach or investigate alternatives if critical operations fail.');
}
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
49394
|
NULL
|
NULL
|
NULL
|
|
49399
|
1763
|
24
|
2026-05-18T06:40:46.940175+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779086446940_m2.jpg...
|
PhpStorm
|
faVsco.js – TestPipedriveOfficialSdkCommand.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
pipedrive-sdk-poc, menu
S Project: faVsco.js, menu
pipedrive-sdk-poc, 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\Console\Commands\Crm;
use Jiminny\Console\Commands\Command;
use Jiminny\Console\Commands\Crm\Traits\SyncObjectsCommandTrait;
use Jiminny\Jobs\Crm\SyncObjects as SyncObjectsJob;
use Jiminny\Jobs\Job;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Models\Team;
class SyncObjects extends Command
{
use SyncObjectsCommandTrait;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'crm:sync-objects {team?} {--sync}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Sync remote CRM objects.';
/**
* Execute the console command.
*/
public function handle(): void
{
$teamId = $this->argument('team');
$teams = [];
if ($teamId) {
$team = Team::idOrUuId($teamId);
if ($team && $team->crm?->provider === Configuration::PROVIDER_HUBSPOT) {
$this->error(sprintf(
'Team %s (%s) uses HubSpot. Use crm:sync-hubspot-objects instead.',
$team->getName(),
$team->getUuid()
));
return;
}
if ($team) {
$teams[] = $team;
}
} else {
// Exclude HubSpot teams - handled by crm:sync-hubspot-objects
$teams = Team::where('status', Team::STATUS_ACTIVE)
->whereHas(
'crm',
fn ($q) => $q
->where('provider', '!=', Configuration::PROVIDER_HUBSPOT)
->where('sync_objects', true)
)
->get();
}
$this->dispatchSyncJobsForTeams($teams);
}
protected function getStaggerDelaySeconds(): float
{
return 2;
}
protected function getMaxDelaySeconds(): int
{
// Cap delay at 15 minutes (SQS max delay limit)
// capacity of 450 teams before reach
return 900;
}
protected function getLogPrefix(): string
{
return '';
}
protected function createSyncJob(Team $team): Job
{
return new SyncObjectsJob($team);
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
1
51
1
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Console\Commands\Crm;
use Carbon\Carbon;
use Exception;
use Illuminate\Console\Command;
use Jiminny\Component\Encryption\EncryptedTokenManager;
use Jiminny\Models\SocialAccount;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Pipedrive\versions\v1\Api\ActivitiesApi;
use Pipedrive\versions\v1\Api\ActivityFieldsApi;
use Pipedrive\versions\v1\Api\DealFieldsApi;
use Pipedrive\versions\v1\Api\DealsApi;
use Pipedrive\versions\v1\Api\PersonFieldsApi;
use Pipedrive\versions\v1\Api\PersonsApi;
use Pipedrive\versions\v1\Configuration;
class TestPipedriveOfficialSdkCommand extends Command
{
protected $signature = 'jiminny:test-pipedrive-official-sdk {teamId}';
protected $description = 'Test official Pipedrive SDK (pipedrive/pipedrive) with existing OAuth tokens';
private array $results = [];
private EncryptedTokenManager $tokenManager;
public function __construct(EncryptedTokenManager $tokenManager)
{
parent::__construct();
$this->tokenManager = $tokenManager;
}
public function handle(): int
{
$teamId = (int) $this->argument('teamId');
$this->info("Starting Pipedrive Official SDK POC for Team ID: {$teamId}");
$this->newLine();
try {
$this->runTests($teamId);
} catch (Exception $e) {
$this->error("POC failed with error: {$e->getMessage()}");
$this->error($e->getTraceAsString());
return 1;
}
$this->displayResults();
return 0;
}
private function runTests(int $teamId): void
{
$team = Team::find($teamId);
if (! $team) {
throw new Exception("Team with ID {$teamId} not found");
}
$socialAccount = SocialAccount::where('provider', SocialAccount::PROVIDER_PIPEDRIVE)
->whereHas('sociable', function ($query) use ($team) {
$query->where('team_id', $team->id);
})
->orderByDesc('expires') // Get the most recently expiring token
->first();
if (! $socialAccount) {
throw new Exception("No Pipedrive social account found for team {$teamId}");
}
$this->info("Found Pipedrive account for team: {$team->name}");
$this->newLine();
// Test 1: OAuth Initialization
$this->testOAuthInitialization($socialAccount);
// Test 2: Basic CRUD Operations
$this->testBasicCrudOperations($socialAccount);
// Test 3: Field Metadata
$this->testFieldMetadata($socialAccount);
// Test 4: v2 API Support
$this->testV2ApiSupport($socialAccount);
// Test 5: Error Handling
$this->testErrorHandling($socialAccount);
}
private function testOAuthInitialization(SocialAccount $socialAccount): void
{
$this->info('Test 1: OAuth Initialization');
$startTime = microtime(true);
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
$client = new DealsApi(null, $config);
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('OAuth Initialization', true, "Client initialized successfully in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('OAuth Initialization', false, $e->getMessage(), $elapsed);
}
$this->newLine();
}
private function testBasicCrudOperations(SocialAccount $socialAccount): void
{
$this->info('Test 2: Basic CRUD Operations');
try {
// Get the raw token from database
$rawDbToken = $socialAccount->getProviderUserToken();
$this->info("DB token (length: " . strlen($rawDbToken) . ")");
$this->info("DB token preview: " . substr($rawDbToken, 0, 20) . "...");
// Check token expiry
$expires = $socialAccount->expires;
$this->info("Token expires at: " . ($expires ? Carbon::createFromTimestamp($expires)->toDateTimeString() : 'null'));
$this->info("Token is expired: " . ($expires && $expires < time() ? 'YES' : 'NO'));
// Test with the DB token directly
$this->info("\n--- Testing with DB token ---");
$config = new Configuration();
$config->setAccessToken($rawDbToken);
// Test 2a: Get Deals
$this->testGetDeals($config);
// Test 2b: Get Persons
$this->testGetPersons($config);
// Test 2c: Get Activities
$this->testGetActivities($config);
} catch (Exception $e) {
$this->recordResult('Basic CRUD Operations', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testCurrentSystemApproach(SocialAccount $socialAccount): void
{
$startTime = microtime(true);
try {
// Simulate how current system makes requests
$token = $socialAccount->getProviderUserToken();
$url = 'https://api.pipedrive.com/api/v2/deals';
$headers = [
'Authorization' => 'Bearer ' . $token,
'Accept' => 'application/json',
];
$client = new \GuzzleHttp\Client();
$response = $client->get($url, [
'headers' => $headers,
'timeout' => 30,
]);
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$statusCode = $response->getStatusCode();
$this->recordResult('Current System v2 API', $statusCode === 200, "Status: {$statusCode} in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Current System v2 API', false, $e->getMessage(), $elapsed);
}
}
private function testGetDeals(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new DealsApi(null, $config);
// Official SDK uses getDealsSummary instead of getDeals
$result = $apiInstance->getDealsSummary();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Deals Summary', true, "Retrieved deals summary in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Deals Summary', false, $e->getMessage(), $elapsed);
}
}
private function testGetPersons(Configuration $config): void
{
$startTime = microtime(true);
try {
// Official SDK doesn't have a simple getPersons method
// Skip this test for now as the API structure is different
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Persons', false, "Method not available in official SDK - API structure different", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Persons', false, $e->getMessage(), $elapsed);
}
}
private function testGetActivities(Configuration $config): void
{
$startTime = microtime(true);
try {
// Official SDK doesn't have a simple getActivities method
// Skip this test for now as the API structure is different
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Activities', false, "Method not available in official SDK - API structure different", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Activities', false, $e->getMessage(), $elapsed);
}
}
private function testFieldMetadata(SocialAccount $socialAccount): void
{
$this->info('Test 3: Field Metadata');
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
// Test 3a: Get Deal Fields
$this->testGetDealFields($config);
// Test 3b: Get Person Fields
$this->testGetPersonFields($config);
// Test 3c: Get Activity Fields
$this->testGetActivityFields($config);
} catch (Exception $e) {
$this->recordResult('Field Metadata', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testGetDealFields(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new DealFieldsApi(null, $config);
$result = $apiInstance->getDealFields();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$count = $result->getData() ? count($result->getData()) : 0;
$this->recordResult('Get Deal Fields', true, "Retrieved {$count} deal fields in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Deal Fields', false, $e->getMessage(), $elapsed);
}
}
private function testGetPersonFields(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new PersonFieldsApi(null, $config);
$result = $apiInstance->getPersonFields();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$count = $result->getData() ? count($result->getData()) : 0;
$this->recordResult('Get Person Fields', true, "Retrieved {$count} person fields in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Person Fields', false, $e->getMessage(), $elapsed);
}
}
private function testGetActivityFields(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new ActivityFieldsApi(null, $config);
$result = $apiInstance->getActivityFields();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$count = $result->getData() ? count($result->getData()) : 0;
$this->recordResult('Get Activity Fields', true, "Retrieved {$count} activity fields in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Activity Fields', false, $e->getMessage(), $elapsed);
}
}
private function testV2ApiSupport(SocialAccount $socialAccount): void
{
$this->info('Test 4: v2 API Support (Cursor-based Pagination)');
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
$startTime = microtime(true);
// Test v2 deals endpoint with cursor pagination
// Note: The official SDK may or may not have v2 API support
// This test will determine if v2 is available
try {
$apiInstance = new DealsApi(null, $config);
$updatedSince = Carbon::now()->subDays(30)->toRfc3339String();
$result = $apiInstance->getDealsSummary();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
// Check if response has cursor information (v2 indicator)
// The response object doesn't have getAdditionalData method
// Just test if the call works - v2 API support is limited in official SDK v1
$this->recordResult('v2 API Support', false, "v1 API works but v2 cursor pagination not available in official SDK v1. Retrieved summary in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('v2 API Support', false, $e->getMessage(), $elapsed);
}
} catch (Exception $e) {
$this->recordResult('v2 API Support', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testErrorHandling(SocialAccount $socialAccount): void
{
$this->info('Test 5: Error Handling');
try {
// Test 5a: Invalid Token
$this->testInvalidToken();
// Test 5b: Non-existent Resource
$this->testNonExistentResource($socialAccount);
} catch (Exception $e) {
$this->recordResult('Error Handling', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testInvalidToken(): void
{
$startTime = microtime(true);
try {
$config = new Configuration();
$config->setAccessToken('invalid_token');
$client = new DealsApi(null, $config);
$client->getDealsSummary();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Invalid Token Error', false, "Expected error but request succeeded in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Invalid Token Error', true, "Correctly threw exception: {$e->getMessage()} in {$elapsed}ms", $elapsed);
}
}
private function testNonExistentResource(SocialAccount $socialAccount): void
{
$startTime = microtime(true);
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
$client = new DealsApi(null, $config);
// Try to get a deal with a very high ID that likely doesn't exist
// Note: Official SDK doesn't have a simple getDeal method, so skip this
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Non-existent Resource Error', false, "Method not available in official SDK - API structure different", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Non-existent Resource Error', true, "Correctly threw exception: {$e->getMessage()} in {$elapsed}ms", $elapsed);
}
}
private function recordResult(string $testName, bool $success, string $message, float $elapsed): void
{
$this->results[] = [
'test' => $testName,
'success' => $success,
'message' => $message,
'elapsed' => $elapsed,
];
$status = $success ? '✅ PASS' : '❌ FAIL';
$this->line(" {$status} - {$testName}: {$message}");
}
private function displayResults(): void
{
$this->newLine();
$this->info('=== POC Test Results Summary ===');
$this->newLine();
$total = count($this->results);
$passed = count(array_filter($this->results, fn ($r) => $r['success']));
$failed = $total - $passed;
$this->line("Total Tests: {$total}");
$this->line("Passed: {$passed}");
$this->line("Failed: {$failed}");
$this->newLine();
$totalTime = array_sum(array_column($this->results, 'elapsed'));
$this->line("Total Execution Time: {$totalTime}ms");
$this->newLine();
if ($failed > 0) {
$this->warn('Failed Tests:');
foreach ($this->results as $result) {
if (! $result['success']) {
$this->line(" - {$result['test']}: {$result['message']}");
}
}
$this->newLine();
}
$this->info('=== Detailed Results ===');
foreach ($this->results as $result) {
$status = $result['success'] ? '✅' : '❌';
$this->line("{$status} {$result['test']} ({$result['elapsed']}ms): {$result['message']}");
}
$this->newLine();
$this->info('=== Recommendations ===');
if ($passed === $total) {
$this->info('✅ All tests passed! The official SDK is viable for migration.');
$this->info('Next steps: Proceed with full migration planning.');
} else {
$this->warn('⚠️ Some tests failed. Review the failures above before proceeding.');
$this->warn('Consider hybrid approach or investigate alternatives if critical operations fail.');
}
}
}
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":"pipedrive-sdk-poc, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.05618351,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: pipedrive-sdk-poc","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\\Console\\Commands\\Crm;\n\nuse Jiminny\\Console\\Commands\\Command;\nuse Jiminny\\Console\\Commands\\Crm\\Traits\\SyncObjectsCommandTrait;\nuse Jiminny\\Jobs\\Crm\\SyncObjects as SyncObjectsJob;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Models\\Team;\n\nclass SyncObjects extends Command\n{\n use SyncObjectsCommandTrait;\n\n /**\n * The name and signature of the console command.\n *\n * @var string\n */\n protected $signature = 'crm:sync-objects {team?} {--sync}';\n\n /**\n * The console command description.\n *\n * @var string\n */\n protected $description = 'Sync remote CRM objects.';\n\n /**\n * Execute the console command.\n */\n public function handle(): void\n {\n $teamId = $this->argument('team');\n $teams = [];\n\n if ($teamId) {\n $team = Team::idOrUuId($teamId);\n if ($team && $team->crm?->provider === Configuration::PROVIDER_HUBSPOT) {\n $this->error(sprintf(\n 'Team %s (%s) uses HubSpot. Use crm:sync-hubspot-objects instead.',\n $team->getName(),\n $team->getUuid()\n ));\n\n return;\n }\n if ($team) {\n $teams[] = $team;\n }\n } else {\n // Exclude HubSpot teams - handled by crm:sync-hubspot-objects\n $teams = Team::where('status', Team::STATUS_ACTIVE)\n ->whereHas(\n 'crm',\n fn ($q) => $q\n ->where('provider', '!=', Configuration::PROVIDER_HUBSPOT)\n ->where('sync_objects', true)\n )\n ->get();\n }\n\n $this->dispatchSyncJobsForTeams($teams);\n }\n\n protected function getStaggerDelaySeconds(): float\n {\n return 2;\n }\n\n protected function getMaxDelaySeconds(): int\n {\n // Cap delay at 15 minutes (SQS max delay limit)\n // capacity of 450 teams before reach\n return 900;\n }\n\n protected function getLogPrefix(): string\n {\n return '';\n }\n\n protected function createSyncJob(Team $team): Job\n {\n return new SyncObjectsJob($team);\n }\n}","depth":4,"bounds":{"left":0.15724733,"top":0.2952913,"width":0.21476063,"height":0.7047087},"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Console\\Commands\\Crm;\n\nuse Jiminny\\Console\\Commands\\Command;\nuse Jiminny\\Console\\Commands\\Crm\\Traits\\SyncObjectsCommandTrait;\nuse Jiminny\\Jobs\\Crm\\SyncObjects as SyncObjectsJob;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Models\\Team;\n\nclass SyncObjects extends Command\n{\n use SyncObjectsCommandTrait;\n\n /**\n * The name and signature of the console command.\n *\n * @var string\n */\n protected $signature = 'crm:sync-objects {team?} {--sync}';\n\n /**\n * The console command description.\n *\n * @var string\n */\n protected $description = 'Sync remote CRM objects.';\n\n /**\n * Execute the console command.\n */\n public function handle(): void\n {\n $teamId = $this->argument('team');\n $teams = [];\n\n if ($teamId) {\n $team = Team::idOrUuId($teamId);\n if ($team && $team->crm?->provider === Configuration::PROVIDER_HUBSPOT) {\n $this->error(sprintf(\n 'Team %s (%s) uses HubSpot. Use crm:sync-hubspot-objects instead.',\n $team->getName(),\n $team->getUuid()\n ));\n\n return;\n }\n if ($team) {\n $teams[] = $team;\n }\n } else {\n // Exclude HubSpot teams - handled by crm:sync-hubspot-objects\n $teams = Team::where('status', Team::STATUS_ACTIVE)\n ->whereHas(\n 'crm',\n fn ($q) => $q\n ->where('provider', '!=', Configuration::PROVIDER_HUBSPOT)\n ->where('sync_objects', true)\n )\n ->get();\n }\n\n $this->dispatchSyncJobsForTeams($teams);\n }\n\n protected function getStaggerDelaySeconds(): float\n {\n return 2;\n }\n\n protected function getMaxDelaySeconds(): int\n {\n // Cap delay at 15 minutes (SQS max delay limit)\n // capacity of 450 teams before reach\n return 900;\n }\n\n protected function getLogPrefix(): string\n {\n return '';\n }\n\n protected function createSyncJob(Team $team): Job\n {\n return new SyncObjectsJob($team);\n }\n}","role_description":"text entry area","is_enabled":true,"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":"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":"AXStaticText","text":"1","depth":4,"bounds":{"left":0.57912236,"top":0.10055866,"width":0.00731383,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"51","depth":4,"bounds":{"left":0.5884308,"top":0.10055866,"width":0.009640957,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"1","depth":4,"bounds":{"left":0.6000665,"top":0.10055866,"width":0.00731383,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.6090425,"top":0.09896249,"width":0.00731383,"height":0.018355945},"on_screen":true,"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.6163564,"top":0.09896249,"width":0.006981383,"height":0.018355945},"on_screen":true,"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\\Console\\Commands\\Crm;\n\nuse Carbon\\Carbon;\nuse Exception;\nuse Illuminate\\Console\\Command;\nuse Jiminny\\Component\\Encryption\\EncryptedTokenManager;\nuse Jiminny\\Models\\SocialAccount;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Pipedrive\\versions\\v1\\Api\\ActivitiesApi;\nuse Pipedrive\\versions\\v1\\Api\\ActivityFieldsApi;\nuse Pipedrive\\versions\\v1\\Api\\DealFieldsApi;\nuse Pipedrive\\versions\\v1\\Api\\DealsApi;\nuse Pipedrive\\versions\\v1\\Api\\PersonFieldsApi;\nuse Pipedrive\\versions\\v1\\Api\\PersonsApi;\nuse Pipedrive\\versions\\v1\\Configuration;\n\nclass TestPipedriveOfficialSdkCommand extends Command\n{\n protected $signature = 'jiminny:test-pipedrive-official-sdk {teamId}';\n\n protected $description = 'Test official Pipedrive SDK (pipedrive/pipedrive) with existing OAuth tokens';\n\n private array $results = [];\n\n private EncryptedTokenManager $tokenManager;\n\n public function __construct(EncryptedTokenManager $tokenManager)\n {\n parent::__construct();\n $this->tokenManager = $tokenManager;\n }\n\n public function handle(): int\n {\n $teamId = (int) $this->argument('teamId');\n\n $this->info(\"Starting Pipedrive Official SDK POC for Team ID: {$teamId}\");\n $this->newLine();\n\n try {\n $this->runTests($teamId);\n } catch (Exception $e) {\n $this->error(\"POC failed with error: {$e->getMessage()}\");\n $this->error($e->getTraceAsString());\n\n return 1;\n }\n\n $this->displayResults();\n\n return 0;\n }\n\n private function runTests(int $teamId): void\n {\n $team = Team::find($teamId);\n if (! $team) {\n throw new Exception(\"Team with ID {$teamId} not found\");\n }\n\n $socialAccount = SocialAccount::where('provider', SocialAccount::PROVIDER_PIPEDRIVE)\n ->whereHas('sociable', function ($query) use ($team) {\n $query->where('team_id', $team->id);\n })\n ->orderByDesc('expires') // Get the most recently expiring token\n ->first();\n\n if (! $socialAccount) {\n throw new Exception(\"No Pipedrive social account found for team {$teamId}\");\n }\n\n $this->info(\"Found Pipedrive account for team: {$team->name}\");\n $this->newLine();\n\n // Test 1: OAuth Initialization\n $this->testOAuthInitialization($socialAccount);\n\n // Test 2: Basic CRUD Operations\n $this->testBasicCrudOperations($socialAccount);\n\n // Test 3: Field Metadata\n $this->testFieldMetadata($socialAccount);\n\n // Test 4: v2 API Support\n $this->testV2ApiSupport($socialAccount);\n\n // Test 5: Error Handling\n $this->testErrorHandling($socialAccount);\n }\n\n private function testOAuthInitialization(SocialAccount $socialAccount): void\n {\n $this->info('Test 1: OAuth Initialization');\n $startTime = microtime(true);\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n\n $client = new DealsApi(null, $config);\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('OAuth Initialization', true, \"Client initialized successfully in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('OAuth Initialization', false, $e->getMessage(), $elapsed);\n }\n\n $this->newLine();\n }\n\n private function testBasicCrudOperations(SocialAccount $socialAccount): void\n {\n $this->info('Test 2: Basic CRUD Operations');\n\n try {\n // Get the raw token from database\n $rawDbToken = $socialAccount->getProviderUserToken();\n $this->info(\"DB token (length: \" . strlen($rawDbToken) . \")\");\n $this->info(\"DB token preview: \" . substr($rawDbToken, 0, 20) . \"...\");\n\n // Check token expiry\n $expires = $socialAccount->expires;\n $this->info(\"Token expires at: \" . ($expires ? Carbon::createFromTimestamp($expires)->toDateTimeString() : 'null'));\n $this->info(\"Token is expired: \" . ($expires && $expires < time() ? 'YES' : 'NO'));\n\n // Test with the DB token directly\n $this->info(\"\\n--- Testing with DB token ---\");\n $config = new Configuration();\n $config->setAccessToken($rawDbToken);\n\n // Test 2a: Get Deals\n $this->testGetDeals($config);\n\n // Test 2b: Get Persons\n $this->testGetPersons($config);\n\n // Test 2c: Get Activities\n $this->testGetActivities($config);\n } catch (Exception $e) {\n $this->recordResult('Basic CRUD Operations', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testCurrentSystemApproach(SocialAccount $socialAccount): void\n {\n $startTime = microtime(true);\n\n try {\n // Simulate how current system makes requests\n $token = $socialAccount->getProviderUserToken();\n $url = 'https://api.pipedrive.com/api/v2/deals';\n\n $headers = [\n 'Authorization' => 'Bearer ' . $token,\n 'Accept' => 'application/json',\n ];\n\n $client = new \\GuzzleHttp\\Client();\n $response = $client->get($url, [\n 'headers' => $headers,\n 'timeout' => 30,\n ]);\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $statusCode = $response->getStatusCode();\n $this->recordResult('Current System v2 API', $statusCode === 200, \"Status: {$statusCode} in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Current System v2 API', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetDeals(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new DealsApi(null, $config);\n // Official SDK uses getDealsSummary instead of getDeals\n $result = $apiInstance->getDealsSummary();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Deals Summary', true, \"Retrieved deals summary in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Deals Summary', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetPersons(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n // Official SDK doesn't have a simple getPersons method\n // Skip this test for now as the API structure is different\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Persons', false, \"Method not available in official SDK - API structure different\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Persons', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetActivities(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n // Official SDK doesn't have a simple getActivities method\n // Skip this test for now as the API structure is different\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Activities', false, \"Method not available in official SDK - API structure different\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Activities', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testFieldMetadata(SocialAccount $socialAccount): void\n {\n $this->info('Test 3: Field Metadata');\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n\n // Test 3a: Get Deal Fields\n $this->testGetDealFields($config);\n\n // Test 3b: Get Person Fields\n $this->testGetPersonFields($config);\n\n // Test 3c: Get Activity Fields\n $this->testGetActivityFields($config);\n } catch (Exception $e) {\n $this->recordResult('Field Metadata', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testGetDealFields(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new DealFieldsApi(null, $config);\n $result = $apiInstance->getDealFields();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $count = $result->getData() ? count($result->getData()) : 0;\n $this->recordResult('Get Deal Fields', true, \"Retrieved {$count} deal fields in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Deal Fields', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetPersonFields(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new PersonFieldsApi(null, $config);\n $result = $apiInstance->getPersonFields();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $count = $result->getData() ? count($result->getData()) : 0;\n $this->recordResult('Get Person Fields', true, \"Retrieved {$count} person fields in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Person Fields', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetActivityFields(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new ActivityFieldsApi(null, $config);\n $result = $apiInstance->getActivityFields();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $count = $result->getData() ? count($result->getData()) : 0;\n $this->recordResult('Get Activity Fields', true, \"Retrieved {$count} activity fields in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Activity Fields', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testV2ApiSupport(SocialAccount $socialAccount): void\n {\n $this->info('Test 4: v2 API Support (Cursor-based Pagination)');\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n\n $startTime = microtime(true);\n\n // Test v2 deals endpoint with cursor pagination\n // Note: The official SDK may or may not have v2 API support\n // This test will determine if v2 is available\n try {\n $apiInstance = new DealsApi(null, $config);\n $updatedSince = Carbon::now()->subDays(30)->toRfc3339String();\n $result = $apiInstance->getDealsSummary();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n\n // Check if response has cursor information (v2 indicator)\n // The response object doesn't have getAdditionalData method\n // Just test if the call works - v2 API support is limited in official SDK v1\n $this->recordResult('v2 API Support', false, \"v1 API works but v2 cursor pagination not available in official SDK v1. Retrieved summary in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('v2 API Support', false, $e->getMessage(), $elapsed);\n }\n } catch (Exception $e) {\n $this->recordResult('v2 API Support', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testErrorHandling(SocialAccount $socialAccount): void\n {\n $this->info('Test 5: Error Handling');\n\n try {\n // Test 5a: Invalid Token\n $this->testInvalidToken();\n\n // Test 5b: Non-existent Resource\n $this->testNonExistentResource($socialAccount);\n } catch (Exception $e) {\n $this->recordResult('Error Handling', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testInvalidToken(): void\n {\n $startTime = microtime(true);\n\n try {\n $config = new Configuration();\n $config->setAccessToken('invalid_token');\n $client = new DealsApi(null, $config);\n\n $client->getDealsSummary();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Invalid Token Error', false, \"Expected error but request succeeded in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Invalid Token Error', true, \"Correctly threw exception: {$e->getMessage()} in {$elapsed}ms\", $elapsed);\n }\n }\n\n private function testNonExistentResource(SocialAccount $socialAccount): void\n {\n $startTime = microtime(true);\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n $client = new DealsApi(null, $config);\n\n // Try to get a deal with a very high ID that likely doesn't exist\n // Note: Official SDK doesn't have a simple getDeal method, so skip this\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Non-existent Resource Error', false, \"Method not available in official SDK - API structure different\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Non-existent Resource Error', true, \"Correctly threw exception: {$e->getMessage()} in {$elapsed}ms\", $elapsed);\n }\n }\n\n private function recordResult(string $testName, bool $success, string $message, float $elapsed): void\n {\n $this->results[] = [\n 'test' => $testName,\n 'success' => $success,\n 'message' => $message,\n 'elapsed' => $elapsed,\n ];\n\n $status = $success ? '✅ PASS' : '❌ FAIL';\n $this->line(\" {$status} - {$testName}: {$message}\");\n }\n\n private function displayResults(): void\n {\n $this->newLine();\n $this->info('=== POC Test Results Summary ===');\n $this->newLine();\n\n $total = count($this->results);\n $passed = count(array_filter($this->results, fn ($r) => $r['success']));\n $failed = $total - $passed;\n\n $this->line(\"Total Tests: {$total}\");\n $this->line(\"Passed: {$passed}\");\n $this->line(\"Failed: {$failed}\");\n $this->newLine();\n\n $totalTime = array_sum(array_column($this->results, 'elapsed'));\n $this->line(\"Total Execution Time: {$totalTime}ms\");\n $this->newLine();\n\n if ($failed > 0) {\n $this->warn('Failed Tests:');\n foreach ($this->results as $result) {\n if (! $result['success']) {\n $this->line(\" - {$result['test']}: {$result['message']}\");\n }\n }\n $this->newLine();\n }\n\n $this->info('=== Detailed Results ===');\n foreach ($this->results as $result) {\n $status = $result['success'] ? '✅' : '❌';\n $this->line(\"{$status} {$result['test']} ({$result['elapsed']}ms): {$result['message']}\");\n }\n\n $this->newLine();\n $this->info('=== Recommendations ===');\n\n if ($passed === $total) {\n $this->info('✅ All tests passed! The official SDK is viable for migration.');\n $this->info('Next steps: Proceed with full migration planning.');\n } else {\n $this->warn('⚠️ Some tests failed. Review the failures above before proceeding.');\n $this->warn('Consider hybrid approach or investigate alternatives if critical operations fail.');\n }\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Console\\Commands\\Crm;\n\nuse Carbon\\Carbon;\nuse Exception;\nuse Illuminate\\Console\\Command;\nuse Jiminny\\Component\\Encryption\\EncryptedTokenManager;\nuse Jiminny\\Models\\SocialAccount;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Pipedrive\\versions\\v1\\Api\\ActivitiesApi;\nuse Pipedrive\\versions\\v1\\Api\\ActivityFieldsApi;\nuse Pipedrive\\versions\\v1\\Api\\DealFieldsApi;\nuse Pipedrive\\versions\\v1\\Api\\DealsApi;\nuse Pipedrive\\versions\\v1\\Api\\PersonFieldsApi;\nuse Pipedrive\\versions\\v1\\Api\\PersonsApi;\nuse Pipedrive\\versions\\v1\\Configuration;\n\nclass TestPipedriveOfficialSdkCommand extends Command\n{\n protected $signature = 'jiminny:test-pipedrive-official-sdk {teamId}';\n\n protected $description = 'Test official Pipedrive SDK (pipedrive/pipedrive) with existing OAuth tokens';\n\n private array $results = [];\n\n private EncryptedTokenManager $tokenManager;\n\n public function __construct(EncryptedTokenManager $tokenManager)\n {\n parent::__construct();\n $this->tokenManager = $tokenManager;\n }\n\n public function handle(): int\n {\n $teamId = (int) $this->argument('teamId');\n\n $this->info(\"Starting Pipedrive Official SDK POC for Team ID: {$teamId}\");\n $this->newLine();\n\n try {\n $this->runTests($teamId);\n } catch (Exception $e) {\n $this->error(\"POC failed with error: {$e->getMessage()}\");\n $this->error($e->getTraceAsString());\n\n return 1;\n }\n\n $this->displayResults();\n\n return 0;\n }\n\n private function runTests(int $teamId): void\n {\n $team = Team::find($teamId);\n if (! $team) {\n throw new Exception(\"Team with ID {$teamId} not found\");\n }\n\n $socialAccount = SocialAccount::where('provider', SocialAccount::PROVIDER_PIPEDRIVE)\n ->whereHas('sociable', function ($query) use ($team) {\n $query->where('team_id', $team->id);\n })\n ->orderByDesc('expires') // Get the most recently expiring token\n ->first();\n\n if (! $socialAccount) {\n throw new Exception(\"No Pipedrive social account found for team {$teamId}\");\n }\n\n $this->info(\"Found Pipedrive account for team: {$team->name}\");\n $this->newLine();\n\n // Test 1: OAuth Initialization\n $this->testOAuthInitialization($socialAccount);\n\n // Test 2: Basic CRUD Operations\n $this->testBasicCrudOperations($socialAccount);\n\n // Test 3: Field Metadata\n $this->testFieldMetadata($socialAccount);\n\n // Test 4: v2 API Support\n $this->testV2ApiSupport($socialAccount);\n\n // Test 5: Error Handling\n $this->testErrorHandling($socialAccount);\n }\n\n private function testOAuthInitialization(SocialAccount $socialAccount): void\n {\n $this->info('Test 1: OAuth Initialization');\n $startTime = microtime(true);\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n\n $client = new DealsApi(null, $config);\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('OAuth Initialization', true, \"Client initialized successfully in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('OAuth Initialization', false, $e->getMessage(), $elapsed);\n }\n\n $this->newLine();\n }\n\n private function testBasicCrudOperations(SocialAccount $socialAccount): void\n {\n $this->info('Test 2: Basic CRUD Operations');\n\n try {\n // Get the raw token from database\n $rawDbToken = $socialAccount->getProviderUserToken();\n $this->info(\"DB token (length: \" . strlen($rawDbToken) . \")\");\n $this->info(\"DB token preview: \" . substr($rawDbToken, 0, 20) . \"...\");\n\n // Check token expiry\n $expires = $socialAccount->expires;\n $this->info(\"Token expires at: \" . ($expires ? Carbon::createFromTimestamp($expires)->toDateTimeString() : 'null'));\n $this->info(\"Token is expired: \" . ($expires && $expires < time() ? 'YES' : 'NO'));\n\n // Test with the DB token directly\n $this->info(\"\\n--- Testing with DB token ---\");\n $config = new Configuration();\n $config->setAccessToken($rawDbToken);\n\n // Test 2a: Get Deals\n $this->testGetDeals($config);\n\n // Test 2b: Get Persons\n $this->testGetPersons($config);\n\n // Test 2c: Get Activities\n $this->testGetActivities($config);\n } catch (Exception $e) {\n $this->recordResult('Basic CRUD Operations', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testCurrentSystemApproach(SocialAccount $socialAccount): void\n {\n $startTime = microtime(true);\n\n try {\n // Simulate how current system makes requests\n $token = $socialAccount->getProviderUserToken();\n $url = 'https://api.pipedrive.com/api/v2/deals';\n\n $headers = [\n 'Authorization' => 'Bearer ' . $token,\n 'Accept' => 'application/json',\n ];\n\n $client = new \\GuzzleHttp\\Client();\n $response = $client->get($url, [\n 'headers' => $headers,\n 'timeout' => 30,\n ]);\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $statusCode = $response->getStatusCode();\n $this->recordResult('Current System v2 API', $statusCode === 200, \"Status: {$statusCode} in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Current System v2 API', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetDeals(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new DealsApi(null, $config);\n // Official SDK uses getDealsSummary instead of getDeals\n $result = $apiInstance->getDealsSummary();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Deals Summary', true, \"Retrieved deals summary in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Deals Summary', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetPersons(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n // Official SDK doesn't have a simple getPersons method\n // Skip this test for now as the API structure is different\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Persons', false, \"Method not available in official SDK - API structure different\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Persons', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetActivities(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n // Official SDK doesn't have a simple getActivities method\n // Skip this test for now as the API structure is different\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Activities', false, \"Method not available in official SDK - API structure different\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Activities', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testFieldMetadata(SocialAccount $socialAccount): void\n {\n $this->info('Test 3: Field Metadata');\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n\n // Test 3a: Get Deal Fields\n $this->testGetDealFields($config);\n\n // Test 3b: Get Person Fields\n $this->testGetPersonFields($config);\n\n // Test 3c: Get Activity Fields\n $this->testGetActivityFields($config);\n } catch (Exception $e) {\n $this->recordResult('Field Metadata', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testGetDealFields(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new DealFieldsApi(null, $config);\n $result = $apiInstance->getDealFields();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $count = $result->getData() ? count($result->getData()) : 0;\n $this->recordResult('Get Deal Fields', true, \"Retrieved {$count} deal fields in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Deal Fields', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetPersonFields(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new PersonFieldsApi(null, $config);\n $result = $apiInstance->getPersonFields();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $count = $result->getData() ? count($result->getData()) : 0;\n $this->recordResult('Get Person Fields', true, \"Retrieved {$count} person fields in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Person Fields', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetActivityFields(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new ActivityFieldsApi(null, $config);\n $result = $apiInstance->getActivityFields();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $count = $result->getData() ? count($result->getData()) : 0;\n $this->recordResult('Get Activity Fields', true, \"Retrieved {$count} activity fields in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Activity Fields', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testV2ApiSupport(SocialAccount $socialAccount): void\n {\n $this->info('Test 4: v2 API Support (Cursor-based Pagination)');\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n\n $startTime = microtime(true);\n\n // Test v2 deals endpoint with cursor pagination\n // Note: The official SDK may or may not have v2 API support\n // This test will determine if v2 is available\n try {\n $apiInstance = new DealsApi(null, $config);\n $updatedSince = Carbon::now()->subDays(30)->toRfc3339String();\n $result = $apiInstance->getDealsSummary();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n\n // Check if response has cursor information (v2 indicator)\n // The response object doesn't have getAdditionalData method\n // Just test if the call works - v2 API support is limited in official SDK v1\n $this->recordResult('v2 API Support', false, \"v1 API works but v2 cursor pagination not available in official SDK v1. Retrieved summary in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('v2 API Support', false, $e->getMessage(), $elapsed);\n }\n } catch (Exception $e) {\n $this->recordResult('v2 API Support', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testErrorHandling(SocialAccount $socialAccount): void\n {\n $this->info('Test 5: Error Handling');\n\n try {\n // Test 5a: Invalid Token\n $this->testInvalidToken();\n\n // Test 5b: Non-existent Resource\n $this->testNonExistentResource($socialAccount);\n } catch (Exception $e) {\n $this->recordResult('Error Handling', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testInvalidToken(): void\n {\n $startTime = microtime(true);\n\n try {\n $config = new Configuration();\n $config->setAccessToken('invalid_token');\n $client = new DealsApi(null, $config);\n\n $client->getDealsSummary();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Invalid Token Error', false, \"Expected error but request succeeded in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Invalid Token Error', true, \"Correctly threw exception: {$e->getMessage()} in {$elapsed}ms\", $elapsed);\n }\n }\n\n private function testNonExistentResource(SocialAccount $socialAccount): void\n {\n $startTime = microtime(true);\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n $client = new DealsApi(null, $config);\n\n // Try to get a deal with a very high ID that likely doesn't exist\n // Note: Official SDK doesn't have a simple getDeal method, so skip this\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Non-existent Resource Error', false, \"Method not available in official SDK - API structure different\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Non-existent Resource Error', true, \"Correctly threw exception: {$e->getMessage()} in {$elapsed}ms\", $elapsed);\n }\n }\n\n private function recordResult(string $testName, bool $success, string $message, float $elapsed): void\n {\n $this->results[] = [\n 'test' => $testName,\n 'success' => $success,\n 'message' => $message,\n 'elapsed' => $elapsed,\n ];\n\n $status = $success ? '✅ PASS' : '❌ FAIL';\n $this->line(\" {$status} - {$testName}: {$message}\");\n }\n\n private function displayResults(): void\n {\n $this->newLine();\n $this->info('=== POC Test Results Summary ===');\n $this->newLine();\n\n $total = count($this->results);\n $passed = count(array_filter($this->results, fn ($r) => $r['success']));\n $failed = $total - $passed;\n\n $this->line(\"Total Tests: {$total}\");\n $this->line(\"Passed: {$passed}\");\n $this->line(\"Failed: {$failed}\");\n $this->newLine();\n\n $totalTime = array_sum(array_column($this->results, 'elapsed'));\n $this->line(\"Total Execution Time: {$totalTime}ms\");\n $this->newLine();\n\n if ($failed > 0) {\n $this->warn('Failed Tests:');\n foreach ($this->results as $result) {\n if (! $result['success']) {\n $this->line(\" - {$result['test']}: {$result['message']}\");\n }\n }\n $this->newLine();\n }\n\n $this->info('=== Detailed Results ===');\n foreach ($this->results as $result) {\n $status = $result['success'] ? '✅' : '❌';\n $this->line(\"{$status} {$result['test']} ({$result['elapsed']}ms): {$result['message']}\");\n }\n\n $this->newLine();\n $this->info('=== Recommendations ===');\n\n if ($passed === $total) {\n $this->info('✅ All tests passed! The official SDK is viable for migration.');\n $this->info('Next steps: Proceed with full migration planning.');\n } else {\n $this->warn('⚠️ Some tests failed. Review the failures above before proceeding.');\n $this->warn('Consider hybrid approach or investigate alternatives if critical operations fail.');\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}]...
|
5365831007722038384
|
61370652661533572
|
idle
|
accessibility
|
NULL
|
Project: faVsco.js, menu
pipedrive-sdk-poc, menu
S Project: faVsco.js, menu
pipedrive-sdk-poc, 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\Console\Commands\Crm;
use Jiminny\Console\Commands\Command;
use Jiminny\Console\Commands\Crm\Traits\SyncObjectsCommandTrait;
use Jiminny\Jobs\Crm\SyncObjects as SyncObjectsJob;
use Jiminny\Jobs\Job;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Models\Team;
class SyncObjects extends Command
{
use SyncObjectsCommandTrait;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'crm:sync-objects {team?} {--sync}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Sync remote CRM objects.';
/**
* Execute the console command.
*/
public function handle(): void
{
$teamId = $this->argument('team');
$teams = [];
if ($teamId) {
$team = Team::idOrUuId($teamId);
if ($team && $team->crm?->provider === Configuration::PROVIDER_HUBSPOT) {
$this->error(sprintf(
'Team %s (%s) uses HubSpot. Use crm:sync-hubspot-objects instead.',
$team->getName(),
$team->getUuid()
));
return;
}
if ($team) {
$teams[] = $team;
}
} else {
// Exclude HubSpot teams - handled by crm:sync-hubspot-objects
$teams = Team::where('status', Team::STATUS_ACTIVE)
->whereHas(
'crm',
fn ($q) => $q
->where('provider', '!=', Configuration::PROVIDER_HUBSPOT)
->where('sync_objects', true)
)
->get();
}
$this->dispatchSyncJobsForTeams($teams);
}
protected function getStaggerDelaySeconds(): float
{
return 2;
}
protected function getMaxDelaySeconds(): int
{
// Cap delay at 15 minutes (SQS max delay limit)
// capacity of 450 teams before reach
return 900;
}
protected function getLogPrefix(): string
{
return '';
}
protected function createSyncJob(Team $team): Job
{
return new SyncObjectsJob($team);
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
1
51
1
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Console\Commands\Crm;
use Carbon\Carbon;
use Exception;
use Illuminate\Console\Command;
use Jiminny\Component\Encryption\EncryptedTokenManager;
use Jiminny\Models\SocialAccount;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Pipedrive\versions\v1\Api\ActivitiesApi;
use Pipedrive\versions\v1\Api\ActivityFieldsApi;
use Pipedrive\versions\v1\Api\DealFieldsApi;
use Pipedrive\versions\v1\Api\DealsApi;
use Pipedrive\versions\v1\Api\PersonFieldsApi;
use Pipedrive\versions\v1\Api\PersonsApi;
use Pipedrive\versions\v1\Configuration;
class TestPipedriveOfficialSdkCommand extends Command
{
protected $signature = 'jiminny:test-pipedrive-official-sdk {teamId}';
protected $description = 'Test official Pipedrive SDK (pipedrive/pipedrive) with existing OAuth tokens';
private array $results = [];
private EncryptedTokenManager $tokenManager;
public function __construct(EncryptedTokenManager $tokenManager)
{
parent::__construct();
$this->tokenManager = $tokenManager;
}
public function handle(): int
{
$teamId = (int) $this->argument('teamId');
$this->info("Starting Pipedrive Official SDK POC for Team ID: {$teamId}");
$this->newLine();
try {
$this->runTests($teamId);
} catch (Exception $e) {
$this->error("POC failed with error: {$e->getMessage()}");
$this->error($e->getTraceAsString());
return 1;
}
$this->displayResults();
return 0;
}
private function runTests(int $teamId): void
{
$team = Team::find($teamId);
if (! $team) {
throw new Exception("Team with ID {$teamId} not found");
}
$socialAccount = SocialAccount::where('provider', SocialAccount::PROVIDER_PIPEDRIVE)
->whereHas('sociable', function ($query) use ($team) {
$query->where('team_id', $team->id);
})
->orderByDesc('expires') // Get the most recently expiring token
->first();
if (! $socialAccount) {
throw new Exception("No Pipedrive social account found for team {$teamId}");
}
$this->info("Found Pipedrive account for team: {$team->name}");
$this->newLine();
// Test 1: OAuth Initialization
$this->testOAuthInitialization($socialAccount);
// Test 2: Basic CRUD Operations
$this->testBasicCrudOperations($socialAccount);
// Test 3: Field Metadata
$this->testFieldMetadata($socialAccount);
// Test 4: v2 API Support
$this->testV2ApiSupport($socialAccount);
// Test 5: Error Handling
$this->testErrorHandling($socialAccount);
}
private function testOAuthInitialization(SocialAccount $socialAccount): void
{
$this->info('Test 1: OAuth Initialization');
$startTime = microtime(true);
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
$client = new DealsApi(null, $config);
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('OAuth Initialization', true, "Client initialized successfully in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('OAuth Initialization', false, $e->getMessage(), $elapsed);
}
$this->newLine();
}
private function testBasicCrudOperations(SocialAccount $socialAccount): void
{
$this->info('Test 2: Basic CRUD Operations');
try {
// Get the raw token from database
$rawDbToken = $socialAccount->getProviderUserToken();
$this->info("DB token (length: " . strlen($rawDbToken) . ")");
$this->info("DB token preview: " . substr($rawDbToken, 0, 20) . "...");
// Check token expiry
$expires = $socialAccount->expires;
$this->info("Token expires at: " . ($expires ? Carbon::createFromTimestamp($expires)->toDateTimeString() : 'null'));
$this->info("Token is expired: " . ($expires && $expires < time() ? 'YES' : 'NO'));
// Test with the DB token directly
$this->info("\n--- Testing with DB token ---");
$config = new Configuration();
$config->setAccessToken($rawDbToken);
// Test 2a: Get Deals
$this->testGetDeals($config);
// Test 2b: Get Persons
$this->testGetPersons($config);
// Test 2c: Get Activities
$this->testGetActivities($config);
} catch (Exception $e) {
$this->recordResult('Basic CRUD Operations', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testCurrentSystemApproach(SocialAccount $socialAccount): void
{
$startTime = microtime(true);
try {
// Simulate how current system makes requests
$token = $socialAccount->getProviderUserToken();
$url = 'https://api.pipedrive.com/api/v2/deals';
$headers = [
'Authorization' => 'Bearer ' . $token,
'Accept' => 'application/json',
];
$client = new \GuzzleHttp\Client();
$response = $client->get($url, [
'headers' => $headers,
'timeout' => 30,
]);
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$statusCode = $response->getStatusCode();
$this->recordResult('Current System v2 API', $statusCode === 200, "Status: {$statusCode} in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Current System v2 API', false, $e->getMessage(), $elapsed);
}
}
private function testGetDeals(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new DealsApi(null, $config);
// Official SDK uses getDealsSummary instead of getDeals
$result = $apiInstance->getDealsSummary();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Deals Summary', true, "Retrieved deals summary in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Deals Summary', false, $e->getMessage(), $elapsed);
}
}
private function testGetPersons(Configuration $config): void
{
$startTime = microtime(true);
try {
// Official SDK doesn't have a simple getPersons method
// Skip this test for now as the API structure is different
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Persons', false, "Method not available in official SDK - API structure different", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Persons', false, $e->getMessage(), $elapsed);
}
}
private function testGetActivities(Configuration $config): void
{
$startTime = microtime(true);
try {
// Official SDK doesn't have a simple getActivities method
// Skip this test for now as the API structure is different
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Activities', false, "Method not available in official SDK - API structure different", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Activities', false, $e->getMessage(), $elapsed);
}
}
private function testFieldMetadata(SocialAccount $socialAccount): void
{
$this->info('Test 3: Field Metadata');
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
// Test 3a: Get Deal Fields
$this->testGetDealFields($config);
// Test 3b: Get Person Fields
$this->testGetPersonFields($config);
// Test 3c: Get Activity Fields
$this->testGetActivityFields($config);
} catch (Exception $e) {
$this->recordResult('Field Metadata', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testGetDealFields(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new DealFieldsApi(null, $config);
$result = $apiInstance->getDealFields();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$count = $result->getData() ? count($result->getData()) : 0;
$this->recordResult('Get Deal Fields', true, "Retrieved {$count} deal fields in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Deal Fields', false, $e->getMessage(), $elapsed);
}
}
private function testGetPersonFields(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new PersonFieldsApi(null, $config);
$result = $apiInstance->getPersonFields();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$count = $result->getData() ? count($result->getData()) : 0;
$this->recordResult('Get Person Fields', true, "Retrieved {$count} person fields in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Person Fields', false, $e->getMessage(), $elapsed);
}
}
private function testGetActivityFields(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new ActivityFieldsApi(null, $config);
$result = $apiInstance->getActivityFields();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$count = $result->getData() ? count($result->getData()) : 0;
$this->recordResult('Get Activity Fields', true, "Retrieved {$count} activity fields in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Activity Fields', false, $e->getMessage(), $elapsed);
}
}
private function testV2ApiSupport(SocialAccount $socialAccount): void
{
$this->info('Test 4: v2 API Support (Cursor-based Pagination)');
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
$startTime = microtime(true);
// Test v2 deals endpoint with cursor pagination
// Note: The official SDK may or may not have v2 API support
// This test will determine if v2 is available
try {
$apiInstance = new DealsApi(null, $config);
$updatedSince = Carbon::now()->subDays(30)->toRfc3339String();
$result = $apiInstance->getDealsSummary();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
// Check if response has cursor information (v2 indicator)
// The response object doesn't have getAdditionalData method
// Just test if the call works - v2 API support is limited in official SDK v1
$this->recordResult('v2 API Support', false, "v1 API works but v2 cursor pagination not available in official SDK v1. Retrieved summary in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('v2 API Support', false, $e->getMessage(), $elapsed);
}
} catch (Exception $e) {
$this->recordResult('v2 API Support', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testErrorHandling(SocialAccount $socialAccount): void
{
$this->info('Test 5: Error Handling');
try {
// Test 5a: Invalid Token
$this->testInvalidToken();
// Test 5b: Non-existent Resource
$this->testNonExistentResource($socialAccount);
} catch (Exception $e) {
$this->recordResult('Error Handling', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testInvalidToken(): void
{
$startTime = microtime(true);
try {
$config = new Configuration();
$config->setAccessToken('invalid_token');
$client = new DealsApi(null, $config);
$client->getDealsSummary();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Invalid Token Error', false, "Expected error but request succeeded in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Invalid Token Error', true, "Correctly threw exception: {$e->getMessage()} in {$elapsed}ms", $elapsed);
}
}
private function testNonExistentResource(SocialAccount $socialAccount): void
{
$startTime = microtime(true);
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
$client = new DealsApi(null, $config);
// Try to get a deal with a very high ID that likely doesn't exist
// Note: Official SDK doesn't have a simple getDeal method, so skip this
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Non-existent Resource Error', false, "Method not available in official SDK - API structure different", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Non-existent Resource Error', true, "Correctly threw exception: {$e->getMessage()} in {$elapsed}ms", $elapsed);
}
}
private function recordResult(string $testName, bool $success, string $message, float $elapsed): void
{
$this->results[] = [
'test' => $testName,
'success' => $success,
'message' => $message,
'elapsed' => $elapsed,
];
$status = $success ? '✅ PASS' : '❌ FAIL';
$this->line(" {$status} - {$testName}: {$message}");
}
private function displayResults(): void
{
$this->newLine();
$this->info('=== POC Test Results Summary ===');
$this->newLine();
$total = count($this->results);
$passed = count(array_filter($this->results, fn ($r) => $r['success']));
$failed = $total - $passed;
$this->line("Total Tests: {$total}");
$this->line("Passed: {$passed}");
$this->line("Failed: {$failed}");
$this->newLine();
$totalTime = array_sum(array_column($this->results, 'elapsed'));
$this->line("Total Execution Time: {$totalTime}ms");
$this->newLine();
if ($failed > 0) {
$this->warn('Failed Tests:');
foreach ($this->results as $result) {
if (! $result['success']) {
$this->line(" - {$result['test']}: {$result['message']}");
}
}
$this->newLine();
}
$this->info('=== Detailed Results ===');
foreach ($this->results as $result) {
$status = $result['success'] ? '✅' : '❌';
$this->line("{$status} {$result['test']} ({$result['elapsed']}ms): {$result['message']}");
}
$this->newLine();
$this->info('=== Recommendations ===');
if ($passed === $total) {
$this->info('✅ All tests passed! The official SDK is viable for migration.');
$this->info('Next steps: Proceed with full migration planning.');
} else {
$this->warn('⚠️ Some tests failed. Review the failures above before proceeding.');
$this->warn('Consider hybrid approach or investigate alternatives if critical operations fail.');
}
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
49394
|
NULL
|
NULL
|
NULL
|
|
49398
|
1762
|
22
|
2026-05-18T06:40:27.601862+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779086427601_m1.jpg...
|
PhpStorm
|
faVsco.js – TestPipedriveOfficialSdkCommand.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
pipedrive-sdk-poc, menu
S Project: faVsco.js, menu
pipedrive-sdk-poc, 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\Console\Commands\Crm;
use Jiminny\Console\Commands\Command;
use Jiminny\Console\Commands\Crm\Traits\SyncObjectsCommandTrait;
use Jiminny\Jobs\Crm\SyncObjects as SyncObjectsJob;
use Jiminny\Jobs\Job;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Models\Team;
class SyncObjects extends Command
{
use SyncObjectsCommandTrait;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'crm:sync-objects {team?} {--sync}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Sync remote CRM objects.';
/**
* Execute the console command.
*/
public function handle(): void
{
$teamId = $this->argument('team');
$teams = [];
if ($teamId) {
$team = Team::idOrUuId($teamId);
if ($team && $team->crm?->provider === Configuration::PROVIDER_HUBSPOT) {
$this->error(sprintf(
'Team %s (%s) uses HubSpot. Use crm:sync-hubspot-objects instead.',
$team->getName(),
$team->getUuid()
));
return;
}
if ($team) {
$teams[] = $team;
}
} else {
// Exclude HubSpot teams - handled by crm:sync-hubspot-objects
$teams = Team::where('status', Team::STATUS_ACTIVE)
->whereHas(
'crm',
fn ($q) => $q
->where('provider', '!=', Configuration::PROVIDER_HUBSPOT)
->where('sync_objects', true)
)
->get();
}
$this->dispatchSyncJobsForTeams($teams);
}
protected function getStaggerDelaySeconds(): float
{
return 2;
}
protected function getMaxDelaySeconds(): int
{
// Cap delay at 15 minutes (SQS max delay limit)
// capacity of 450 teams before reach
return 900;
}
protected function getLogPrefix(): string
{
return '';
}
protected function createSyncJob(Team $team): Job
{
return new SyncObjectsJob($team);
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
1
51
1
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Console\Commands\Crm;
use Carbon\Carbon;
use Exception;
use Illuminate\Console\Command;
use Jiminny\Component\Encryption\EncryptedTokenManager;
use Jiminny\Models\SocialAccount;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Pipedrive\versions\v1\Api\ActivitiesApi;
use Pipedrive\versions\v1\Api\ActivityFieldsApi;
use Pipedrive\versions\v1\Api\DealFieldsApi;
use Pipedrive\versions\v1\Api\DealsApi;
use Pipedrive\versions\v1\Api\PersonFieldsApi;
use Pipedrive\versions\v1\Api\PersonsApi;
use Pipedrive\versions\v1\Configuration;
class TestPipedriveOfficialSdkCommand extends Command
{
protected $signature = 'jiminny:test-pipedrive-official-sdk {teamId}';
protected $description = 'Test official Pipedrive SDK (pipedrive/pipedrive) with existing OAuth tokens';
private array $results = [];
private EncryptedTokenManager $tokenManager;
public function __construct(EncryptedTokenManager $tokenManager)
{
parent::__construct();
$this->tokenManager = $tokenManager;
}
public function handle(): int
{
$teamId = (int) $this->argument('teamId');
$this->info("Starting Pipedrive Official SDK POC for Team ID: {$teamId}");
$this->newLine();
try {
$this->runTests($teamId);
} catch (Exception $e) {
$this->error("POC failed with error: {$e->getMessage()}");
$this->error($e->getTraceAsString());
return 1;
}
$this->displayResults();
return 0;
}
private function runTests(int $teamId): void
{
$team = Team::find($teamId);
if (! $team) {
throw new Exception("Team with ID {$teamId} not found");
}
$socialAccount = SocialAccount::where('provider', SocialAccount::PROVIDER_PIPEDRIVE)
->whereHas('sociable', function ($query) use ($team) {
$query->where('team_id', $team->id);
})
->orderByDesc('expires') // Get the most recently expiring token
->first();
if (! $socialAccount) {
throw new Exception("No Pipedrive social account found for team {$teamId}");
}
$this->info("Found Pipedrive account for team: {$team->name}");
$this->newLine();
// Test 1: OAuth Initialization
$this->testOAuthInitialization($socialAccount);
// Test 2: Basic CRUD Operations
$this->testBasicCrudOperations($socialAccount);
// Test 3: Field Metadata
$this->testFieldMetadata($socialAccount);
// Test 4: v2 API Support
$this->testV2ApiSupport($socialAccount);
// Test 5: Error Handling
$this->testErrorHandling($socialAccount);
}
private function testOAuthInitialization(SocialAccount $socialAccount): void
{
$this->info('Test 1: OAuth Initialization');
$startTime = microtime(true);
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
$client = new DealsApi(null, $config);
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('OAuth Initialization', true, "Client initialized successfully in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('OAuth Initialization', false, $e->getMessage(), $elapsed);
}
$this->newLine();
}
private function testBasicCrudOperations(SocialAccount $socialAccount): void
{
$this->info('Test 2: Basic CRUD Operations');
try {
// Get the raw token from database
$rawDbToken = $socialAccount->getProviderUserToken();
$this->info("DB token (length: " . strlen($rawDbToken) . ")");
$this->info("DB token preview: " . substr($rawDbToken, 0, 20) . "...");
// Check token expiry
$expires = $socialAccount->expires;
$this->info("Token expires at: " . ($expires ? Carbon::createFromTimestamp($expires)->toDateTimeString() : 'null'));
$this->info("Token is expired: " . ($expires && $expires < time() ? 'YES' : 'NO'));
// Test with the DB token directly
$this->info("\n--- Testing with DB token ---");
$config = new Configuration();
$config->setAccessToken($rawDbToken);
// Test 2a: Get Deals
$this->testGetDeals($config);
// Test 2b: Get Persons
$this->testGetPersons($config);
// Test 2c: Get Activities
$this->testGetActivities($config);
} catch (Exception $e) {
$this->recordResult('Basic CRUD Operations', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testCurrentSystemApproach(SocialAccount $socialAccount): void
{
$startTime = microtime(true);
try {
// Simulate how current system makes requests
$token = $socialAccount->getProviderUserToken();
$url = 'https://api.pipedrive.com/api/v2/deals';
$headers = [
'Authorization' => 'Bearer ' . $token,
'Accept' => 'application/json',
];
$client = new \GuzzleHttp\Client();
$response = $client->get($url, [
'headers' => $headers,
'timeout' => 30,
]);
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$statusCode = $response->getStatusCode();
$this->recordResult('Current System v2 API', $statusCode === 200, "Status: {$statusCode} in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Current System v2 API', false, $e->getMessage(), $elapsed);
}
}
private function testGetDeals(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new DealsApi(null, $config);
// Official SDK uses getDealsSummary instead of getDeals
$result = $apiInstance->getDealsSummary();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Deals Summary', true, "Retrieved deals summary in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Deals Summary', false, $e->getMessage(), $elapsed);
}
}
private function testGetPersons(Configuration $config): void
{
$startTime = microtime(true);
try {
// Official SDK doesn't have a simple getPersons method
// Skip this test for now as the API structure is different
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Persons', false, "Method not available in official SDK - API structure different", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Persons', false, $e->getMessage(), $elapsed);
}
}
private function testGetActivities(Configuration $config): void
{
$startTime = microtime(true);
try {
// Official SDK doesn't have a simple getActivities method
// Skip this test for now as the API structure is different
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Activities', false, "Method not available in official SDK - API structure different", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Activities', false, $e->getMessage(), $elapsed);
}
}
private function testFieldMetadata(SocialAccount $socialAccount): void
{
$this->info('Test 3: Field Metadata');
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
// Test 3a: Get Deal Fields
$this->testGetDealFields($config);
// Test 3b: Get Person Fields
$this->testGetPersonFields($config);
// Test 3c: Get Activity Fields
$this->testGetActivityFields($config);
} catch (Exception $e) {
$this->recordResult('Field Metadata', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testGetDealFields(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new DealFieldsApi(null, $config);
$result = $apiInstance->getDealFields();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$count = $result->getData() ? count($result->getData()) : 0;
$this->recordResult('Get Deal Fields', true, "Retrieved {$count} deal fields in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Deal Fields', false, $e->getMessage(), $elapsed);
}
}
private function testGetPersonFields(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new PersonFieldsApi(null, $config);
$result = $apiInstance->getPersonFields();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$count = $result->getData() ? count($result->getData()) : 0;
$this->recordResult('Get Person Fields', true, "Retrieved {$count} person fields in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Person Fields', false, $e->getMessage(), $elapsed);
}
}
private function testGetActivityFields(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new ActivityFieldsApi(null, $config);
$result = $apiInstance->getActivityFields();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$count = $result->getData() ? count($result->getData()) : 0;
$this->recordResult('Get Activity Fields', true, "Retrieved {$count} activity fields in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Activity Fields', false, $e->getMessage(), $elapsed);
}
}
private function testV2ApiSupport(SocialAccount $socialAccount): void
{
$this->info('Test 4: v2 API Support (Cursor-based Pagination)');
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
$startTime = microtime(true);
// Test v2 deals endpoint with cursor pagination
// Note: The official SDK may or may not have v2 API support
// This test will determine if v2 is available
try {
$apiInstance = new DealsApi(null, $config);
$updatedSince = Carbon::now()->subDays(30)->toRfc3339String();
$result = $apiInstance->getDealsSummary();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
// Check if response has cursor information (v2 indicator)
// The response object doesn't have getAdditionalData method
// Just test if the call works - v2 API support is limited in official SDK v1
$this->recordResult('v2 API Support', false, "v1 API works but v2 cursor pagination not available in official SDK v1. Retrieved summary in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('v2 API Support', false, $e->getMessage(), $elapsed);
}
} catch (Exception $e) {
$this->recordResult('v2 API Support', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testErrorHandling(SocialAccount $socialAccount): void
{
$this->info('Test 5: Error Handling');
try {
// Test 5a: Invalid Token
$this->testInvalidToken();
// Test 5b: Non-existent Resource
$this->testNonExistentResource($socialAccount);
} catch (Exception $e) {
$this->recordResult('Error Handling', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testInvalidToken(): void
{
$startTime = microtime(true);
try {
$config = new Configuration();
$config->setAccessToken('invalid_token');
$client = new DealsApi(null, $config);
$client->getDealsSummary();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Invalid Token Error', false, "Expected error but request succeeded in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Invalid Token Error', true, "Correctly threw exception: {$e->getMessage()} in {$elapsed}ms", $elapsed);
}
}
private function testNonExistentResource(SocialAccount $socialAccount): void
{
$startTime = microtime(true);
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
$client = new DealsApi(null, $config);
// Try to get a deal with a very high ID that likely doesn't exist
// Note: Official SDK doesn't have a simple getDeal method, so skip this
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Non-existent Resource Error', false, "Method not available in official SDK - API structure different", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Non-existent Resource Error', true, "Correctly threw exception: {$e->getMessage()} in {$elapsed}ms", $elapsed);
}
}
private function recordResult(string $testName, bool $success, string $message, float $elapsed): void
{
$this->results[] = [
'test' => $testName,
'success' => $success,
'message' => $message,
'elapsed' => $elapsed,
];
$status = $success ? '✅ PASS' : '❌ FAIL';
$this->line(" {$status} - {$testName}: {$message}");
}
private function displayResults(): void
{
$this->newLine();
$this->info('=== POC Test Results Summary ===');
$this->newLine();
$total = count($this->results);
$passed = count(array_filter($this->results, fn ($r) => $r['success']));
$failed = $total - $passed;
$this->line("Total Tests: {$total}");
$this->line("Passed: {$passed}");
$this->line("Failed: {$failed}");
$this->newLine();
$totalTime = array_sum(array_column($this->results, 'elapsed'));
$this->line("Total Execution Time: {$totalTime}ms");
$this->newLine();
if ($failed > 0) {
$this->warn('Failed Tests:');
foreach ($this->results as $result) {
if (! $result['success']) {
$this->line(" - {$result['test']}: {$result['message']}");
}
}
$this->newLine();
}
$this->info('=== Detailed Results ===');
foreach ($this->results as $result) {
$status = $result['success'] ? '✅' : '❌';
$this->line("{$status} {$result['test']} ({$result['elapsed']}ms): {$result['message']}");
}
$this->newLine();
$this->info('=== Recommendations ===');
if ($passed === $total) {
$this->info('✅ All tests passed! The official SDK is viable for migration.');
$this->info('Next steps: Proceed with full migration planning.');
} else {
$this->warn('⚠️ Some tests failed. Review the failures above before proceeding.');
$this->warn('Consider hybrid approach or investigate alternatives if critical operations fail.');
}
}
}
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":"pipedrive-sdk-poc, menu","depth":5,"on_screen":true,"help_text":"Git Branch: pipedrive-sdk-poc","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\\Console\\Commands\\Crm;\n\nuse Jiminny\\Console\\Commands\\Command;\nuse Jiminny\\Console\\Commands\\Crm\\Traits\\SyncObjectsCommandTrait;\nuse Jiminny\\Jobs\\Crm\\SyncObjects as SyncObjectsJob;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Models\\Team;\n\nclass SyncObjects extends Command\n{\n use SyncObjectsCommandTrait;\n\n /**\n * The name and signature of the console command.\n *\n * @var string\n */\n protected $signature = 'crm:sync-objects {team?} {--sync}';\n\n /**\n * The console command description.\n *\n * @var string\n */\n protected $description = 'Sync remote CRM objects.';\n\n /**\n * Execute the console command.\n */\n public function handle(): void\n {\n $teamId = $this->argument('team');\n $teams = [];\n\n if ($teamId) {\n $team = Team::idOrUuId($teamId);\n if ($team && $team->crm?->provider === Configuration::PROVIDER_HUBSPOT) {\n $this->error(sprintf(\n 'Team %s (%s) uses HubSpot. Use crm:sync-hubspot-objects instead.',\n $team->getName(),\n $team->getUuid()\n ));\n\n return;\n }\n if ($team) {\n $teams[] = $team;\n }\n } else {\n // Exclude HubSpot teams - handled by crm:sync-hubspot-objects\n $teams = Team::where('status', Team::STATUS_ACTIVE)\n ->whereHas(\n 'crm',\n fn ($q) => $q\n ->where('provider', '!=', Configuration::PROVIDER_HUBSPOT)\n ->where('sync_objects', true)\n )\n ->get();\n }\n\n $this->dispatchSyncJobsForTeams($teams);\n }\n\n protected function getStaggerDelaySeconds(): float\n {\n return 2;\n }\n\n protected function getMaxDelaySeconds(): int\n {\n // Cap delay at 15 minutes (SQS max delay limit)\n // capacity of 450 teams before reach\n return 900;\n }\n\n protected function getLogPrefix(): string\n {\n return '';\n }\n\n protected function createSyncJob(Team $team): Job\n {\n return new SyncObjectsJob($team);\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Console\\Commands\\Crm;\n\nuse Jiminny\\Console\\Commands\\Command;\nuse Jiminny\\Console\\Commands\\Crm\\Traits\\SyncObjectsCommandTrait;\nuse Jiminny\\Jobs\\Crm\\SyncObjects as SyncObjectsJob;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Models\\Team;\n\nclass SyncObjects extends Command\n{\n use SyncObjectsCommandTrait;\n\n /**\n * The name and signature of the console command.\n *\n * @var string\n */\n protected $signature = 'crm:sync-objects {team?} {--sync}';\n\n /**\n * The console command description.\n *\n * @var string\n */\n protected $description = 'Sync remote CRM objects.';\n\n /**\n * Execute the console command.\n */\n public function handle(): void\n {\n $teamId = $this->argument('team');\n $teams = [];\n\n if ($teamId) {\n $team = Team::idOrUuId($teamId);\n if ($team && $team->crm?->provider === Configuration::PROVIDER_HUBSPOT) {\n $this->error(sprintf(\n 'Team %s (%s) uses HubSpot. Use crm:sync-hubspot-objects instead.',\n $team->getName(),\n $team->getUuid()\n ));\n\n return;\n }\n if ($team) {\n $teams[] = $team;\n }\n } else {\n // Exclude HubSpot teams - handled by crm:sync-hubspot-objects\n $teams = Team::where('status', Team::STATUS_ACTIVE)\n ->whereHas(\n 'crm',\n fn ($q) => $q\n ->where('provider', '!=', Configuration::PROVIDER_HUBSPOT)\n ->where('sync_objects', true)\n )\n ->get();\n }\n\n $this->dispatchSyncJobsForTeams($teams);\n }\n\n protected function getStaggerDelaySeconds(): float\n {\n return 2;\n }\n\n protected function getMaxDelaySeconds(): int\n {\n // Cap delay at 15 minutes (SQS max delay limit)\n // capacity of 450 teams before reach\n return 900;\n }\n\n protected function getLogPrefix(): string\n {\n return '';\n }\n\n protected function createSyncJob(Team $team): Job\n {\n return new SyncObjectsJob($team);\n }\n}","role_description":"text entry area","is_enabled":true,"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":"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":"AXStaticText","text":"1","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"51","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"1","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"on_screen":true,"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\\Console\\Commands\\Crm;\n\nuse Carbon\\Carbon;\nuse Exception;\nuse Illuminate\\Console\\Command;\nuse Jiminny\\Component\\Encryption\\EncryptedTokenManager;\nuse Jiminny\\Models\\SocialAccount;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Pipedrive\\versions\\v1\\Api\\ActivitiesApi;\nuse Pipedrive\\versions\\v1\\Api\\ActivityFieldsApi;\nuse Pipedrive\\versions\\v1\\Api\\DealFieldsApi;\nuse Pipedrive\\versions\\v1\\Api\\DealsApi;\nuse Pipedrive\\versions\\v1\\Api\\PersonFieldsApi;\nuse Pipedrive\\versions\\v1\\Api\\PersonsApi;\nuse Pipedrive\\versions\\v1\\Configuration;\n\nclass TestPipedriveOfficialSdkCommand extends Command\n{\n protected $signature = 'jiminny:test-pipedrive-official-sdk {teamId}';\n\n protected $description = 'Test official Pipedrive SDK (pipedrive/pipedrive) with existing OAuth tokens';\n\n private array $results = [];\n\n private EncryptedTokenManager $tokenManager;\n\n public function __construct(EncryptedTokenManager $tokenManager)\n {\n parent::__construct();\n $this->tokenManager = $tokenManager;\n }\n\n public function handle(): int\n {\n $teamId = (int) $this->argument('teamId');\n\n $this->info(\"Starting Pipedrive Official SDK POC for Team ID: {$teamId}\");\n $this->newLine();\n\n try {\n $this->runTests($teamId);\n } catch (Exception $e) {\n $this->error(\"POC failed with error: {$e->getMessage()}\");\n $this->error($e->getTraceAsString());\n\n return 1;\n }\n\n $this->displayResults();\n\n return 0;\n }\n\n private function runTests(int $teamId): void\n {\n $team = Team::find($teamId);\n if (! $team) {\n throw new Exception(\"Team with ID {$teamId} not found\");\n }\n\n $socialAccount = SocialAccount::where('provider', SocialAccount::PROVIDER_PIPEDRIVE)\n ->whereHas('sociable', function ($query) use ($team) {\n $query->where('team_id', $team->id);\n })\n ->orderByDesc('expires') // Get the most recently expiring token\n ->first();\n\n if (! $socialAccount) {\n throw new Exception(\"No Pipedrive social account found for team {$teamId}\");\n }\n\n $this->info(\"Found Pipedrive account for team: {$team->name}\");\n $this->newLine();\n\n // Test 1: OAuth Initialization\n $this->testOAuthInitialization($socialAccount);\n\n // Test 2: Basic CRUD Operations\n $this->testBasicCrudOperations($socialAccount);\n\n // Test 3: Field Metadata\n $this->testFieldMetadata($socialAccount);\n\n // Test 4: v2 API Support\n $this->testV2ApiSupport($socialAccount);\n\n // Test 5: Error Handling\n $this->testErrorHandling($socialAccount);\n }\n\n private function testOAuthInitialization(SocialAccount $socialAccount): void\n {\n $this->info('Test 1: OAuth Initialization');\n $startTime = microtime(true);\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n\n $client = new DealsApi(null, $config);\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('OAuth Initialization', true, \"Client initialized successfully in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('OAuth Initialization', false, $e->getMessage(), $elapsed);\n }\n\n $this->newLine();\n }\n\n private function testBasicCrudOperations(SocialAccount $socialAccount): void\n {\n $this->info('Test 2: Basic CRUD Operations');\n\n try {\n // Get the raw token from database\n $rawDbToken = $socialAccount->getProviderUserToken();\n $this->info(\"DB token (length: \" . strlen($rawDbToken) . \")\");\n $this->info(\"DB token preview: \" . substr($rawDbToken, 0, 20) . \"...\");\n\n // Check token expiry\n $expires = $socialAccount->expires;\n $this->info(\"Token expires at: \" . ($expires ? Carbon::createFromTimestamp($expires)->toDateTimeString() : 'null'));\n $this->info(\"Token is expired: \" . ($expires && $expires < time() ? 'YES' : 'NO'));\n\n // Test with the DB token directly\n $this->info(\"\\n--- Testing with DB token ---\");\n $config = new Configuration();\n $config->setAccessToken($rawDbToken);\n\n // Test 2a: Get Deals\n $this->testGetDeals($config);\n\n // Test 2b: Get Persons\n $this->testGetPersons($config);\n\n // Test 2c: Get Activities\n $this->testGetActivities($config);\n } catch (Exception $e) {\n $this->recordResult('Basic CRUD Operations', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testCurrentSystemApproach(SocialAccount $socialAccount): void\n {\n $startTime = microtime(true);\n\n try {\n // Simulate how current system makes requests\n $token = $socialAccount->getProviderUserToken();\n $url = 'https://api.pipedrive.com/api/v2/deals';\n\n $headers = [\n 'Authorization' => 'Bearer ' . $token,\n 'Accept' => 'application/json',\n ];\n\n $client = new \\GuzzleHttp\\Client();\n $response = $client->get($url, [\n 'headers' => $headers,\n 'timeout' => 30,\n ]);\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $statusCode = $response->getStatusCode();\n $this->recordResult('Current System v2 API', $statusCode === 200, \"Status: {$statusCode} in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Current System v2 API', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetDeals(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new DealsApi(null, $config);\n // Official SDK uses getDealsSummary instead of getDeals\n $result = $apiInstance->getDealsSummary();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Deals Summary', true, \"Retrieved deals summary in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Deals Summary', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetPersons(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n // Official SDK doesn't have a simple getPersons method\n // Skip this test for now as the API structure is different\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Persons', false, \"Method not available in official SDK - API structure different\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Persons', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetActivities(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n // Official SDK doesn't have a simple getActivities method\n // Skip this test for now as the API structure is different\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Activities', false, \"Method not available in official SDK - API structure different\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Activities', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testFieldMetadata(SocialAccount $socialAccount): void\n {\n $this->info('Test 3: Field Metadata');\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n\n // Test 3a: Get Deal Fields\n $this->testGetDealFields($config);\n\n // Test 3b: Get Person Fields\n $this->testGetPersonFields($config);\n\n // Test 3c: Get Activity Fields\n $this->testGetActivityFields($config);\n } catch (Exception $e) {\n $this->recordResult('Field Metadata', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testGetDealFields(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new DealFieldsApi(null, $config);\n $result = $apiInstance->getDealFields();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $count = $result->getData() ? count($result->getData()) : 0;\n $this->recordResult('Get Deal Fields', true, \"Retrieved {$count} deal fields in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Deal Fields', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetPersonFields(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new PersonFieldsApi(null, $config);\n $result = $apiInstance->getPersonFields();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $count = $result->getData() ? count($result->getData()) : 0;\n $this->recordResult('Get Person Fields', true, \"Retrieved {$count} person fields in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Person Fields', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetActivityFields(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new ActivityFieldsApi(null, $config);\n $result = $apiInstance->getActivityFields();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $count = $result->getData() ? count($result->getData()) : 0;\n $this->recordResult('Get Activity Fields', true, \"Retrieved {$count} activity fields in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Activity Fields', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testV2ApiSupport(SocialAccount $socialAccount): void\n {\n $this->info('Test 4: v2 API Support (Cursor-based Pagination)');\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n\n $startTime = microtime(true);\n\n // Test v2 deals endpoint with cursor pagination\n // Note: The official SDK may or may not have v2 API support\n // This test will determine if v2 is available\n try {\n $apiInstance = new DealsApi(null, $config);\n $updatedSince = Carbon::now()->subDays(30)->toRfc3339String();\n $result = $apiInstance->getDealsSummary();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n\n // Check if response has cursor information (v2 indicator)\n // The response object doesn't have getAdditionalData method\n // Just test if the call works - v2 API support is limited in official SDK v1\n $this->recordResult('v2 API Support', false, \"v1 API works but v2 cursor pagination not available in official SDK v1. Retrieved summary in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('v2 API Support', false, $e->getMessage(), $elapsed);\n }\n } catch (Exception $e) {\n $this->recordResult('v2 API Support', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testErrorHandling(SocialAccount $socialAccount): void\n {\n $this->info('Test 5: Error Handling');\n\n try {\n // Test 5a: Invalid Token\n $this->testInvalidToken();\n\n // Test 5b: Non-existent Resource\n $this->testNonExistentResource($socialAccount);\n } catch (Exception $e) {\n $this->recordResult('Error Handling', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testInvalidToken(): void\n {\n $startTime = microtime(true);\n\n try {\n $config = new Configuration();\n $config->setAccessToken('invalid_token');\n $client = new DealsApi(null, $config);\n\n $client->getDealsSummary();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Invalid Token Error', false, \"Expected error but request succeeded in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Invalid Token Error', true, \"Correctly threw exception: {$e->getMessage()} in {$elapsed}ms\", $elapsed);\n }\n }\n\n private function testNonExistentResource(SocialAccount $socialAccount): void\n {\n $startTime = microtime(true);\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n $client = new DealsApi(null, $config);\n\n // Try to get a deal with a very high ID that likely doesn't exist\n // Note: Official SDK doesn't have a simple getDeal method, so skip this\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Non-existent Resource Error', false, \"Method not available in official SDK - API structure different\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Non-existent Resource Error', true, \"Correctly threw exception: {$e->getMessage()} in {$elapsed}ms\", $elapsed);\n }\n }\n\n private function recordResult(string $testName, bool $success, string $message, float $elapsed): void\n {\n $this->results[] = [\n 'test' => $testName,\n 'success' => $success,\n 'message' => $message,\n 'elapsed' => $elapsed,\n ];\n\n $status = $success ? '✅ PASS' : '❌ FAIL';\n $this->line(\" {$status} - {$testName}: {$message}\");\n }\n\n private function displayResults(): void\n {\n $this->newLine();\n $this->info('=== POC Test Results Summary ===');\n $this->newLine();\n\n $total = count($this->results);\n $passed = count(array_filter($this->results, fn ($r) => $r['success']));\n $failed = $total - $passed;\n\n $this->line(\"Total Tests: {$total}\");\n $this->line(\"Passed: {$passed}\");\n $this->line(\"Failed: {$failed}\");\n $this->newLine();\n\n $totalTime = array_sum(array_column($this->results, 'elapsed'));\n $this->line(\"Total Execution Time: {$totalTime}ms\");\n $this->newLine();\n\n if ($failed > 0) {\n $this->warn('Failed Tests:');\n foreach ($this->results as $result) {\n if (! $result['success']) {\n $this->line(\" - {$result['test']}: {$result['message']}\");\n }\n }\n $this->newLine();\n }\n\n $this->info('=== Detailed Results ===');\n foreach ($this->results as $result) {\n $status = $result['success'] ? '✅' : '❌';\n $this->line(\"{$status} {$result['test']} ({$result['elapsed']}ms): {$result['message']}\");\n }\n\n $this->newLine();\n $this->info('=== Recommendations ===');\n\n if ($passed === $total) {\n $this->info('✅ All tests passed! The official SDK is viable for migration.');\n $this->info('Next steps: Proceed with full migration planning.');\n } else {\n $this->warn('⚠️ Some tests failed. Review the failures above before proceeding.');\n $this->warn('Consider hybrid approach or investigate alternatives if critical operations fail.');\n }\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Console\\Commands\\Crm;\n\nuse Carbon\\Carbon;\nuse Exception;\nuse Illuminate\\Console\\Command;\nuse Jiminny\\Component\\Encryption\\EncryptedTokenManager;\nuse Jiminny\\Models\\SocialAccount;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Pipedrive\\versions\\v1\\Api\\ActivitiesApi;\nuse Pipedrive\\versions\\v1\\Api\\ActivityFieldsApi;\nuse Pipedrive\\versions\\v1\\Api\\DealFieldsApi;\nuse Pipedrive\\versions\\v1\\Api\\DealsApi;\nuse Pipedrive\\versions\\v1\\Api\\PersonFieldsApi;\nuse Pipedrive\\versions\\v1\\Api\\PersonsApi;\nuse Pipedrive\\versions\\v1\\Configuration;\n\nclass TestPipedriveOfficialSdkCommand extends Command\n{\n protected $signature = 'jiminny:test-pipedrive-official-sdk {teamId}';\n\n protected $description = 'Test official Pipedrive SDK (pipedrive/pipedrive) with existing OAuth tokens';\n\n private array $results = [];\n\n private EncryptedTokenManager $tokenManager;\n\n public function __construct(EncryptedTokenManager $tokenManager)\n {\n parent::__construct();\n $this->tokenManager = $tokenManager;\n }\n\n public function handle(): int\n {\n $teamId = (int) $this->argument('teamId');\n\n $this->info(\"Starting Pipedrive Official SDK POC for Team ID: {$teamId}\");\n $this->newLine();\n\n try {\n $this->runTests($teamId);\n } catch (Exception $e) {\n $this->error(\"POC failed with error: {$e->getMessage()}\");\n $this->error($e->getTraceAsString());\n\n return 1;\n }\n\n $this->displayResults();\n\n return 0;\n }\n\n private function runTests(int $teamId): void\n {\n $team = Team::find($teamId);\n if (! $team) {\n throw new Exception(\"Team with ID {$teamId} not found\");\n }\n\n $socialAccount = SocialAccount::where('provider', SocialAccount::PROVIDER_PIPEDRIVE)\n ->whereHas('sociable', function ($query) use ($team) {\n $query->where('team_id', $team->id);\n })\n ->orderByDesc('expires') // Get the most recently expiring token\n ->first();\n\n if (! $socialAccount) {\n throw new Exception(\"No Pipedrive social account found for team {$teamId}\");\n }\n\n $this->info(\"Found Pipedrive account for team: {$team->name}\");\n $this->newLine();\n\n // Test 1: OAuth Initialization\n $this->testOAuthInitialization($socialAccount);\n\n // Test 2: Basic CRUD Operations\n $this->testBasicCrudOperations($socialAccount);\n\n // Test 3: Field Metadata\n $this->testFieldMetadata($socialAccount);\n\n // Test 4: v2 API Support\n $this->testV2ApiSupport($socialAccount);\n\n // Test 5: Error Handling\n $this->testErrorHandling($socialAccount);\n }\n\n private function testOAuthInitialization(SocialAccount $socialAccount): void\n {\n $this->info('Test 1: OAuth Initialization');\n $startTime = microtime(true);\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n\n $client = new DealsApi(null, $config);\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('OAuth Initialization', true, \"Client initialized successfully in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('OAuth Initialization', false, $e->getMessage(), $elapsed);\n }\n\n $this->newLine();\n }\n\n private function testBasicCrudOperations(SocialAccount $socialAccount): void\n {\n $this->info('Test 2: Basic CRUD Operations');\n\n try {\n // Get the raw token from database\n $rawDbToken = $socialAccount->getProviderUserToken();\n $this->info(\"DB token (length: \" . strlen($rawDbToken) . \")\");\n $this->info(\"DB token preview: \" . substr($rawDbToken, 0, 20) . \"...\");\n\n // Check token expiry\n $expires = $socialAccount->expires;\n $this->info(\"Token expires at: \" . ($expires ? Carbon::createFromTimestamp($expires)->toDateTimeString() : 'null'));\n $this->info(\"Token is expired: \" . ($expires && $expires < time() ? 'YES' : 'NO'));\n\n // Test with the DB token directly\n $this->info(\"\\n--- Testing with DB token ---\");\n $config = new Configuration();\n $config->setAccessToken($rawDbToken);\n\n // Test 2a: Get Deals\n $this->testGetDeals($config);\n\n // Test 2b: Get Persons\n $this->testGetPersons($config);\n\n // Test 2c: Get Activities\n $this->testGetActivities($config);\n } catch (Exception $e) {\n $this->recordResult('Basic CRUD Operations', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testCurrentSystemApproach(SocialAccount $socialAccount): void\n {\n $startTime = microtime(true);\n\n try {\n // Simulate how current system makes requests\n $token = $socialAccount->getProviderUserToken();\n $url = 'https://api.pipedrive.com/api/v2/deals';\n\n $headers = [\n 'Authorization' => 'Bearer ' . $token,\n 'Accept' => 'application/json',\n ];\n\n $client = new \\GuzzleHttp\\Client();\n $response = $client->get($url, [\n 'headers' => $headers,\n 'timeout' => 30,\n ]);\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $statusCode = $response->getStatusCode();\n $this->recordResult('Current System v2 API', $statusCode === 200, \"Status: {$statusCode} in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Current System v2 API', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetDeals(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new DealsApi(null, $config);\n // Official SDK uses getDealsSummary instead of getDeals\n $result = $apiInstance->getDealsSummary();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Deals Summary', true, \"Retrieved deals summary in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Deals Summary', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetPersons(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n // Official SDK doesn't have a simple getPersons method\n // Skip this test for now as the API structure is different\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Persons', false, \"Method not available in official SDK - API structure different\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Persons', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetActivities(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n // Official SDK doesn't have a simple getActivities method\n // Skip this test for now as the API structure is different\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Activities', false, \"Method not available in official SDK - API structure different\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Activities', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testFieldMetadata(SocialAccount $socialAccount): void\n {\n $this->info('Test 3: Field Metadata');\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n\n // Test 3a: Get Deal Fields\n $this->testGetDealFields($config);\n\n // Test 3b: Get Person Fields\n $this->testGetPersonFields($config);\n\n // Test 3c: Get Activity Fields\n $this->testGetActivityFields($config);\n } catch (Exception $e) {\n $this->recordResult('Field Metadata', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testGetDealFields(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new DealFieldsApi(null, $config);\n $result = $apiInstance->getDealFields();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $count = $result->getData() ? count($result->getData()) : 0;\n $this->recordResult('Get Deal Fields', true, \"Retrieved {$count} deal fields in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Deal Fields', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetPersonFields(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new PersonFieldsApi(null, $config);\n $result = $apiInstance->getPersonFields();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $count = $result->getData() ? count($result->getData()) : 0;\n $this->recordResult('Get Person Fields', true, \"Retrieved {$count} person fields in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Person Fields', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetActivityFields(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new ActivityFieldsApi(null, $config);\n $result = $apiInstance->getActivityFields();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $count = $result->getData() ? count($result->getData()) : 0;\n $this->recordResult('Get Activity Fields', true, \"Retrieved {$count} activity fields in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Activity Fields', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testV2ApiSupport(SocialAccount $socialAccount): void\n {\n $this->info('Test 4: v2 API Support (Cursor-based Pagination)');\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n\n $startTime = microtime(true);\n\n // Test v2 deals endpoint with cursor pagination\n // Note: The official SDK may or may not have v2 API support\n // This test will determine if v2 is available\n try {\n $apiInstance = new DealsApi(null, $config);\n $updatedSince = Carbon::now()->subDays(30)->toRfc3339String();\n $result = $apiInstance->getDealsSummary();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n\n // Check if response has cursor information (v2 indicator)\n // The response object doesn't have getAdditionalData method\n // Just test if the call works - v2 API support is limited in official SDK v1\n $this->recordResult('v2 API Support', false, \"v1 API works but v2 cursor pagination not available in official SDK v1. Retrieved summary in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('v2 API Support', false, $e->getMessage(), $elapsed);\n }\n } catch (Exception $e) {\n $this->recordResult('v2 API Support', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testErrorHandling(SocialAccount $socialAccount): void\n {\n $this->info('Test 5: Error Handling');\n\n try {\n // Test 5a: Invalid Token\n $this->testInvalidToken();\n\n // Test 5b: Non-existent Resource\n $this->testNonExistentResource($socialAccount);\n } catch (Exception $e) {\n $this->recordResult('Error Handling', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testInvalidToken(): void\n {\n $startTime = microtime(true);\n\n try {\n $config = new Configuration();\n $config->setAccessToken('invalid_token');\n $client = new DealsApi(null, $config);\n\n $client->getDealsSummary();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Invalid Token Error', false, \"Expected error but request succeeded in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Invalid Token Error', true, \"Correctly threw exception: {$e->getMessage()} in {$elapsed}ms\", $elapsed);\n }\n }\n\n private function testNonExistentResource(SocialAccount $socialAccount): void\n {\n $startTime = microtime(true);\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n $client = new DealsApi(null, $config);\n\n // Try to get a deal with a very high ID that likely doesn't exist\n // Note: Official SDK doesn't have a simple getDeal method, so skip this\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Non-existent Resource Error', false, \"Method not available in official SDK - API structure different\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Non-existent Resource Error', true, \"Correctly threw exception: {$e->getMessage()} in {$elapsed}ms\", $elapsed);\n }\n }\n\n private function recordResult(string $testName, bool $success, string $message, float $elapsed): void\n {\n $this->results[] = [\n 'test' => $testName,\n 'success' => $success,\n 'message' => $message,\n 'elapsed' => $elapsed,\n ];\n\n $status = $success ? '✅ PASS' : '❌ FAIL';\n $this->line(\" {$status} - {$testName}: {$message}\");\n }\n\n private function displayResults(): void\n {\n $this->newLine();\n $this->info('=== POC Test Results Summary ===');\n $this->newLine();\n\n $total = count($this->results);\n $passed = count(array_filter($this->results, fn ($r) => $r['success']));\n $failed = $total - $passed;\n\n $this->line(\"Total Tests: {$total}\");\n $this->line(\"Passed: {$passed}\");\n $this->line(\"Failed: {$failed}\");\n $this->newLine();\n\n $totalTime = array_sum(array_column($this->results, 'elapsed'));\n $this->line(\"Total Execution Time: {$totalTime}ms\");\n $this->newLine();\n\n if ($failed > 0) {\n $this->warn('Failed Tests:');\n foreach ($this->results as $result) {\n if (! $result['success']) {\n $this->line(\" - {$result['test']}: {$result['message']}\");\n }\n }\n $this->newLine();\n }\n\n $this->info('=== Detailed Results ===');\n foreach ($this->results as $result) {\n $status = $result['success'] ? '✅' : '❌';\n $this->line(\"{$status} {$result['test']} ({$result['elapsed']}ms): {$result['message']}\");\n }\n\n $this->newLine();\n $this->info('=== Recommendations ===');\n\n if ($passed === $total) {\n $this->info('✅ All tests passed! The official SDK is viable for migration.');\n $this->info('Next steps: Proceed with full migration planning.');\n } else {\n $this->warn('⚠️ Some tests failed. Review the failures above before proceeding.');\n $this->warn('Consider hybrid approach or investigate alternatives if critical operations fail.');\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}]...
|
5365831007722038384
|
61370652661533572
|
visual_change
|
accessibility
|
NULL
|
Project: faVsco.js, menu
pipedrive-sdk-poc, menu
S Project: faVsco.js, menu
pipedrive-sdk-poc, 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\Console\Commands\Crm;
use Jiminny\Console\Commands\Command;
use Jiminny\Console\Commands\Crm\Traits\SyncObjectsCommandTrait;
use Jiminny\Jobs\Crm\SyncObjects as SyncObjectsJob;
use Jiminny\Jobs\Job;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Models\Team;
class SyncObjects extends Command
{
use SyncObjectsCommandTrait;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'crm:sync-objects {team?} {--sync}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Sync remote CRM objects.';
/**
* Execute the console command.
*/
public function handle(): void
{
$teamId = $this->argument('team');
$teams = [];
if ($teamId) {
$team = Team::idOrUuId($teamId);
if ($team && $team->crm?->provider === Configuration::PROVIDER_HUBSPOT) {
$this->error(sprintf(
'Team %s (%s) uses HubSpot. Use crm:sync-hubspot-objects instead.',
$team->getName(),
$team->getUuid()
));
return;
}
if ($team) {
$teams[] = $team;
}
} else {
// Exclude HubSpot teams - handled by crm:sync-hubspot-objects
$teams = Team::where('status', Team::STATUS_ACTIVE)
->whereHas(
'crm',
fn ($q) => $q
->where('provider', '!=', Configuration::PROVIDER_HUBSPOT)
->where('sync_objects', true)
)
->get();
}
$this->dispatchSyncJobsForTeams($teams);
}
protected function getStaggerDelaySeconds(): float
{
return 2;
}
protected function getMaxDelaySeconds(): int
{
// Cap delay at 15 minutes (SQS max delay limit)
// capacity of 450 teams before reach
return 900;
}
protected function getLogPrefix(): string
{
return '';
}
protected function createSyncJob(Team $team): Job
{
return new SyncObjectsJob($team);
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
1
51
1
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Console\Commands\Crm;
use Carbon\Carbon;
use Exception;
use Illuminate\Console\Command;
use Jiminny\Component\Encryption\EncryptedTokenManager;
use Jiminny\Models\SocialAccount;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Pipedrive\versions\v1\Api\ActivitiesApi;
use Pipedrive\versions\v1\Api\ActivityFieldsApi;
use Pipedrive\versions\v1\Api\DealFieldsApi;
use Pipedrive\versions\v1\Api\DealsApi;
use Pipedrive\versions\v1\Api\PersonFieldsApi;
use Pipedrive\versions\v1\Api\PersonsApi;
use Pipedrive\versions\v1\Configuration;
class TestPipedriveOfficialSdkCommand extends Command
{
protected $signature = 'jiminny:test-pipedrive-official-sdk {teamId}';
protected $description = 'Test official Pipedrive SDK (pipedrive/pipedrive) with existing OAuth tokens';
private array $results = [];
private EncryptedTokenManager $tokenManager;
public function __construct(EncryptedTokenManager $tokenManager)
{
parent::__construct();
$this->tokenManager = $tokenManager;
}
public function handle(): int
{
$teamId = (int) $this->argument('teamId');
$this->info("Starting Pipedrive Official SDK POC for Team ID: {$teamId}");
$this->newLine();
try {
$this->runTests($teamId);
} catch (Exception $e) {
$this->error("POC failed with error: {$e->getMessage()}");
$this->error($e->getTraceAsString());
return 1;
}
$this->displayResults();
return 0;
}
private function runTests(int $teamId): void
{
$team = Team::find($teamId);
if (! $team) {
throw new Exception("Team with ID {$teamId} not found");
}
$socialAccount = SocialAccount::where('provider', SocialAccount::PROVIDER_PIPEDRIVE)
->whereHas('sociable', function ($query) use ($team) {
$query->where('team_id', $team->id);
})
->orderByDesc('expires') // Get the most recently expiring token
->first();
if (! $socialAccount) {
throw new Exception("No Pipedrive social account found for team {$teamId}");
}
$this->info("Found Pipedrive account for team: {$team->name}");
$this->newLine();
// Test 1: OAuth Initialization
$this->testOAuthInitialization($socialAccount);
// Test 2: Basic CRUD Operations
$this->testBasicCrudOperations($socialAccount);
// Test 3: Field Metadata
$this->testFieldMetadata($socialAccount);
// Test 4: v2 API Support
$this->testV2ApiSupport($socialAccount);
// Test 5: Error Handling
$this->testErrorHandling($socialAccount);
}
private function testOAuthInitialization(SocialAccount $socialAccount): void
{
$this->info('Test 1: OAuth Initialization');
$startTime = microtime(true);
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
$client = new DealsApi(null, $config);
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('OAuth Initialization', true, "Client initialized successfully in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('OAuth Initialization', false, $e->getMessage(), $elapsed);
}
$this->newLine();
}
private function testBasicCrudOperations(SocialAccount $socialAccount): void
{
$this->info('Test 2: Basic CRUD Operations');
try {
// Get the raw token from database
$rawDbToken = $socialAccount->getProviderUserToken();
$this->info("DB token (length: " . strlen($rawDbToken) . ")");
$this->info("DB token preview: " . substr($rawDbToken, 0, 20) . "...");
// Check token expiry
$expires = $socialAccount->expires;
$this->info("Token expires at: " . ($expires ? Carbon::createFromTimestamp($expires)->toDateTimeString() : 'null'));
$this->info("Token is expired: " . ($expires && $expires < time() ? 'YES' : 'NO'));
// Test with the DB token directly
$this->info("\n--- Testing with DB token ---");
$config = new Configuration();
$config->setAccessToken($rawDbToken);
// Test 2a: Get Deals
$this->testGetDeals($config);
// Test 2b: Get Persons
$this->testGetPersons($config);
// Test 2c: Get Activities
$this->testGetActivities($config);
} catch (Exception $e) {
$this->recordResult('Basic CRUD Operations', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testCurrentSystemApproach(SocialAccount $socialAccount): void
{
$startTime = microtime(true);
try {
// Simulate how current system makes requests
$token = $socialAccount->getProviderUserToken();
$url = 'https://api.pipedrive.com/api/v2/deals';
$headers = [
'Authorization' => 'Bearer ' . $token,
'Accept' => 'application/json',
];
$client = new \GuzzleHttp\Client();
$response = $client->get($url, [
'headers' => $headers,
'timeout' => 30,
]);
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$statusCode = $response->getStatusCode();
$this->recordResult('Current System v2 API', $statusCode === 200, "Status: {$statusCode} in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Current System v2 API', false, $e->getMessage(), $elapsed);
}
}
private function testGetDeals(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new DealsApi(null, $config);
// Official SDK uses getDealsSummary instead of getDeals
$result = $apiInstance->getDealsSummary();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Deals Summary', true, "Retrieved deals summary in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Deals Summary', false, $e->getMessage(), $elapsed);
}
}
private function testGetPersons(Configuration $config): void
{
$startTime = microtime(true);
try {
// Official SDK doesn't have a simple getPersons method
// Skip this test for now as the API structure is different
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Persons', false, "Method not available in official SDK - API structure different", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Persons', false, $e->getMessage(), $elapsed);
}
}
private function testGetActivities(Configuration $config): void
{
$startTime = microtime(true);
try {
// Official SDK doesn't have a simple getActivities method
// Skip this test for now as the API structure is different
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Activities', false, "Method not available in official SDK - API structure different", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Activities', false, $e->getMessage(), $elapsed);
}
}
private function testFieldMetadata(SocialAccount $socialAccount): void
{
$this->info('Test 3: Field Metadata');
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
// Test 3a: Get Deal Fields
$this->testGetDealFields($config);
// Test 3b: Get Person Fields
$this->testGetPersonFields($config);
// Test 3c: Get Activity Fields
$this->testGetActivityFields($config);
} catch (Exception $e) {
$this->recordResult('Field Metadata', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testGetDealFields(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new DealFieldsApi(null, $config);
$result = $apiInstance->getDealFields();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$count = $result->getData() ? count($result->getData()) : 0;
$this->recordResult('Get Deal Fields', true, "Retrieved {$count} deal fields in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Deal Fields', false, $e->getMessage(), $elapsed);
}
}
private function testGetPersonFields(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new PersonFieldsApi(null, $config);
$result = $apiInstance->getPersonFields();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$count = $result->getData() ? count($result->getData()) : 0;
$this->recordResult('Get Person Fields', true, "Retrieved {$count} person fields in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Person Fields', false, $e->getMessage(), $elapsed);
}
}
private function testGetActivityFields(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new ActivityFieldsApi(null, $config);
$result = $apiInstance->getActivityFields();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$count = $result->getData() ? count($result->getData()) : 0;
$this->recordResult('Get Activity Fields', true, "Retrieved {$count} activity fields in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Activity Fields', false, $e->getMessage(), $elapsed);
}
}
private function testV2ApiSupport(SocialAccount $socialAccount): void
{
$this->info('Test 4: v2 API Support (Cursor-based Pagination)');
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
$startTime = microtime(true);
// Test v2 deals endpoint with cursor pagination
// Note: The official SDK may or may not have v2 API support
// This test will determine if v2 is available
try {
$apiInstance = new DealsApi(null, $config);
$updatedSince = Carbon::now()->subDays(30)->toRfc3339String();
$result = $apiInstance->getDealsSummary();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
// Check if response has cursor information (v2 indicator)
// The response object doesn't have getAdditionalData method
// Just test if the call works - v2 API support is limited in official SDK v1
$this->recordResult('v2 API Support', false, "v1 API works but v2 cursor pagination not available in official SDK v1. Retrieved summary in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('v2 API Support', false, $e->getMessage(), $elapsed);
}
} catch (Exception $e) {
$this->recordResult('v2 API Support', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testErrorHandling(SocialAccount $socialAccount): void
{
$this->info('Test 5: Error Handling');
try {
// Test 5a: Invalid Token
$this->testInvalidToken();
// Test 5b: Non-existent Resource
$this->testNonExistentResource($socialAccount);
} catch (Exception $e) {
$this->recordResult('Error Handling', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testInvalidToken(): void
{
$startTime = microtime(true);
try {
$config = new Configuration();
$config->setAccessToken('invalid_token');
$client = new DealsApi(null, $config);
$client->getDealsSummary();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Invalid Token Error', false, "Expected error but request succeeded in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Invalid Token Error', true, "Correctly threw exception: {$e->getMessage()} in {$elapsed}ms", $elapsed);
}
}
private function testNonExistentResource(SocialAccount $socialAccount): void
{
$startTime = microtime(true);
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
$client = new DealsApi(null, $config);
// Try to get a deal with a very high ID that likely doesn't exist
// Note: Official SDK doesn't have a simple getDeal method, so skip this
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Non-existent Resource Error', false, "Method not available in official SDK - API structure different", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Non-existent Resource Error', true, "Correctly threw exception: {$e->getMessage()} in {$elapsed}ms", $elapsed);
}
}
private function recordResult(string $testName, bool $success, string $message, float $elapsed): void
{
$this->results[] = [
'test' => $testName,
'success' => $success,
'message' => $message,
'elapsed' => $elapsed,
];
$status = $success ? '✅ PASS' : '❌ FAIL';
$this->line(" {$status} - {$testName}: {$message}");
}
private function displayResults(): void
{
$this->newLine();
$this->info('=== POC Test Results Summary ===');
$this->newLine();
$total = count($this->results);
$passed = count(array_filter($this->results, fn ($r) => $r['success']));
$failed = $total - $passed;
$this->line("Total Tests: {$total}");
$this->line("Passed: {$passed}");
$this->line("Failed: {$failed}");
$this->newLine();
$totalTime = array_sum(array_column($this->results, 'elapsed'));
$this->line("Total Execution Time: {$totalTime}ms");
$this->newLine();
if ($failed > 0) {
$this->warn('Failed Tests:');
foreach ($this->results as $result) {
if (! $result['success']) {
$this->line(" - {$result['test']}: {$result['message']}");
}
}
$this->newLine();
}
$this->info('=== Detailed Results ===');
foreach ($this->results as $result) {
$status = $result['success'] ? '✅' : '❌';
$this->line("{$status} {$result['test']} ({$result['elapsed']}ms): {$result['message']}");
}
$this->newLine();
$this->info('=== Recommendations ===');
if ($passed === $total) {
$this->info('✅ All tests passed! The official SDK is viable for migration.');
$this->info('Next steps: Proceed with full migration planning.');
} else {
$this->warn('⚠️ Some tests failed. Review the failures above before proceeding.');
$this->warn('Consider hybrid approach or investigate alternatives if critical operations fail.');
}
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
49397
|
1763
|
23
|
2026-05-18T06:40:16.495340+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779086416495_m2.jpg...
|
PhpStorm
|
faVsco.js – TestPipedriveOfficialSdkCommand.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
pipedrive-sdk-poc, menu
S Project: faVsco.js, menu
pipedrive-sdk-poc, 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\Console\Commands\Crm;
use Jiminny\Console\Commands\Command;
use Jiminny\Console\Commands\Crm\Traits\SyncObjectsCommandTrait;
use Jiminny\Jobs\Crm\SyncObjects as SyncObjectsJob;
use Jiminny\Jobs\Job;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Models\Team;
class SyncObjects extends Command
{
use SyncObjectsCommandTrait;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'crm:sync-objects {team?} {--sync}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Sync remote CRM objects.';
/**
* Execute the console command.
*/
public function handle(): void
{
$teamId = $this->argument('team');
$teams = [];
if ($teamId) {
$team = Team::idOrUuId($teamId);
if ($team && $team->crm?->provider === Configuration::PROVIDER_HUBSPOT) {
$this->error(sprintf(
'Team %s (%s) uses HubSpot. Use crm:sync-hubspot-objects instead.',
$team->getName(),
$team->getUuid()
));
return;
}
if ($team) {
$teams[] = $team;
}
} else {
// Exclude HubSpot teams - handled by crm:sync-hubspot-objects
$teams = Team::where('status', Team::STATUS_ACTIVE)
->whereHas(
'crm',
fn ($q) => $q
->where('provider', '!=', Configuration::PROVIDER_HUBSPOT)
->where('sync_objects', true)
)
->get();
}
$this->dispatchSyncJobsForTeams($teams);
}
protected function getStaggerDelaySeconds(): float
{
return 2;
}
protected function getMaxDelaySeconds(): int
{
// Cap delay at 15 minutes (SQS max delay limit)
// capacity of 450 teams before reach
return 900;
}
protected function getLogPrefix(): string
{
return '';
}
protected function createSyncJob(Team $team): Job
{
return new SyncObjectsJob($team);
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
1
51
1
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Console\Commands\Crm;
use Carbon\Carbon;
use Exception;
use Illuminate\Console\Command;
use Jiminny\Component\Encryption\EncryptedTokenManager;
use Jiminny\Models\SocialAccount;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Pipedrive\versions\v1\Api\ActivitiesApi;
use Pipedrive\versions\v1\Api\ActivityFieldsApi;
use Pipedrive\versions\v1\Api\DealFieldsApi;
use Pipedrive\versions\v1\Api\DealsApi;
use Pipedrive\versions\v1\Api\PersonFieldsApi;
use Pipedrive\versions\v1\Api\PersonsApi;
use Pipedrive\versions\v1\Configuration;
class TestPipedriveOfficialSdkCommand extends Command
{
protected $signature = 'jiminny:test-pipedrive-official-sdk {teamId}';
protected $description = 'Test official Pipedrive SDK (pipedrive/pipedrive) with existing OAuth tokens';
private array $results = [];
private EncryptedTokenManager $tokenManager;
public function __construct(EncryptedTokenManager $tokenManager)
{
parent::__construct();
$this->tokenManager = $tokenManager;
}
public function handle(): int
{
$teamId = (int) $this->argument('teamId');
$this->info("Starting Pipedrive Official SDK POC for Team ID: {$teamId}");
$this->newLine();
try {
$this->runTests($teamId);
} catch (Exception $e) {
$this->error("POC failed with error: {$e->getMessage()}");
$this->error($e->getTraceAsString());
return 1;
}
$this->displayResults();
return 0;
}
private function runTests(int $teamId): void
{
$team = Team::find($teamId);
if (! $team) {
throw new Exception("Team with ID {$teamId} not found");
}
$socialAccount = SocialAccount::where('provider', SocialAccount::PROVIDER_PIPEDRIVE)
->whereHas('sociable', function ($query) use ($team) {
$query->where('team_id', $team->id);
})
->orderByDesc('expires') // Get the most recently expiring token
->first();
if (! $socialAccount) {
throw new Exception("No Pipedrive social account found for team {$teamId}");
}
$this->info("Found Pipedrive account for team: {$team->name}");
$this->newLine();
// Test 1: OAuth Initialization
$this->testOAuthInitialization($socialAccount);
// Test 2: Basic CRUD Operations
$this->testBasicCrudOperations($socialAccount);
// Test 3: Field Metadata
$this->testFieldMetadata($socialAccount);
// Test 4: v2 API Support
$this->testV2ApiSupport($socialAccount);
// Test 5: Error Handling
$this->testErrorHandling($socialAccount);
}
private function testOAuthInitialization(SocialAccount $socialAccount): void
{
$this->info('Test 1: OAuth Initialization');
$startTime = microtime(true);
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
$client = new DealsApi(null, $config);
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('OAuth Initialization', true, "Client initialized successfully in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('OAuth Initialization', false, $e->getMessage(), $elapsed);
}
$this->newLine();
}
private function testBasicCrudOperations(SocialAccount $socialAccount): void
{
$this->info('Test 2: Basic CRUD Operations');
try {
// Get the raw token from database
$rawDbToken = $socialAccount->getProviderUserToken();
$this->info("DB token (length: " . strlen($rawDbToken) . ")");
$this->info("DB token preview: " . substr($rawDbToken, 0, 20) . "...");
// Check token expiry
$expires = $socialAccount->expires;
$this->info("Token expires at: " . ($expires ? Carbon::createFromTimestamp($expires)->toDateTimeString() : 'null'));
$this->info("Token is expired: " . ($expires && $expires < time() ? 'YES' : 'NO'));
// Test with the DB token directly
$this->info("\n--- Testing with DB token ---");
$config = new Configuration();
$config->setAccessToken($rawDbToken);
// Test 2a: Get Deals
$this->testGetDeals($config);
// Test 2b: Get Persons
$this->testGetPersons($config);
// Test 2c: Get Activities
$this->testGetActivities($config);
} catch (Exception $e) {
$this->recordResult('Basic CRUD Operations', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testCurrentSystemApproach(SocialAccount $socialAccount): void
{
$startTime = microtime(true);
try {
// Simulate how current system makes requests
$token = $socialAccount->getProviderUserToken();
$url = 'https://api.pipedrive.com/api/v2/deals';
$headers = [
'Authorization' => 'Bearer ' . $token,
'Accept' => 'application/json',
];
$client = new \GuzzleHttp\Client();
$response = $client->get($url, [
'headers' => $headers,
'timeout' => 30,
]);
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$statusCode = $response->getStatusCode();
$this->recordResult('Current System v2 API', $statusCode === 200, "Status: {$statusCode} in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Current System v2 API', false, $e->getMessage(), $elapsed);
}
}
private function testGetDeals(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new DealsApi(null, $config);
// Official SDK uses getDealsSummary instead of getDeals
$result = $apiInstance->getDealsSummary();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Deals Summary', true, "Retrieved deals summary in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Deals Summary', false, $e->getMessage(), $elapsed);
}
}
private function testGetPersons(Configuration $config): void
{
$startTime = microtime(true);
try {
// Official SDK doesn't have a simple getPersons method
// Skip this test for now as the API structure is different
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Persons', false, "Method not available in official SDK - API structure different", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Persons', false, $e->getMessage(), $elapsed);
}
}
private function testGetActivities(Configuration $config): void
{
$startTime = microtime(true);
try {
// Official SDK doesn't have a simple getActivities method
// Skip this test for now as the API structure is different
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Activities', false, "Method not available in official SDK - API structure different", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Activities', false, $e->getMessage(), $elapsed);
}
}
private function testFieldMetadata(SocialAccount $socialAccount): void
{
$this->info('Test 3: Field Metadata');
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
// Test 3a: Get Deal Fields
$this->testGetDealFields($config);
// Test 3b: Get Person Fields
$this->testGetPersonFields($config);
// Test 3c: Get Activity Fields
$this->testGetActivityFields($config);
} catch (Exception $e) {
$this->recordResult('Field Metadata', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testGetDealFields(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new DealFieldsApi(null, $config);
$result = $apiInstance->getDealFields();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$count = $result->getData() ? count($result->getData()) : 0;
$this->recordResult('Get Deal Fields', true, "Retrieved {$count} deal fields in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Deal Fields', false, $e->getMessage(), $elapsed);
}
}
private function testGetPersonFields(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new PersonFieldsApi(null, $config);
$result = $apiInstance->getPersonFields();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$count = $result->getData() ? count($result->getData()) : 0;
$this->recordResult('Get Person Fields', true, "Retrieved {$count} person fields in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Person Fields', false, $e->getMessage(), $elapsed);
}
}
private function testGetActivityFields(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new ActivityFieldsApi(null, $config);
$result = $apiInstance->getActivityFields();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$count = $result->getData() ? count($result->getData()) : 0;
$this->recordResult('Get Activity Fields', true, "Retrieved {$count} activity fields in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Activity Fields', false, $e->getMessage(), $elapsed);
}
}
private function testV2ApiSupport(SocialAccount $socialAccount): void
{
$this->info('Test 4: v2 API Support (Cursor-based Pagination)');
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
$startTime = microtime(true);
// Test v2 deals endpoint with cursor pagination
// Note: The official SDK may or may not have v2 API support
// This test will determine if v2 is available
try {
$apiInstance = new DealsApi(null, $config);
$updatedSince = Carbon::now()->subDays(30)->toRfc3339String();
$result = $apiInstance->getDealsSummary();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
// Check if response has cursor information (v2 indicator)
// The response object doesn't have getAdditionalData method
// Just test if the call works - v2 API support is limited in official SDK v1
$this->recordResult('v2 API Support', false, "v1 API works but v2 cursor pagination not available in official SDK v1. Retrieved summary in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('v2 API Support', false, $e->getMessage(), $elapsed);
}
} catch (Exception $e) {
$this->recordResult('v2 API Support', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testErrorHandling(SocialAccount $socialAccount): void
{
$this->info('Test 5: Error Handling');
try {
// Test 5a: Invalid Token
$this->testInvalidToken();
// Test 5b: Non-existent Resource
$this->testNonExistentResource($socialAccount);
} catch (Exception $e) {
$this->recordResult('Error Handling', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testInvalidToken(): void
{
$startTime = microtime(true);
try {
$config = new Configuration();
$config->setAccessToken('invalid_token');
$client = new DealsApi(null, $config);
$client->getDealsSummary();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Invalid Token Error', false, "Expected error but request succeeded in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Invalid Token Error', true, "Correctly threw exception: {$e->getMessage()} in {$elapsed}ms", $elapsed);
}
}
private function testNonExistentResource(SocialAccount $socialAccount): void
{
$startTime = microtime(true);
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
$client = new DealsApi(null, $config);
// Try to get a deal with a very high ID that likely doesn't exist
// Note: Official SDK doesn't have a simple getDeal method, so skip this
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Non-existent Resource Error', false, "Method not available in official SDK - API structure different", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Non-existent Resource Error', true, "Correctly threw exception: {$e->getMessage()} in {$elapsed}ms", $elapsed);
}
}
private function recordResult(string $testName, bool $success, string $message, float $elapsed): void
{
$this->results[] = [
'test' => $testName,
'success' => $success,
'message' => $message,
'elapsed' => $elapsed,
];
$status = $success ? '✅ PASS' : '❌ FAIL';
$this->line(" {$status} - {$testName}: {$message}");
}
private function displayResults(): void
{
$this->newLine();
$this->info('=== POC Test Results Summary ===');
$this->newLine();
$total = count($this->results);
$passed = count(array_filter($this->results, fn ($r) => $r['success']));
$failed = $total - $passed;
$this->line("Total Tests: {$total}");
$this->line("Passed: {$passed}");
$this->line("Failed: {$failed}");
$this->newLine();
$totalTime = array_sum(array_column($this->results, 'elapsed'));
$this->line("Total Execution Time: {$totalTime}ms");
$this->newLine();
if ($failed > 0) {
$this->warn('Failed Tests:');
foreach ($this->results as $result) {
if (! $result['success']) {
$this->line(" - {$result['test']}: {$result['message']}");
}
}
$this->newLine();
}
$this->info('=== Detailed Results ===');
foreach ($this->results as $result) {
$status = $result['success'] ? '✅' : '❌';
$this->line("{$status} {$result['test']} ({$result['elapsed']}ms): {$result['message']}");
}
$this->newLine();
$this->info('=== Recommendations ===');
if ($passed === $total) {
$this->info('✅ All tests passed! The official SDK is viable for migration.');
$this->info('Next steps: Proceed with full migration planning.');
} else {
$this->warn('⚠️ Some tests failed. Review the failures above before proceeding.');
$this->warn('Consider hybrid approach or investigate alternatives if critical operations fail.');
}
}
}
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":"pipedrive-sdk-poc, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.05618351,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: pipedrive-sdk-poc","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\\Console\\Commands\\Crm;\n\nuse Jiminny\\Console\\Commands\\Command;\nuse Jiminny\\Console\\Commands\\Crm\\Traits\\SyncObjectsCommandTrait;\nuse Jiminny\\Jobs\\Crm\\SyncObjects as SyncObjectsJob;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Models\\Team;\n\nclass SyncObjects extends Command\n{\n use SyncObjectsCommandTrait;\n\n /**\n * The name and signature of the console command.\n *\n * @var string\n */\n protected $signature = 'crm:sync-objects {team?} {--sync}';\n\n /**\n * The console command description.\n *\n * @var string\n */\n protected $description = 'Sync remote CRM objects.';\n\n /**\n * Execute the console command.\n */\n public function handle(): void\n {\n $teamId = $this->argument('team');\n $teams = [];\n\n if ($teamId) {\n $team = Team::idOrUuId($teamId);\n if ($team && $team->crm?->provider === Configuration::PROVIDER_HUBSPOT) {\n $this->error(sprintf(\n 'Team %s (%s) uses HubSpot. Use crm:sync-hubspot-objects instead.',\n $team->getName(),\n $team->getUuid()\n ));\n\n return;\n }\n if ($team) {\n $teams[] = $team;\n }\n } else {\n // Exclude HubSpot teams - handled by crm:sync-hubspot-objects\n $teams = Team::where('status', Team::STATUS_ACTIVE)\n ->whereHas(\n 'crm',\n fn ($q) => $q\n ->where('provider', '!=', Configuration::PROVIDER_HUBSPOT)\n ->where('sync_objects', true)\n )\n ->get();\n }\n\n $this->dispatchSyncJobsForTeams($teams);\n }\n\n protected function getStaggerDelaySeconds(): float\n {\n return 2;\n }\n\n protected function getMaxDelaySeconds(): int\n {\n // Cap delay at 15 minutes (SQS max delay limit)\n // capacity of 450 teams before reach\n return 900;\n }\n\n protected function getLogPrefix(): string\n {\n return '';\n }\n\n protected function createSyncJob(Team $team): Job\n {\n return new SyncObjectsJob($team);\n }\n}","depth":4,"bounds":{"left":0.15724733,"top":0.2952913,"width":0.21476063,"height":0.7047087},"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Console\\Commands\\Crm;\n\nuse Jiminny\\Console\\Commands\\Command;\nuse Jiminny\\Console\\Commands\\Crm\\Traits\\SyncObjectsCommandTrait;\nuse Jiminny\\Jobs\\Crm\\SyncObjects as SyncObjectsJob;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Models\\Team;\n\nclass SyncObjects extends Command\n{\n use SyncObjectsCommandTrait;\n\n /**\n * The name and signature of the console command.\n *\n * @var string\n */\n protected $signature = 'crm:sync-objects {team?} {--sync}';\n\n /**\n * The console command description.\n *\n * @var string\n */\n protected $description = 'Sync remote CRM objects.';\n\n /**\n * Execute the console command.\n */\n public function handle(): void\n {\n $teamId = $this->argument('team');\n $teams = [];\n\n if ($teamId) {\n $team = Team::idOrUuId($teamId);\n if ($team && $team->crm?->provider === Configuration::PROVIDER_HUBSPOT) {\n $this->error(sprintf(\n 'Team %s (%s) uses HubSpot. Use crm:sync-hubspot-objects instead.',\n $team->getName(),\n $team->getUuid()\n ));\n\n return;\n }\n if ($team) {\n $teams[] = $team;\n }\n } else {\n // Exclude HubSpot teams - handled by crm:sync-hubspot-objects\n $teams = Team::where('status', Team::STATUS_ACTIVE)\n ->whereHas(\n 'crm',\n fn ($q) => $q\n ->where('provider', '!=', Configuration::PROVIDER_HUBSPOT)\n ->where('sync_objects', true)\n )\n ->get();\n }\n\n $this->dispatchSyncJobsForTeams($teams);\n }\n\n protected function getStaggerDelaySeconds(): float\n {\n return 2;\n }\n\n protected function getMaxDelaySeconds(): int\n {\n // Cap delay at 15 minutes (SQS max delay limit)\n // capacity of 450 teams before reach\n return 900;\n }\n\n protected function getLogPrefix(): string\n {\n return '';\n }\n\n protected function createSyncJob(Team $team): Job\n {\n return new SyncObjectsJob($team);\n }\n}","role_description":"text entry area","is_enabled":true,"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":"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":"AXStaticText","text":"1","depth":4,"bounds":{"left":0.57912236,"top":0.10055866,"width":0.00731383,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"51","depth":4,"bounds":{"left":0.5884308,"top":0.10055866,"width":0.009640957,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"1","depth":4,"bounds":{"left":0.6000665,"top":0.10055866,"width":0.00731383,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.6090425,"top":0.09896249,"width":0.00731383,"height":0.018355945},"on_screen":true,"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.6163564,"top":0.09896249,"width":0.006981383,"height":0.018355945},"on_screen":true,"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\\Console\\Commands\\Crm;\n\nuse Carbon\\Carbon;\nuse Exception;\nuse Illuminate\\Console\\Command;\nuse Jiminny\\Component\\Encryption\\EncryptedTokenManager;\nuse Jiminny\\Models\\SocialAccount;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Pipedrive\\versions\\v1\\Api\\ActivitiesApi;\nuse Pipedrive\\versions\\v1\\Api\\ActivityFieldsApi;\nuse Pipedrive\\versions\\v1\\Api\\DealFieldsApi;\nuse Pipedrive\\versions\\v1\\Api\\DealsApi;\nuse Pipedrive\\versions\\v1\\Api\\PersonFieldsApi;\nuse Pipedrive\\versions\\v1\\Api\\PersonsApi;\nuse Pipedrive\\versions\\v1\\Configuration;\n\nclass TestPipedriveOfficialSdkCommand extends Command\n{\n protected $signature = 'jiminny:test-pipedrive-official-sdk {teamId}';\n\n protected $description = 'Test official Pipedrive SDK (pipedrive/pipedrive) with existing OAuth tokens';\n\n private array $results = [];\n\n private EncryptedTokenManager $tokenManager;\n\n public function __construct(EncryptedTokenManager $tokenManager)\n {\n parent::__construct();\n $this->tokenManager = $tokenManager;\n }\n\n public function handle(): int\n {\n $teamId = (int) $this->argument('teamId');\n\n $this->info(\"Starting Pipedrive Official SDK POC for Team ID: {$teamId}\");\n $this->newLine();\n\n try {\n $this->runTests($teamId);\n } catch (Exception $e) {\n $this->error(\"POC failed with error: {$e->getMessage()}\");\n $this->error($e->getTraceAsString());\n\n return 1;\n }\n\n $this->displayResults();\n\n return 0;\n }\n\n private function runTests(int $teamId): void\n {\n $team = Team::find($teamId);\n if (! $team) {\n throw new Exception(\"Team with ID {$teamId} not found\");\n }\n\n $socialAccount = SocialAccount::where('provider', SocialAccount::PROVIDER_PIPEDRIVE)\n ->whereHas('sociable', function ($query) use ($team) {\n $query->where('team_id', $team->id);\n })\n ->orderByDesc('expires') // Get the most recently expiring token\n ->first();\n\n if (! $socialAccount) {\n throw new Exception(\"No Pipedrive social account found for team {$teamId}\");\n }\n\n $this->info(\"Found Pipedrive account for team: {$team->name}\");\n $this->newLine();\n\n // Test 1: OAuth Initialization\n $this->testOAuthInitialization($socialAccount);\n\n // Test 2: Basic CRUD Operations\n $this->testBasicCrudOperations($socialAccount);\n\n // Test 3: Field Metadata\n $this->testFieldMetadata($socialAccount);\n\n // Test 4: v2 API Support\n $this->testV2ApiSupport($socialAccount);\n\n // Test 5: Error Handling\n $this->testErrorHandling($socialAccount);\n }\n\n private function testOAuthInitialization(SocialAccount $socialAccount): void\n {\n $this->info('Test 1: OAuth Initialization');\n $startTime = microtime(true);\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n\n $client = new DealsApi(null, $config);\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('OAuth Initialization', true, \"Client initialized successfully in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('OAuth Initialization', false, $e->getMessage(), $elapsed);\n }\n\n $this->newLine();\n }\n\n private function testBasicCrudOperations(SocialAccount $socialAccount): void\n {\n $this->info('Test 2: Basic CRUD Operations');\n\n try {\n // Get the raw token from database\n $rawDbToken = $socialAccount->getProviderUserToken();\n $this->info(\"DB token (length: \" . strlen($rawDbToken) . \")\");\n $this->info(\"DB token preview: \" . substr($rawDbToken, 0, 20) . \"...\");\n\n // Check token expiry\n $expires = $socialAccount->expires;\n $this->info(\"Token expires at: \" . ($expires ? Carbon::createFromTimestamp($expires)->toDateTimeString() : 'null'));\n $this->info(\"Token is expired: \" . ($expires && $expires < time() ? 'YES' : 'NO'));\n\n // Test with the DB token directly\n $this->info(\"\\n--- Testing with DB token ---\");\n $config = new Configuration();\n $config->setAccessToken($rawDbToken);\n\n // Test 2a: Get Deals\n $this->testGetDeals($config);\n\n // Test 2b: Get Persons\n $this->testGetPersons($config);\n\n // Test 2c: Get Activities\n $this->testGetActivities($config);\n } catch (Exception $e) {\n $this->recordResult('Basic CRUD Operations', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testCurrentSystemApproach(SocialAccount $socialAccount): void\n {\n $startTime = microtime(true);\n\n try {\n // Simulate how current system makes requests\n $token = $socialAccount->getProviderUserToken();\n $url = 'https://api.pipedrive.com/api/v2/deals';\n\n $headers = [\n 'Authorization' => 'Bearer ' . $token,\n 'Accept' => 'application/json',\n ];\n\n $client = new \\GuzzleHttp\\Client();\n $response = $client->get($url, [\n 'headers' => $headers,\n 'timeout' => 30,\n ]);\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $statusCode = $response->getStatusCode();\n $this->recordResult('Current System v2 API', $statusCode === 200, \"Status: {$statusCode} in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Current System v2 API', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetDeals(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new DealsApi(null, $config);\n // Official SDK uses getDealsSummary instead of getDeals\n $result = $apiInstance->getDealsSummary();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Deals Summary', true, \"Retrieved deals summary in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Deals Summary', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetPersons(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n // Official SDK doesn't have a simple getPersons method\n // Skip this test for now as the API structure is different\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Persons', false, \"Method not available in official SDK - API structure different\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Persons', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetActivities(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n // Official SDK doesn't have a simple getActivities method\n // Skip this test for now as the API structure is different\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Activities', false, \"Method not available in official SDK - API structure different\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Activities', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testFieldMetadata(SocialAccount $socialAccount): void\n {\n $this->info('Test 3: Field Metadata');\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n\n // Test 3a: Get Deal Fields\n $this->testGetDealFields($config);\n\n // Test 3b: Get Person Fields\n $this->testGetPersonFields($config);\n\n // Test 3c: Get Activity Fields\n $this->testGetActivityFields($config);\n } catch (Exception $e) {\n $this->recordResult('Field Metadata', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testGetDealFields(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new DealFieldsApi(null, $config);\n $result = $apiInstance->getDealFields();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $count = $result->getData() ? count($result->getData()) : 0;\n $this->recordResult('Get Deal Fields', true, \"Retrieved {$count} deal fields in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Deal Fields', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetPersonFields(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new PersonFieldsApi(null, $config);\n $result = $apiInstance->getPersonFields();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $count = $result->getData() ? count($result->getData()) : 0;\n $this->recordResult('Get Person Fields', true, \"Retrieved {$count} person fields in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Person Fields', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetActivityFields(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new ActivityFieldsApi(null, $config);\n $result = $apiInstance->getActivityFields();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $count = $result->getData() ? count($result->getData()) : 0;\n $this->recordResult('Get Activity Fields', true, \"Retrieved {$count} activity fields in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Activity Fields', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testV2ApiSupport(SocialAccount $socialAccount): void\n {\n $this->info('Test 4: v2 API Support (Cursor-based Pagination)');\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n\n $startTime = microtime(true);\n\n // Test v2 deals endpoint with cursor pagination\n // Note: The official SDK may or may not have v2 API support\n // This test will determine if v2 is available\n try {\n $apiInstance = new DealsApi(null, $config);\n $updatedSince = Carbon::now()->subDays(30)->toRfc3339String();\n $result = $apiInstance->getDealsSummary();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n\n // Check if response has cursor information (v2 indicator)\n // The response object doesn't have getAdditionalData method\n // Just test if the call works - v2 API support is limited in official SDK v1\n $this->recordResult('v2 API Support', false, \"v1 API works but v2 cursor pagination not available in official SDK v1. Retrieved summary in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('v2 API Support', false, $e->getMessage(), $elapsed);\n }\n } catch (Exception $e) {\n $this->recordResult('v2 API Support', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testErrorHandling(SocialAccount $socialAccount): void\n {\n $this->info('Test 5: Error Handling');\n\n try {\n // Test 5a: Invalid Token\n $this->testInvalidToken();\n\n // Test 5b: Non-existent Resource\n $this->testNonExistentResource($socialAccount);\n } catch (Exception $e) {\n $this->recordResult('Error Handling', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testInvalidToken(): void\n {\n $startTime = microtime(true);\n\n try {\n $config = new Configuration();\n $config->setAccessToken('invalid_token');\n $client = new DealsApi(null, $config);\n\n $client->getDealsSummary();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Invalid Token Error', false, \"Expected error but request succeeded in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Invalid Token Error', true, \"Correctly threw exception: {$e->getMessage()} in {$elapsed}ms\", $elapsed);\n }\n }\n\n private function testNonExistentResource(SocialAccount $socialAccount): void\n {\n $startTime = microtime(true);\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n $client = new DealsApi(null, $config);\n\n // Try to get a deal with a very high ID that likely doesn't exist\n // Note: Official SDK doesn't have a simple getDeal method, so skip this\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Non-existent Resource Error', false, \"Method not available in official SDK - API structure different\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Non-existent Resource Error', true, \"Correctly threw exception: {$e->getMessage()} in {$elapsed}ms\", $elapsed);\n }\n }\n\n private function recordResult(string $testName, bool $success, string $message, float $elapsed): void\n {\n $this->results[] = [\n 'test' => $testName,\n 'success' => $success,\n 'message' => $message,\n 'elapsed' => $elapsed,\n ];\n\n $status = $success ? '✅ PASS' : '❌ FAIL';\n $this->line(\" {$status} - {$testName}: {$message}\");\n }\n\n private function displayResults(): void\n {\n $this->newLine();\n $this->info('=== POC Test Results Summary ===');\n $this->newLine();\n\n $total = count($this->results);\n $passed = count(array_filter($this->results, fn ($r) => $r['success']));\n $failed = $total - $passed;\n\n $this->line(\"Total Tests: {$total}\");\n $this->line(\"Passed: {$passed}\");\n $this->line(\"Failed: {$failed}\");\n $this->newLine();\n\n $totalTime = array_sum(array_column($this->results, 'elapsed'));\n $this->line(\"Total Execution Time: {$totalTime}ms\");\n $this->newLine();\n\n if ($failed > 0) {\n $this->warn('Failed Tests:');\n foreach ($this->results as $result) {\n if (! $result['success']) {\n $this->line(\" - {$result['test']}: {$result['message']}\");\n }\n }\n $this->newLine();\n }\n\n $this->info('=== Detailed Results ===');\n foreach ($this->results as $result) {\n $status = $result['success'] ? '✅' : '❌';\n $this->line(\"{$status} {$result['test']} ({$result['elapsed']}ms): {$result['message']}\");\n }\n\n $this->newLine();\n $this->info('=== Recommendations ===');\n\n if ($passed === $total) {\n $this->info('✅ All tests passed! The official SDK is viable for migration.');\n $this->info('Next steps: Proceed with full migration planning.');\n } else {\n $this->warn('⚠️ Some tests failed. Review the failures above before proceeding.');\n $this->warn('Consider hybrid approach or investigate alternatives if critical operations fail.');\n }\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Console\\Commands\\Crm;\n\nuse Carbon\\Carbon;\nuse Exception;\nuse Illuminate\\Console\\Command;\nuse Jiminny\\Component\\Encryption\\EncryptedTokenManager;\nuse Jiminny\\Models\\SocialAccount;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Pipedrive\\versions\\v1\\Api\\ActivitiesApi;\nuse Pipedrive\\versions\\v1\\Api\\ActivityFieldsApi;\nuse Pipedrive\\versions\\v1\\Api\\DealFieldsApi;\nuse Pipedrive\\versions\\v1\\Api\\DealsApi;\nuse Pipedrive\\versions\\v1\\Api\\PersonFieldsApi;\nuse Pipedrive\\versions\\v1\\Api\\PersonsApi;\nuse Pipedrive\\versions\\v1\\Configuration;\n\nclass TestPipedriveOfficialSdkCommand extends Command\n{\n protected $signature = 'jiminny:test-pipedrive-official-sdk {teamId}';\n\n protected $description = 'Test official Pipedrive SDK (pipedrive/pipedrive) with existing OAuth tokens';\n\n private array $results = [];\n\n private EncryptedTokenManager $tokenManager;\n\n public function __construct(EncryptedTokenManager $tokenManager)\n {\n parent::__construct();\n $this->tokenManager = $tokenManager;\n }\n\n public function handle(): int\n {\n $teamId = (int) $this->argument('teamId');\n\n $this->info(\"Starting Pipedrive Official SDK POC for Team ID: {$teamId}\");\n $this->newLine();\n\n try {\n $this->runTests($teamId);\n } catch (Exception $e) {\n $this->error(\"POC failed with error: {$e->getMessage()}\");\n $this->error($e->getTraceAsString());\n\n return 1;\n }\n\n $this->displayResults();\n\n return 0;\n }\n\n private function runTests(int $teamId): void\n {\n $team = Team::find($teamId);\n if (! $team) {\n throw new Exception(\"Team with ID {$teamId} not found\");\n }\n\n $socialAccount = SocialAccount::where('provider', SocialAccount::PROVIDER_PIPEDRIVE)\n ->whereHas('sociable', function ($query) use ($team) {\n $query->where('team_id', $team->id);\n })\n ->orderByDesc('expires') // Get the most recently expiring token\n ->first();\n\n if (! $socialAccount) {\n throw new Exception(\"No Pipedrive social account found for team {$teamId}\");\n }\n\n $this->info(\"Found Pipedrive account for team: {$team->name}\");\n $this->newLine();\n\n // Test 1: OAuth Initialization\n $this->testOAuthInitialization($socialAccount);\n\n // Test 2: Basic CRUD Operations\n $this->testBasicCrudOperations($socialAccount);\n\n // Test 3: Field Metadata\n $this->testFieldMetadata($socialAccount);\n\n // Test 4: v2 API Support\n $this->testV2ApiSupport($socialAccount);\n\n // Test 5: Error Handling\n $this->testErrorHandling($socialAccount);\n }\n\n private function testOAuthInitialization(SocialAccount $socialAccount): void\n {\n $this->info('Test 1: OAuth Initialization');\n $startTime = microtime(true);\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n\n $client = new DealsApi(null, $config);\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('OAuth Initialization', true, \"Client initialized successfully in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('OAuth Initialization', false, $e->getMessage(), $elapsed);\n }\n\n $this->newLine();\n }\n\n private function testBasicCrudOperations(SocialAccount $socialAccount): void\n {\n $this->info('Test 2: Basic CRUD Operations');\n\n try {\n // Get the raw token from database\n $rawDbToken = $socialAccount->getProviderUserToken();\n $this->info(\"DB token (length: \" . strlen($rawDbToken) . \")\");\n $this->info(\"DB token preview: \" . substr($rawDbToken, 0, 20) . \"...\");\n\n // Check token expiry\n $expires = $socialAccount->expires;\n $this->info(\"Token expires at: \" . ($expires ? Carbon::createFromTimestamp($expires)->toDateTimeString() : 'null'));\n $this->info(\"Token is expired: \" . ($expires && $expires < time() ? 'YES' : 'NO'));\n\n // Test with the DB token directly\n $this->info(\"\\n--- Testing with DB token ---\");\n $config = new Configuration();\n $config->setAccessToken($rawDbToken);\n\n // Test 2a: Get Deals\n $this->testGetDeals($config);\n\n // Test 2b: Get Persons\n $this->testGetPersons($config);\n\n // Test 2c: Get Activities\n $this->testGetActivities($config);\n } catch (Exception $e) {\n $this->recordResult('Basic CRUD Operations', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testCurrentSystemApproach(SocialAccount $socialAccount): void\n {\n $startTime = microtime(true);\n\n try {\n // Simulate how current system makes requests\n $token = $socialAccount->getProviderUserToken();\n $url = 'https://api.pipedrive.com/api/v2/deals';\n\n $headers = [\n 'Authorization' => 'Bearer ' . $token,\n 'Accept' => 'application/json',\n ];\n\n $client = new \\GuzzleHttp\\Client();\n $response = $client->get($url, [\n 'headers' => $headers,\n 'timeout' => 30,\n ]);\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $statusCode = $response->getStatusCode();\n $this->recordResult('Current System v2 API', $statusCode === 200, \"Status: {$statusCode} in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Current System v2 API', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetDeals(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new DealsApi(null, $config);\n // Official SDK uses getDealsSummary instead of getDeals\n $result = $apiInstance->getDealsSummary();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Deals Summary', true, \"Retrieved deals summary in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Deals Summary', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetPersons(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n // Official SDK doesn't have a simple getPersons method\n // Skip this test for now as the API structure is different\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Persons', false, \"Method not available in official SDK - API structure different\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Persons', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetActivities(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n // Official SDK doesn't have a simple getActivities method\n // Skip this test for now as the API structure is different\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Activities', false, \"Method not available in official SDK - API structure different\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Activities', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testFieldMetadata(SocialAccount $socialAccount): void\n {\n $this->info('Test 3: Field Metadata');\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n\n // Test 3a: Get Deal Fields\n $this->testGetDealFields($config);\n\n // Test 3b: Get Person Fields\n $this->testGetPersonFields($config);\n\n // Test 3c: Get Activity Fields\n $this->testGetActivityFields($config);\n } catch (Exception $e) {\n $this->recordResult('Field Metadata', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testGetDealFields(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new DealFieldsApi(null, $config);\n $result = $apiInstance->getDealFields();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $count = $result->getData() ? count($result->getData()) : 0;\n $this->recordResult('Get Deal Fields', true, \"Retrieved {$count} deal fields in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Deal Fields', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetPersonFields(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new PersonFieldsApi(null, $config);\n $result = $apiInstance->getPersonFields();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $count = $result->getData() ? count($result->getData()) : 0;\n $this->recordResult('Get Person Fields', true, \"Retrieved {$count} person fields in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Person Fields', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetActivityFields(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new ActivityFieldsApi(null, $config);\n $result = $apiInstance->getActivityFields();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $count = $result->getData() ? count($result->getData()) : 0;\n $this->recordResult('Get Activity Fields', true, \"Retrieved {$count} activity fields in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Activity Fields', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testV2ApiSupport(SocialAccount $socialAccount): void\n {\n $this->info('Test 4: v2 API Support (Cursor-based Pagination)');\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n\n $startTime = microtime(true);\n\n // Test v2 deals endpoint with cursor pagination\n // Note: The official SDK may or may not have v2 API support\n // This test will determine if v2 is available\n try {\n $apiInstance = new DealsApi(null, $config);\n $updatedSince = Carbon::now()->subDays(30)->toRfc3339String();\n $result = $apiInstance->getDealsSummary();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n\n // Check if response has cursor information (v2 indicator)\n // The response object doesn't have getAdditionalData method\n // Just test if the call works - v2 API support is limited in official SDK v1\n $this->recordResult('v2 API Support', false, \"v1 API works but v2 cursor pagination not available in official SDK v1. Retrieved summary in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('v2 API Support', false, $e->getMessage(), $elapsed);\n }\n } catch (Exception $e) {\n $this->recordResult('v2 API Support', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testErrorHandling(SocialAccount $socialAccount): void\n {\n $this->info('Test 5: Error Handling');\n\n try {\n // Test 5a: Invalid Token\n $this->testInvalidToken();\n\n // Test 5b: Non-existent Resource\n $this->testNonExistentResource($socialAccount);\n } catch (Exception $e) {\n $this->recordResult('Error Handling', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testInvalidToken(): void\n {\n $startTime = microtime(true);\n\n try {\n $config = new Configuration();\n $config->setAccessToken('invalid_token');\n $client = new DealsApi(null, $config);\n\n $client->getDealsSummary();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Invalid Token Error', false, \"Expected error but request succeeded in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Invalid Token Error', true, \"Correctly threw exception: {$e->getMessage()} in {$elapsed}ms\", $elapsed);\n }\n }\n\n private function testNonExistentResource(SocialAccount $socialAccount): void\n {\n $startTime = microtime(true);\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n $client = new DealsApi(null, $config);\n\n // Try to get a deal with a very high ID that likely doesn't exist\n // Note: Official SDK doesn't have a simple getDeal method, so skip this\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Non-existent Resource Error', false, \"Method not available in official SDK - API structure different\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Non-existent Resource Error', true, \"Correctly threw exception: {$e->getMessage()} in {$elapsed}ms\", $elapsed);\n }\n }\n\n private function recordResult(string $testName, bool $success, string $message, float $elapsed): void\n {\n $this->results[] = [\n 'test' => $testName,\n 'success' => $success,\n 'message' => $message,\n 'elapsed' => $elapsed,\n ];\n\n $status = $success ? '✅ PASS' : '❌ FAIL';\n $this->line(\" {$status} - {$testName}: {$message}\");\n }\n\n private function displayResults(): void\n {\n $this->newLine();\n $this->info('=== POC Test Results Summary ===');\n $this->newLine();\n\n $total = count($this->results);\n $passed = count(array_filter($this->results, fn ($r) => $r['success']));\n $failed = $total - $passed;\n\n $this->line(\"Total Tests: {$total}\");\n $this->line(\"Passed: {$passed}\");\n $this->line(\"Failed: {$failed}\");\n $this->newLine();\n\n $totalTime = array_sum(array_column($this->results, 'elapsed'));\n $this->line(\"Total Execution Time: {$totalTime}ms\");\n $this->newLine();\n\n if ($failed > 0) {\n $this->warn('Failed Tests:');\n foreach ($this->results as $result) {\n if (! $result['success']) {\n $this->line(\" - {$result['test']}: {$result['message']}\");\n }\n }\n $this->newLine();\n }\n\n $this->info('=== Detailed Results ===');\n foreach ($this->results as $result) {\n $status = $result['success'] ? '✅' : '❌';\n $this->line(\"{$status} {$result['test']} ({$result['elapsed']}ms): {$result['message']}\");\n }\n\n $this->newLine();\n $this->info('=== Recommendations ===');\n\n if ($passed === $total) {\n $this->info('✅ All tests passed! The official SDK is viable for migration.');\n $this->info('Next steps: Proceed with full migration planning.');\n } else {\n $this->warn('⚠️ Some tests failed. Review the failures above before proceeding.');\n $this->warn('Consider hybrid approach or investigate alternatives if critical operations fail.');\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}]...
|
5365831007722038384
|
61370652661533572
|
idle
|
accessibility
|
NULL
|
Project: faVsco.js, menu
pipedrive-sdk-poc, menu
S Project: faVsco.js, menu
pipedrive-sdk-poc, 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\Console\Commands\Crm;
use Jiminny\Console\Commands\Command;
use Jiminny\Console\Commands\Crm\Traits\SyncObjectsCommandTrait;
use Jiminny\Jobs\Crm\SyncObjects as SyncObjectsJob;
use Jiminny\Jobs\Job;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Models\Team;
class SyncObjects extends Command
{
use SyncObjectsCommandTrait;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'crm:sync-objects {team?} {--sync}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Sync remote CRM objects.';
/**
* Execute the console command.
*/
public function handle(): void
{
$teamId = $this->argument('team');
$teams = [];
if ($teamId) {
$team = Team::idOrUuId($teamId);
if ($team && $team->crm?->provider === Configuration::PROVIDER_HUBSPOT) {
$this->error(sprintf(
'Team %s (%s) uses HubSpot. Use crm:sync-hubspot-objects instead.',
$team->getName(),
$team->getUuid()
));
return;
}
if ($team) {
$teams[] = $team;
}
} else {
// Exclude HubSpot teams - handled by crm:sync-hubspot-objects
$teams = Team::where('status', Team::STATUS_ACTIVE)
->whereHas(
'crm',
fn ($q) => $q
->where('provider', '!=', Configuration::PROVIDER_HUBSPOT)
->where('sync_objects', true)
)
->get();
}
$this->dispatchSyncJobsForTeams($teams);
}
protected function getStaggerDelaySeconds(): float
{
return 2;
}
protected function getMaxDelaySeconds(): int
{
// Cap delay at 15 minutes (SQS max delay limit)
// capacity of 450 teams before reach
return 900;
}
protected function getLogPrefix(): string
{
return '';
}
protected function createSyncJob(Team $team): Job
{
return new SyncObjectsJob($team);
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
1
51
1
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Console\Commands\Crm;
use Carbon\Carbon;
use Exception;
use Illuminate\Console\Command;
use Jiminny\Component\Encryption\EncryptedTokenManager;
use Jiminny\Models\SocialAccount;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Pipedrive\versions\v1\Api\ActivitiesApi;
use Pipedrive\versions\v1\Api\ActivityFieldsApi;
use Pipedrive\versions\v1\Api\DealFieldsApi;
use Pipedrive\versions\v1\Api\DealsApi;
use Pipedrive\versions\v1\Api\PersonFieldsApi;
use Pipedrive\versions\v1\Api\PersonsApi;
use Pipedrive\versions\v1\Configuration;
class TestPipedriveOfficialSdkCommand extends Command
{
protected $signature = 'jiminny:test-pipedrive-official-sdk {teamId}';
protected $description = 'Test official Pipedrive SDK (pipedrive/pipedrive) with existing OAuth tokens';
private array $results = [];
private EncryptedTokenManager $tokenManager;
public function __construct(EncryptedTokenManager $tokenManager)
{
parent::__construct();
$this->tokenManager = $tokenManager;
}
public function handle(): int
{
$teamId = (int) $this->argument('teamId');
$this->info("Starting Pipedrive Official SDK POC for Team ID: {$teamId}");
$this->newLine();
try {
$this->runTests($teamId);
} catch (Exception $e) {
$this->error("POC failed with error: {$e->getMessage()}");
$this->error($e->getTraceAsString());
return 1;
}
$this->displayResults();
return 0;
}
private function runTests(int $teamId): void
{
$team = Team::find($teamId);
if (! $team) {
throw new Exception("Team with ID {$teamId} not found");
}
$socialAccount = SocialAccount::where('provider', SocialAccount::PROVIDER_PIPEDRIVE)
->whereHas('sociable', function ($query) use ($team) {
$query->where('team_id', $team->id);
})
->orderByDesc('expires') // Get the most recently expiring token
->first();
if (! $socialAccount) {
throw new Exception("No Pipedrive social account found for team {$teamId}");
}
$this->info("Found Pipedrive account for team: {$team->name}");
$this->newLine();
// Test 1: OAuth Initialization
$this->testOAuthInitialization($socialAccount);
// Test 2: Basic CRUD Operations
$this->testBasicCrudOperations($socialAccount);
// Test 3: Field Metadata
$this->testFieldMetadata($socialAccount);
// Test 4: v2 API Support
$this->testV2ApiSupport($socialAccount);
// Test 5: Error Handling
$this->testErrorHandling($socialAccount);
}
private function testOAuthInitialization(SocialAccount $socialAccount): void
{
$this->info('Test 1: OAuth Initialization');
$startTime = microtime(true);
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
$client = new DealsApi(null, $config);
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('OAuth Initialization', true, "Client initialized successfully in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('OAuth Initialization', false, $e->getMessage(), $elapsed);
}
$this->newLine();
}
private function testBasicCrudOperations(SocialAccount $socialAccount): void
{
$this->info('Test 2: Basic CRUD Operations');
try {
// Get the raw token from database
$rawDbToken = $socialAccount->getProviderUserToken();
$this->info("DB token (length: " . strlen($rawDbToken) . ")");
$this->info("DB token preview: " . substr($rawDbToken, 0, 20) . "...");
// Check token expiry
$expires = $socialAccount->expires;
$this->info("Token expires at: " . ($expires ? Carbon::createFromTimestamp($expires)->toDateTimeString() : 'null'));
$this->info("Token is expired: " . ($expires && $expires < time() ? 'YES' : 'NO'));
// Test with the DB token directly
$this->info("\n--- Testing with DB token ---");
$config = new Configuration();
$config->setAccessToken($rawDbToken);
// Test 2a: Get Deals
$this->testGetDeals($config);
// Test 2b: Get Persons
$this->testGetPersons($config);
// Test 2c: Get Activities
$this->testGetActivities($config);
} catch (Exception $e) {
$this->recordResult('Basic CRUD Operations', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testCurrentSystemApproach(SocialAccount $socialAccount): void
{
$startTime = microtime(true);
try {
// Simulate how current system makes requests
$token = $socialAccount->getProviderUserToken();
$url = 'https://api.pipedrive.com/api/v2/deals';
$headers = [
'Authorization' => 'Bearer ' . $token,
'Accept' => 'application/json',
];
$client = new \GuzzleHttp\Client();
$response = $client->get($url, [
'headers' => $headers,
'timeout' => 30,
]);
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$statusCode = $response->getStatusCode();
$this->recordResult('Current System v2 API', $statusCode === 200, "Status: {$statusCode} in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Current System v2 API', false, $e->getMessage(), $elapsed);
}
}
private function testGetDeals(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new DealsApi(null, $config);
// Official SDK uses getDealsSummary instead of getDeals
$result = $apiInstance->getDealsSummary();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Deals Summary', true, "Retrieved deals summary in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Deals Summary', false, $e->getMessage(), $elapsed);
}
}
private function testGetPersons(Configuration $config): void
{
$startTime = microtime(true);
try {
// Official SDK doesn't have a simple getPersons method
// Skip this test for now as the API structure is different
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Persons', false, "Method not available in official SDK - API structure different", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Persons', false, $e->getMessage(), $elapsed);
}
}
private function testGetActivities(Configuration $config): void
{
$startTime = microtime(true);
try {
// Official SDK doesn't have a simple getActivities method
// Skip this test for now as the API structure is different
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Activities', false, "Method not available in official SDK - API structure different", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Activities', false, $e->getMessage(), $elapsed);
}
}
private function testFieldMetadata(SocialAccount $socialAccount): void
{
$this->info('Test 3: Field Metadata');
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
// Test 3a: Get Deal Fields
$this->testGetDealFields($config);
// Test 3b: Get Person Fields
$this->testGetPersonFields($config);
// Test 3c: Get Activity Fields
$this->testGetActivityFields($config);
} catch (Exception $e) {
$this->recordResult('Field Metadata', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testGetDealFields(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new DealFieldsApi(null, $config);
$result = $apiInstance->getDealFields();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$count = $result->getData() ? count($result->getData()) : 0;
$this->recordResult('Get Deal Fields', true, "Retrieved {$count} deal fields in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Deal Fields', false, $e->getMessage(), $elapsed);
}
}
private function testGetPersonFields(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new PersonFieldsApi(null, $config);
$result = $apiInstance->getPersonFields();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$count = $result->getData() ? count($result->getData()) : 0;
$this->recordResult('Get Person Fields', true, "Retrieved {$count} person fields in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Person Fields', false, $e->getMessage(), $elapsed);
}
}
private function testGetActivityFields(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new ActivityFieldsApi(null, $config);
$result = $apiInstance->getActivityFields();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$count = $result->getData() ? count($result->getData()) : 0;
$this->recordResult('Get Activity Fields', true, "Retrieved {$count} activity fields in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Activity Fields', false, $e->getMessage(), $elapsed);
}
}
private function testV2ApiSupport(SocialAccount $socialAccount): void
{
$this->info('Test 4: v2 API Support (Cursor-based Pagination)');
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
$startTime = microtime(true);
// Test v2 deals endpoint with cursor pagination
// Note: The official SDK may or may not have v2 API support
// This test will determine if v2 is available
try {
$apiInstance = new DealsApi(null, $config);
$updatedSince = Carbon::now()->subDays(30)->toRfc3339String();
$result = $apiInstance->getDealsSummary();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
// Check if response has cursor information (v2 indicator)
// The response object doesn't have getAdditionalData method
// Just test if the call works - v2 API support is limited in official SDK v1
$this->recordResult('v2 API Support', false, "v1 API works but v2 cursor pagination not available in official SDK v1. Retrieved summary in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('v2 API Support', false, $e->getMessage(), $elapsed);
}
} catch (Exception $e) {
$this->recordResult('v2 API Support', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testErrorHandling(SocialAccount $socialAccount): void
{
$this->info('Test 5: Error Handling');
try {
// Test 5a: Invalid Token
$this->testInvalidToken();
// Test 5b: Non-existent Resource
$this->testNonExistentResource($socialAccount);
} catch (Exception $e) {
$this->recordResult('Error Handling', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testInvalidToken(): void
{
$startTime = microtime(true);
try {
$config = new Configuration();
$config->setAccessToken('invalid_token');
$client = new DealsApi(null, $config);
$client->getDealsSummary();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Invalid Token Error', false, "Expected error but request succeeded in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Invalid Token Error', true, "Correctly threw exception: {$e->getMessage()} in {$elapsed}ms", $elapsed);
}
}
private function testNonExistentResource(SocialAccount $socialAccount): void
{
$startTime = microtime(true);
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
$client = new DealsApi(null, $config);
// Try to get a deal with a very high ID that likely doesn't exist
// Note: Official SDK doesn't have a simple getDeal method, so skip this
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Non-existent Resource Error', false, "Method not available in official SDK - API structure different", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Non-existent Resource Error', true, "Correctly threw exception: {$e->getMessage()} in {$elapsed}ms", $elapsed);
}
}
private function recordResult(string $testName, bool $success, string $message, float $elapsed): void
{
$this->results[] = [
'test' => $testName,
'success' => $success,
'message' => $message,
'elapsed' => $elapsed,
];
$status = $success ? '✅ PASS' : '❌ FAIL';
$this->line(" {$status} - {$testName}: {$message}");
}
private function displayResults(): void
{
$this->newLine();
$this->info('=== POC Test Results Summary ===');
$this->newLine();
$total = count($this->results);
$passed = count(array_filter($this->results, fn ($r) => $r['success']));
$failed = $total - $passed;
$this->line("Total Tests: {$total}");
$this->line("Passed: {$passed}");
$this->line("Failed: {$failed}");
$this->newLine();
$totalTime = array_sum(array_column($this->results, 'elapsed'));
$this->line("Total Execution Time: {$totalTime}ms");
$this->newLine();
if ($failed > 0) {
$this->warn('Failed Tests:');
foreach ($this->results as $result) {
if (! $result['success']) {
$this->line(" - {$result['test']}: {$result['message']}");
}
}
$this->newLine();
}
$this->info('=== Detailed Results ===');
foreach ($this->results as $result) {
$status = $result['success'] ? '✅' : '❌';
$this->line("{$status} {$result['test']} ({$result['elapsed']}ms): {$result['message']}");
}
$this->newLine();
$this->info('=== Recommendations ===');
if ($passed === $total) {
$this->info('✅ All tests passed! The official SDK is viable for migration.');
$this->info('Next steps: Proceed with full migration planning.');
} else {
$this->warn('⚠️ Some tests failed. Review the failures above before proceeding.');
$this->warn('Consider hybrid approach or investigate alternatives if critical operations fail.');
}
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
49394
|
NULL
|
NULL
|
NULL
|
|
49396
|
1762
|
21
|
2026-05-18T06:39:57.425889+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779086397425_m1.jpg...
|
PhpStorm
|
faVsco.js – TestPipedriveOfficialSdkCommand.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
pipedrive-sdk-poc, menu
S Project: faVsco.js, menu
pipedrive-sdk-poc, 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\Console\Commands\Crm;
use Jiminny\Console\Commands\Command;
use Jiminny\Console\Commands\Crm\Traits\SyncObjectsCommandTrait;
use Jiminny\Jobs\Crm\SyncObjects as SyncObjectsJob;
use Jiminny\Jobs\Job;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Models\Team;
class SyncObjects extends Command
{
use SyncObjectsCommandTrait;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'crm:sync-objects {team?} {--sync}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Sync remote CRM objects.';
/**
* Execute the console command.
*/
public function handle(): void
{
$teamId = $this->argument('team');
$teams = [];
if ($teamId) {
$team = Team::idOrUuId($teamId);
if ($team && $team->crm?->provider === Configuration::PROVIDER_HUBSPOT) {
$this->error(sprintf(
'Team %s (%s) uses HubSpot. Use crm:sync-hubspot-objects instead.',
$team->getName(),
$team->getUuid()
));
return;
}
if ($team) {
$teams[] = $team;
}
} else {
// Exclude HubSpot teams - handled by crm:sync-hubspot-objects
$teams = Team::where('status', Team::STATUS_ACTIVE)
->whereHas(
'crm',
fn ($q) => $q
->where('provider', '!=', Configuration::PROVIDER_HUBSPOT)
->where('sync_objects', true)
)
->get();
}
$this->dispatchSyncJobsForTeams($teams);
}
protected function getStaggerDelaySeconds(): float
{
return 2;
}
protected function getMaxDelaySeconds(): int
{
// Cap delay at 15 minutes (SQS max delay limit)
// capacity of 450 teams before reach
return 900;
}
protected function getLogPrefix(): string
{
return '';
}
protected function createSyncJob(Team $team): Job
{
return new SyncObjectsJob($team);
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
1
51
1
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Console\Commands\Crm;
use Carbon\Carbon;
use Exception;
use Illuminate\Console\Command;
use Jiminny\Component\Encryption\EncryptedTokenManager;
use Jiminny\Models\SocialAccount;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Pipedrive\versions\v1\Api\ActivitiesApi;
use Pipedrive\versions\v1\Api\ActivityFieldsApi;
use Pipedrive\versions\v1\Api\DealFieldsApi;
use Pipedrive\versions\v1\Api\DealsApi;
use Pipedrive\versions\v1\Api\PersonFieldsApi;
use Pipedrive\versions\v1\Api\PersonsApi;
use Pipedrive\versions\v1\Configuration;
class TestPipedriveOfficialSdkCommand extends Command
{
protected $signature = 'jiminny:test-pipedrive-official-sdk {teamId}';
protected $description = 'Test official Pipedrive SDK (pipedrive/pipedrive) with existing OAuth tokens';
private array $results = [];
private EncryptedTokenManager $tokenManager;
public function __construct(EncryptedTokenManager $tokenManager)
{
parent::__construct();
$this->tokenManager = $tokenManager;
}
public function handle(): int
{
$teamId = (int) $this->argument('teamId');
$this->info("Starting Pipedrive Official SDK POC for Team ID: {$teamId}");
$this->newLine();
try {
$this->runTests($teamId);
} catch (Exception $e) {
$this->error("POC failed with error: {$e->getMessage()}");
$this->error($e->getTraceAsString());
return 1;
}
$this->displayResults();
return 0;
}
private function runTests(int $teamId): void
{
$team = Team::find($teamId);
if (! $team) {
throw new Exception("Team with ID {$teamId} not found");
}
$socialAccount = SocialAccount::where('provider', SocialAccount::PROVIDER_PIPEDRIVE)
->whereHas('sociable', function ($query) use ($team) {
$query->where('team_id', $team->id);
})
->orderByDesc('expires') // Get the most recently expiring token
->first();
if (! $socialAccount) {
throw new Exception("No Pipedrive social account found for team {$teamId}");
}
$this->info("Found Pipedrive account for team: {$team->name}");
$this->newLine();
// Test 1: OAuth Initialization
$this->testOAuthInitialization($socialAccount);
// Test 2: Basic CRUD Operations
$this->testBasicCrudOperations($socialAccount);
// Test 3: Field Metadata
$this->testFieldMetadata($socialAccount);
// Test 4: v2 API Support
$this->testV2ApiSupport($socialAccount);
// Test 5: Error Handling
$this->testErrorHandling($socialAccount);
}
private function testOAuthInitialization(SocialAccount $socialAccount): void
{
$this->info('Test 1: OAuth Initialization');
$startTime = microtime(true);
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
$client = new DealsApi(null, $config);
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('OAuth Initialization', true, "Client initialized successfully in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('OAuth Initialization', false, $e->getMessage(), $elapsed);
}
$this->newLine();
}
private function testBasicCrudOperations(SocialAccount $socialAccount): void
{
$this->info('Test 2: Basic CRUD Operations');
try {
// Get the raw token from database
$rawDbToken = $socialAccount->getProviderUserToken();
$this->info("DB token (length: " . strlen($rawDbToken) . ")");
$this->info("DB token preview: " . substr($rawDbToken, 0, 20) . "...");
// Check token expiry
$expires = $socialAccount->expires;
$this->info("Token expires at: " . ($expires ? Carbon::createFromTimestamp($expires)->toDateTimeString() : 'null'));
$this->info("Token is expired: " . ($expires && $expires < time() ? 'YES' : 'NO'));
// Test with the DB token directly
$this->info("\n--- Testing with DB token ---");
$config = new Configuration();
$config->setAccessToken($rawDbToken);
// Test 2a: Get Deals
$this->testGetDeals($config);
// Test 2b: Get Persons
$this->testGetPersons($config);
// Test 2c: Get Activities
$this->testGetActivities($config);
} catch (Exception $e) {
$this->recordResult('Basic CRUD Operations', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testCurrentSystemApproach(SocialAccount $socialAccount): void
{
$startTime = microtime(true);
try {
// Simulate how current system makes requests
$token = $socialAccount->getProviderUserToken();
$url = 'https://api.pipedrive.com/api/v2/deals';
$headers = [
'Authorization' => 'Bearer ' . $token,
'Accept' => 'application/json',
];
$client = new \GuzzleHttp\Client();
$response = $client->get($url, [
'headers' => $headers,
'timeout' => 30,
]);
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$statusCode = $response->getStatusCode();
$this->recordResult('Current System v2 API', $statusCode === 200, "Status: {$statusCode} in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Current System v2 API', false, $e->getMessage(), $elapsed);
}
}
private function testGetDeals(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new DealsApi(null, $config);
// Official SDK uses getDealsSummary instead of getDeals
$result = $apiInstance->getDealsSummary();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Deals Summary', true, "Retrieved deals summary in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Deals Summary', false, $e->getMessage(), $elapsed);
}
}
private function testGetPersons(Configuration $config): void
{
$startTime = microtime(true);
try {
// Official SDK doesn't have a simple getPersons method
// Skip this test for now as the API structure is different
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Persons', false, "Method not available in official SDK - API structure different", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Persons', false, $e->getMessage(), $elapsed);
}
}
private function testGetActivities(Configuration $config): void
{
$startTime = microtime(true);
try {
// Official SDK doesn't have a simple getActivities method
// Skip this test for now as the API structure is different
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Activities', false, "Method not available in official SDK - API structure different", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Activities', false, $e->getMessage(), $elapsed);
}
}
private function testFieldMetadata(SocialAccount $socialAccount): void
{
$this->info('Test 3: Field Metadata');
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
// Test 3a: Get Deal Fields
$this->testGetDealFields($config);
// Test 3b: Get Person Fields
$this->testGetPersonFields($config);
// Test 3c: Get Activity Fields
$this->testGetActivityFields($config);
} catch (Exception $e) {
$this->recordResult('Field Metadata', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testGetDealFields(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new DealFieldsApi(null, $config);
$result = $apiInstance->getDealFields();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$count = $result->getData() ? count($result->getData()) : 0;
$this->recordResult('Get Deal Fields', true, "Retrieved {$count} deal fields in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Deal Fields', false, $e->getMessage(), $elapsed);
}
}
private function testGetPersonFields(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new PersonFieldsApi(null, $config);
$result = $apiInstance->getPersonFields();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$count = $result->getData() ? count($result->getData()) : 0;
$this->recordResult('Get Person Fields', true, "Retrieved {$count} person fields in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Person Fields', false, $e->getMessage(), $elapsed);
}
}
private function testGetActivityFields(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new ActivityFieldsApi(null, $config);
$result = $apiInstance->getActivityFields();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$count = $result->getData() ? count($result->getData()) : 0;
$this->recordResult('Get Activity Fields', true, "Retrieved {$count} activity fields in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Activity Fields', false, $e->getMessage(), $elapsed);
}
}
private function testV2ApiSupport(SocialAccount $socialAccount): void
{
$this->info('Test 4: v2 API Support (Cursor-based Pagination)');
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
$startTime = microtime(true);
// Test v2 deals endpoint with cursor pagination
// Note: The official SDK may or may not have v2 API support
// This test will determine if v2 is available
try {
$apiInstance = new DealsApi(null, $config);
$updatedSince = Carbon::now()->subDays(30)->toRfc3339String();
$result = $apiInstance->getDealsSummary();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
// Check if response has cursor information (v2 indicator)
// The response object doesn't have getAdditionalData method
// Just test if the call works - v2 API support is limited in official SDK v1
$this->recordResult('v2 API Support', false, "v1 API works but v2 cursor pagination not available in official SDK v1. Retrieved summary in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('v2 API Support', false, $e->getMessage(), $elapsed);
}
} catch (Exception $e) {
$this->recordResult('v2 API Support', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testErrorHandling(SocialAccount $socialAccount): void
{
$this->info('Test 5: Error Handling');
try {
// Test 5a: Invalid Token
$this->testInvalidToken();
// Test 5b: Non-existent Resource
$this->testNonExistentResource($socialAccount);
} catch (Exception $e) {
$this->recordResult('Error Handling', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testInvalidToken(): void
{
$startTime = microtime(true);
try {
$config = new Configuration();
$config->setAccessToken('invalid_token');
$client = new DealsApi(null, $config);
$client->getDealsSummary();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Invalid Token Error', false, "Expected error but request succeeded in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Invalid Token Error', true, "Correctly threw exception: {$e->getMessage()} in {$elapsed}ms", $elapsed);
}
}
private function testNonExistentResource(SocialAccount $socialAccount): void
{
$startTime = microtime(true);
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
$client = new DealsApi(null, $config);
// Try to get a deal with a very high ID that likely doesn't exist
// Note: Official SDK doesn't have a simple getDeal method, so skip this
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Non-existent Resource Error', false, "Method not available in official SDK - API structure different", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Non-existent Resource Error', true, "Correctly threw exception: {$e->getMessage()} in {$elapsed}ms", $elapsed);
}
}
private function recordResult(string $testName, bool $success, string $message, float $elapsed): void
{
$this->results[] = [
'test' => $testName,
'success' => $success,
'message' => $message,
'elapsed' => $elapsed,
];
$status = $success ? '✅ PASS' : '❌ FAIL';
$this->line(" {$status} - {$testName}: {$message}");
}
private function displayResults(): void
{
$this->newLine();
$this->info('=== POC Test Results Summary ===');
$this->newLine();
$total = count($this->results);
$passed = count(array_filter($this->results, fn ($r) => $r['success']));
$failed = $total - $passed;
$this->line("Total Tests: {$total}");
$this->line("Passed: {$passed}");
$this->line("Failed: {$failed}");
$this->newLine();
$totalTime = array_sum(array_column($this->results, 'elapsed'));
$this->line("Total Execution Time: {$totalTime}ms");
$this->newLine();
if ($failed > 0) {
$this->warn('Failed Tests:');
foreach ($this->results as $result) {
if (! $result['success']) {
$this->line(" - {$result['test']}: {$result['message']}");
}
}
$this->newLine();
}
$this->info('=== Detailed Results ===');
foreach ($this->results as $result) {
$status = $result['success'] ? '✅' : '❌';
$this->line("{$status} {$result['test']} ({$result['elapsed']}ms): {$result['message']}");
}
$this->newLine();
$this->info('=== Recommendations ===');
if ($passed === $total) {
$this->info('✅ All tests passed! The official SDK is viable for migration.');
$this->info('Next steps: Proceed with full migration planning.');
} else {
$this->warn('⚠️ Some tests failed. Review the failures above before proceeding.');
$this->warn('Consider hybrid approach or investigate alternatives if critical operations fail.');
}
}
}
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":"pipedrive-sdk-poc, menu","depth":5,"on_screen":true,"help_text":"Git Branch: pipedrive-sdk-poc","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\\Console\\Commands\\Crm;\n\nuse Jiminny\\Console\\Commands\\Command;\nuse Jiminny\\Console\\Commands\\Crm\\Traits\\SyncObjectsCommandTrait;\nuse Jiminny\\Jobs\\Crm\\SyncObjects as SyncObjectsJob;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Models\\Team;\n\nclass SyncObjects extends Command\n{\n use SyncObjectsCommandTrait;\n\n /**\n * The name and signature of the console command.\n *\n * @var string\n */\n protected $signature = 'crm:sync-objects {team?} {--sync}';\n\n /**\n * The console command description.\n *\n * @var string\n */\n protected $description = 'Sync remote CRM objects.';\n\n /**\n * Execute the console command.\n */\n public function handle(): void\n {\n $teamId = $this->argument('team');\n $teams = [];\n\n if ($teamId) {\n $team = Team::idOrUuId($teamId);\n if ($team && $team->crm?->provider === Configuration::PROVIDER_HUBSPOT) {\n $this->error(sprintf(\n 'Team %s (%s) uses HubSpot. Use crm:sync-hubspot-objects instead.',\n $team->getName(),\n $team->getUuid()\n ));\n\n return;\n }\n if ($team) {\n $teams[] = $team;\n }\n } else {\n // Exclude HubSpot teams - handled by crm:sync-hubspot-objects\n $teams = Team::where('status', Team::STATUS_ACTIVE)\n ->whereHas(\n 'crm',\n fn ($q) => $q\n ->where('provider', '!=', Configuration::PROVIDER_HUBSPOT)\n ->where('sync_objects', true)\n )\n ->get();\n }\n\n $this->dispatchSyncJobsForTeams($teams);\n }\n\n protected function getStaggerDelaySeconds(): float\n {\n return 2;\n }\n\n protected function getMaxDelaySeconds(): int\n {\n // Cap delay at 15 minutes (SQS max delay limit)\n // capacity of 450 teams before reach\n return 900;\n }\n\n protected function getLogPrefix(): string\n {\n return '';\n }\n\n protected function createSyncJob(Team $team): Job\n {\n return new SyncObjectsJob($team);\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Console\\Commands\\Crm;\n\nuse Jiminny\\Console\\Commands\\Command;\nuse Jiminny\\Console\\Commands\\Crm\\Traits\\SyncObjectsCommandTrait;\nuse Jiminny\\Jobs\\Crm\\SyncObjects as SyncObjectsJob;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Models\\Team;\n\nclass SyncObjects extends Command\n{\n use SyncObjectsCommandTrait;\n\n /**\n * The name and signature of the console command.\n *\n * @var string\n */\n protected $signature = 'crm:sync-objects {team?} {--sync}';\n\n /**\n * The console command description.\n *\n * @var string\n */\n protected $description = 'Sync remote CRM objects.';\n\n /**\n * Execute the console command.\n */\n public function handle(): void\n {\n $teamId = $this->argument('team');\n $teams = [];\n\n if ($teamId) {\n $team = Team::idOrUuId($teamId);\n if ($team && $team->crm?->provider === Configuration::PROVIDER_HUBSPOT) {\n $this->error(sprintf(\n 'Team %s (%s) uses HubSpot. Use crm:sync-hubspot-objects instead.',\n $team->getName(),\n $team->getUuid()\n ));\n\n return;\n }\n if ($team) {\n $teams[] = $team;\n }\n } else {\n // Exclude HubSpot teams - handled by crm:sync-hubspot-objects\n $teams = Team::where('status', Team::STATUS_ACTIVE)\n ->whereHas(\n 'crm',\n fn ($q) => $q\n ->where('provider', '!=', Configuration::PROVIDER_HUBSPOT)\n ->where('sync_objects', true)\n )\n ->get();\n }\n\n $this->dispatchSyncJobsForTeams($teams);\n }\n\n protected function getStaggerDelaySeconds(): float\n {\n return 2;\n }\n\n protected function getMaxDelaySeconds(): int\n {\n // Cap delay at 15 minutes (SQS max delay limit)\n // capacity of 450 teams before reach\n return 900;\n }\n\n protected function getLogPrefix(): string\n {\n return '';\n }\n\n protected function createSyncJob(Team $team): Job\n {\n return new SyncObjectsJob($team);\n }\n}","role_description":"text entry area","is_enabled":true,"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":"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":"AXStaticText","text":"1","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"51","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"1","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"on_screen":true,"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\\Console\\Commands\\Crm;\n\nuse Carbon\\Carbon;\nuse Exception;\nuse Illuminate\\Console\\Command;\nuse Jiminny\\Component\\Encryption\\EncryptedTokenManager;\nuse Jiminny\\Models\\SocialAccount;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Pipedrive\\versions\\v1\\Api\\ActivitiesApi;\nuse Pipedrive\\versions\\v1\\Api\\ActivityFieldsApi;\nuse Pipedrive\\versions\\v1\\Api\\DealFieldsApi;\nuse Pipedrive\\versions\\v1\\Api\\DealsApi;\nuse Pipedrive\\versions\\v1\\Api\\PersonFieldsApi;\nuse Pipedrive\\versions\\v1\\Api\\PersonsApi;\nuse Pipedrive\\versions\\v1\\Configuration;\n\nclass TestPipedriveOfficialSdkCommand extends Command\n{\n protected $signature = 'jiminny:test-pipedrive-official-sdk {teamId}';\n\n protected $description = 'Test official Pipedrive SDK (pipedrive/pipedrive) with existing OAuth tokens';\n\n private array $results = [];\n\n private EncryptedTokenManager $tokenManager;\n\n public function __construct(EncryptedTokenManager $tokenManager)\n {\n parent::__construct();\n $this->tokenManager = $tokenManager;\n }\n\n public function handle(): int\n {\n $teamId = (int) $this->argument('teamId');\n\n $this->info(\"Starting Pipedrive Official SDK POC for Team ID: {$teamId}\");\n $this->newLine();\n\n try {\n $this->runTests($teamId);\n } catch (Exception $e) {\n $this->error(\"POC failed with error: {$e->getMessage()}\");\n $this->error($e->getTraceAsString());\n\n return 1;\n }\n\n $this->displayResults();\n\n return 0;\n }\n\n private function runTests(int $teamId): void\n {\n $team = Team::find($teamId);\n if (! $team) {\n throw new Exception(\"Team with ID {$teamId} not found\");\n }\n\n $socialAccount = SocialAccount::where('provider', SocialAccount::PROVIDER_PIPEDRIVE)\n ->whereHas('sociable', function ($query) use ($team) {\n $query->where('team_id', $team->id);\n })\n ->orderByDesc('expires') // Get the most recently expiring token\n ->first();\n\n if (! $socialAccount) {\n throw new Exception(\"No Pipedrive social account found for team {$teamId}\");\n }\n\n $this->info(\"Found Pipedrive account for team: {$team->name}\");\n $this->newLine();\n\n // Test 1: OAuth Initialization\n $this->testOAuthInitialization($socialAccount);\n\n // Test 2: Basic CRUD Operations\n $this->testBasicCrudOperations($socialAccount);\n\n // Test 3: Field Metadata\n $this->testFieldMetadata($socialAccount);\n\n // Test 4: v2 API Support\n $this->testV2ApiSupport($socialAccount);\n\n // Test 5: Error Handling\n $this->testErrorHandling($socialAccount);\n }\n\n private function testOAuthInitialization(SocialAccount $socialAccount): void\n {\n $this->info('Test 1: OAuth Initialization');\n $startTime = microtime(true);\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n\n $client = new DealsApi(null, $config);\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('OAuth Initialization', true, \"Client initialized successfully in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('OAuth Initialization', false, $e->getMessage(), $elapsed);\n }\n\n $this->newLine();\n }\n\n private function testBasicCrudOperations(SocialAccount $socialAccount): void\n {\n $this->info('Test 2: Basic CRUD Operations');\n\n try {\n // Get the raw token from database\n $rawDbToken = $socialAccount->getProviderUserToken();\n $this->info(\"DB token (length: \" . strlen($rawDbToken) . \")\");\n $this->info(\"DB token preview: \" . substr($rawDbToken, 0, 20) . \"...\");\n\n // Check token expiry\n $expires = $socialAccount->expires;\n $this->info(\"Token expires at: \" . ($expires ? Carbon::createFromTimestamp($expires)->toDateTimeString() : 'null'));\n $this->info(\"Token is expired: \" . ($expires && $expires < time() ? 'YES' : 'NO'));\n\n // Test with the DB token directly\n $this->info(\"\\n--- Testing with DB token ---\");\n $config = new Configuration();\n $config->setAccessToken($rawDbToken);\n\n // Test 2a: Get Deals\n $this->testGetDeals($config);\n\n // Test 2b: Get Persons\n $this->testGetPersons($config);\n\n // Test 2c: Get Activities\n $this->testGetActivities($config);\n } catch (Exception $e) {\n $this->recordResult('Basic CRUD Operations', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testCurrentSystemApproach(SocialAccount $socialAccount): void\n {\n $startTime = microtime(true);\n\n try {\n // Simulate how current system makes requests\n $token = $socialAccount->getProviderUserToken();\n $url = 'https://api.pipedrive.com/api/v2/deals';\n\n $headers = [\n 'Authorization' => 'Bearer ' . $token,\n 'Accept' => 'application/json',\n ];\n\n $client = new \\GuzzleHttp\\Client();\n $response = $client->get($url, [\n 'headers' => $headers,\n 'timeout' => 30,\n ]);\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $statusCode = $response->getStatusCode();\n $this->recordResult('Current System v2 API', $statusCode === 200, \"Status: {$statusCode} in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Current System v2 API', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetDeals(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new DealsApi(null, $config);\n // Official SDK uses getDealsSummary instead of getDeals\n $result = $apiInstance->getDealsSummary();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Deals Summary', true, \"Retrieved deals summary in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Deals Summary', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetPersons(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n // Official SDK doesn't have a simple getPersons method\n // Skip this test for now as the API structure is different\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Persons', false, \"Method not available in official SDK - API structure different\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Persons', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetActivities(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n // Official SDK doesn't have a simple getActivities method\n // Skip this test for now as the API structure is different\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Activities', false, \"Method not available in official SDK - API structure different\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Activities', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testFieldMetadata(SocialAccount $socialAccount): void\n {\n $this->info('Test 3: Field Metadata');\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n\n // Test 3a: Get Deal Fields\n $this->testGetDealFields($config);\n\n // Test 3b: Get Person Fields\n $this->testGetPersonFields($config);\n\n // Test 3c: Get Activity Fields\n $this->testGetActivityFields($config);\n } catch (Exception $e) {\n $this->recordResult('Field Metadata', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testGetDealFields(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new DealFieldsApi(null, $config);\n $result = $apiInstance->getDealFields();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $count = $result->getData() ? count($result->getData()) : 0;\n $this->recordResult('Get Deal Fields', true, \"Retrieved {$count} deal fields in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Deal Fields', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetPersonFields(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new PersonFieldsApi(null, $config);\n $result = $apiInstance->getPersonFields();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $count = $result->getData() ? count($result->getData()) : 0;\n $this->recordResult('Get Person Fields', true, \"Retrieved {$count} person fields in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Person Fields', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetActivityFields(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new ActivityFieldsApi(null, $config);\n $result = $apiInstance->getActivityFields();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $count = $result->getData() ? count($result->getData()) : 0;\n $this->recordResult('Get Activity Fields', true, \"Retrieved {$count} activity fields in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Activity Fields', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testV2ApiSupport(SocialAccount $socialAccount): void\n {\n $this->info('Test 4: v2 API Support (Cursor-based Pagination)');\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n\n $startTime = microtime(true);\n\n // Test v2 deals endpoint with cursor pagination\n // Note: The official SDK may or may not have v2 API support\n // This test will determine if v2 is available\n try {\n $apiInstance = new DealsApi(null, $config);\n $updatedSince = Carbon::now()->subDays(30)->toRfc3339String();\n $result = $apiInstance->getDealsSummary();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n\n // Check if response has cursor information (v2 indicator)\n // The response object doesn't have getAdditionalData method\n // Just test if the call works - v2 API support is limited in official SDK v1\n $this->recordResult('v2 API Support', false, \"v1 API works but v2 cursor pagination not available in official SDK v1. Retrieved summary in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('v2 API Support', false, $e->getMessage(), $elapsed);\n }\n } catch (Exception $e) {\n $this->recordResult('v2 API Support', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testErrorHandling(SocialAccount $socialAccount): void\n {\n $this->info('Test 5: Error Handling');\n\n try {\n // Test 5a: Invalid Token\n $this->testInvalidToken();\n\n // Test 5b: Non-existent Resource\n $this->testNonExistentResource($socialAccount);\n } catch (Exception $e) {\n $this->recordResult('Error Handling', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testInvalidToken(): void\n {\n $startTime = microtime(true);\n\n try {\n $config = new Configuration();\n $config->setAccessToken('invalid_token');\n $client = new DealsApi(null, $config);\n\n $client->getDealsSummary();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Invalid Token Error', false, \"Expected error but request succeeded in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Invalid Token Error', true, \"Correctly threw exception: {$e->getMessage()} in {$elapsed}ms\", $elapsed);\n }\n }\n\n private function testNonExistentResource(SocialAccount $socialAccount): void\n {\n $startTime = microtime(true);\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n $client = new DealsApi(null, $config);\n\n // Try to get a deal with a very high ID that likely doesn't exist\n // Note: Official SDK doesn't have a simple getDeal method, so skip this\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Non-existent Resource Error', false, \"Method not available in official SDK - API structure different\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Non-existent Resource Error', true, \"Correctly threw exception: {$e->getMessage()} in {$elapsed}ms\", $elapsed);\n }\n }\n\n private function recordResult(string $testName, bool $success, string $message, float $elapsed): void\n {\n $this->results[] = [\n 'test' => $testName,\n 'success' => $success,\n 'message' => $message,\n 'elapsed' => $elapsed,\n ];\n\n $status = $success ? '✅ PASS' : '❌ FAIL';\n $this->line(\" {$status} - {$testName}: {$message}\");\n }\n\n private function displayResults(): void\n {\n $this->newLine();\n $this->info('=== POC Test Results Summary ===');\n $this->newLine();\n\n $total = count($this->results);\n $passed = count(array_filter($this->results, fn ($r) => $r['success']));\n $failed = $total - $passed;\n\n $this->line(\"Total Tests: {$total}\");\n $this->line(\"Passed: {$passed}\");\n $this->line(\"Failed: {$failed}\");\n $this->newLine();\n\n $totalTime = array_sum(array_column($this->results, 'elapsed'));\n $this->line(\"Total Execution Time: {$totalTime}ms\");\n $this->newLine();\n\n if ($failed > 0) {\n $this->warn('Failed Tests:');\n foreach ($this->results as $result) {\n if (! $result['success']) {\n $this->line(\" - {$result['test']}: {$result['message']}\");\n }\n }\n $this->newLine();\n }\n\n $this->info('=== Detailed Results ===');\n foreach ($this->results as $result) {\n $status = $result['success'] ? '✅' : '❌';\n $this->line(\"{$status} {$result['test']} ({$result['elapsed']}ms): {$result['message']}\");\n }\n\n $this->newLine();\n $this->info('=== Recommendations ===');\n\n if ($passed === $total) {\n $this->info('✅ All tests passed! The official SDK is viable for migration.');\n $this->info('Next steps: Proceed with full migration planning.');\n } else {\n $this->warn('⚠️ Some tests failed. Review the failures above before proceeding.');\n $this->warn('Consider hybrid approach or investigate alternatives if critical operations fail.');\n }\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Console\\Commands\\Crm;\n\nuse Carbon\\Carbon;\nuse Exception;\nuse Illuminate\\Console\\Command;\nuse Jiminny\\Component\\Encryption\\EncryptedTokenManager;\nuse Jiminny\\Models\\SocialAccount;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Pipedrive\\versions\\v1\\Api\\ActivitiesApi;\nuse Pipedrive\\versions\\v1\\Api\\ActivityFieldsApi;\nuse Pipedrive\\versions\\v1\\Api\\DealFieldsApi;\nuse Pipedrive\\versions\\v1\\Api\\DealsApi;\nuse Pipedrive\\versions\\v1\\Api\\PersonFieldsApi;\nuse Pipedrive\\versions\\v1\\Api\\PersonsApi;\nuse Pipedrive\\versions\\v1\\Configuration;\n\nclass TestPipedriveOfficialSdkCommand extends Command\n{\n protected $signature = 'jiminny:test-pipedrive-official-sdk {teamId}';\n\n protected $description = 'Test official Pipedrive SDK (pipedrive/pipedrive) with existing OAuth tokens';\n\n private array $results = [];\n\n private EncryptedTokenManager $tokenManager;\n\n public function __construct(EncryptedTokenManager $tokenManager)\n {\n parent::__construct();\n $this->tokenManager = $tokenManager;\n }\n\n public function handle(): int\n {\n $teamId = (int) $this->argument('teamId');\n\n $this->info(\"Starting Pipedrive Official SDK POC for Team ID: {$teamId}\");\n $this->newLine();\n\n try {\n $this->runTests($teamId);\n } catch (Exception $e) {\n $this->error(\"POC failed with error: {$e->getMessage()}\");\n $this->error($e->getTraceAsString());\n\n return 1;\n }\n\n $this->displayResults();\n\n return 0;\n }\n\n private function runTests(int $teamId): void\n {\n $team = Team::find($teamId);\n if (! $team) {\n throw new Exception(\"Team with ID {$teamId} not found\");\n }\n\n $socialAccount = SocialAccount::where('provider', SocialAccount::PROVIDER_PIPEDRIVE)\n ->whereHas('sociable', function ($query) use ($team) {\n $query->where('team_id', $team->id);\n })\n ->orderByDesc('expires') // Get the most recently expiring token\n ->first();\n\n if (! $socialAccount) {\n throw new Exception(\"No Pipedrive social account found for team {$teamId}\");\n }\n\n $this->info(\"Found Pipedrive account for team: {$team->name}\");\n $this->newLine();\n\n // Test 1: OAuth Initialization\n $this->testOAuthInitialization($socialAccount);\n\n // Test 2: Basic CRUD Operations\n $this->testBasicCrudOperations($socialAccount);\n\n // Test 3: Field Metadata\n $this->testFieldMetadata($socialAccount);\n\n // Test 4: v2 API Support\n $this->testV2ApiSupport($socialAccount);\n\n // Test 5: Error Handling\n $this->testErrorHandling($socialAccount);\n }\n\n private function testOAuthInitialization(SocialAccount $socialAccount): void\n {\n $this->info('Test 1: OAuth Initialization');\n $startTime = microtime(true);\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n\n $client = new DealsApi(null, $config);\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('OAuth Initialization', true, \"Client initialized successfully in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('OAuth Initialization', false, $e->getMessage(), $elapsed);\n }\n\n $this->newLine();\n }\n\n private function testBasicCrudOperations(SocialAccount $socialAccount): void\n {\n $this->info('Test 2: Basic CRUD Operations');\n\n try {\n // Get the raw token from database\n $rawDbToken = $socialAccount->getProviderUserToken();\n $this->info(\"DB token (length: \" . strlen($rawDbToken) . \")\");\n $this->info(\"DB token preview: \" . substr($rawDbToken, 0, 20) . \"...\");\n\n // Check token expiry\n $expires = $socialAccount->expires;\n $this->info(\"Token expires at: \" . ($expires ? Carbon::createFromTimestamp($expires)->toDateTimeString() : 'null'));\n $this->info(\"Token is expired: \" . ($expires && $expires < time() ? 'YES' : 'NO'));\n\n // Test with the DB token directly\n $this->info(\"\\n--- Testing with DB token ---\");\n $config = new Configuration();\n $config->setAccessToken($rawDbToken);\n\n // Test 2a: Get Deals\n $this->testGetDeals($config);\n\n // Test 2b: Get Persons\n $this->testGetPersons($config);\n\n // Test 2c: Get Activities\n $this->testGetActivities($config);\n } catch (Exception $e) {\n $this->recordResult('Basic CRUD Operations', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testCurrentSystemApproach(SocialAccount $socialAccount): void\n {\n $startTime = microtime(true);\n\n try {\n // Simulate how current system makes requests\n $token = $socialAccount->getProviderUserToken();\n $url = 'https://api.pipedrive.com/api/v2/deals';\n\n $headers = [\n 'Authorization' => 'Bearer ' . $token,\n 'Accept' => 'application/json',\n ];\n\n $client = new \\GuzzleHttp\\Client();\n $response = $client->get($url, [\n 'headers' => $headers,\n 'timeout' => 30,\n ]);\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $statusCode = $response->getStatusCode();\n $this->recordResult('Current System v2 API', $statusCode === 200, \"Status: {$statusCode} in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Current System v2 API', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetDeals(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new DealsApi(null, $config);\n // Official SDK uses getDealsSummary instead of getDeals\n $result = $apiInstance->getDealsSummary();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Deals Summary', true, \"Retrieved deals summary in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Deals Summary', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetPersons(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n // Official SDK doesn't have a simple getPersons method\n // Skip this test for now as the API structure is different\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Persons', false, \"Method not available in official SDK - API structure different\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Persons', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetActivities(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n // Official SDK doesn't have a simple getActivities method\n // Skip this test for now as the API structure is different\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Activities', false, \"Method not available in official SDK - API structure different\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Activities', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testFieldMetadata(SocialAccount $socialAccount): void\n {\n $this->info('Test 3: Field Metadata');\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n\n // Test 3a: Get Deal Fields\n $this->testGetDealFields($config);\n\n // Test 3b: Get Person Fields\n $this->testGetPersonFields($config);\n\n // Test 3c: Get Activity Fields\n $this->testGetActivityFields($config);\n } catch (Exception $e) {\n $this->recordResult('Field Metadata', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testGetDealFields(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new DealFieldsApi(null, $config);\n $result = $apiInstance->getDealFields();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $count = $result->getData() ? count($result->getData()) : 0;\n $this->recordResult('Get Deal Fields', true, \"Retrieved {$count} deal fields in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Deal Fields', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetPersonFields(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new PersonFieldsApi(null, $config);\n $result = $apiInstance->getPersonFields();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $count = $result->getData() ? count($result->getData()) : 0;\n $this->recordResult('Get Person Fields', true, \"Retrieved {$count} person fields in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Person Fields', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetActivityFields(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new ActivityFieldsApi(null, $config);\n $result = $apiInstance->getActivityFields();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $count = $result->getData() ? count($result->getData()) : 0;\n $this->recordResult('Get Activity Fields', true, \"Retrieved {$count} activity fields in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Activity Fields', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testV2ApiSupport(SocialAccount $socialAccount): void\n {\n $this->info('Test 4: v2 API Support (Cursor-based Pagination)');\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n\n $startTime = microtime(true);\n\n // Test v2 deals endpoint with cursor pagination\n // Note: The official SDK may or may not have v2 API support\n // This test will determine if v2 is available\n try {\n $apiInstance = new DealsApi(null, $config);\n $updatedSince = Carbon::now()->subDays(30)->toRfc3339String();\n $result = $apiInstance->getDealsSummary();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n\n // Check if response has cursor information (v2 indicator)\n // The response object doesn't have getAdditionalData method\n // Just test if the call works - v2 API support is limited in official SDK v1\n $this->recordResult('v2 API Support', false, \"v1 API works but v2 cursor pagination not available in official SDK v1. Retrieved summary in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('v2 API Support', false, $e->getMessage(), $elapsed);\n }\n } catch (Exception $e) {\n $this->recordResult('v2 API Support', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testErrorHandling(SocialAccount $socialAccount): void\n {\n $this->info('Test 5: Error Handling');\n\n try {\n // Test 5a: Invalid Token\n $this->testInvalidToken();\n\n // Test 5b: Non-existent Resource\n $this->testNonExistentResource($socialAccount);\n } catch (Exception $e) {\n $this->recordResult('Error Handling', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testInvalidToken(): void\n {\n $startTime = microtime(true);\n\n try {\n $config = new Configuration();\n $config->setAccessToken('invalid_token');\n $client = new DealsApi(null, $config);\n\n $client->getDealsSummary();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Invalid Token Error', false, \"Expected error but request succeeded in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Invalid Token Error', true, \"Correctly threw exception: {$e->getMessage()} in {$elapsed}ms\", $elapsed);\n }\n }\n\n private function testNonExistentResource(SocialAccount $socialAccount): void\n {\n $startTime = microtime(true);\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n $client = new DealsApi(null, $config);\n\n // Try to get a deal with a very high ID that likely doesn't exist\n // Note: Official SDK doesn't have a simple getDeal method, so skip this\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Non-existent Resource Error', false, \"Method not available in official SDK - API structure different\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Non-existent Resource Error', true, \"Correctly threw exception: {$e->getMessage()} in {$elapsed}ms\", $elapsed);\n }\n }\n\n private function recordResult(string $testName, bool $success, string $message, float $elapsed): void\n {\n $this->results[] = [\n 'test' => $testName,\n 'success' => $success,\n 'message' => $message,\n 'elapsed' => $elapsed,\n ];\n\n $status = $success ? '✅ PASS' : '❌ FAIL';\n $this->line(\" {$status} - {$testName}: {$message}\");\n }\n\n private function displayResults(): void\n {\n $this->newLine();\n $this->info('=== POC Test Results Summary ===');\n $this->newLine();\n\n $total = count($this->results);\n $passed = count(array_filter($this->results, fn ($r) => $r['success']));\n $failed = $total - $passed;\n\n $this->line(\"Total Tests: {$total}\");\n $this->line(\"Passed: {$passed}\");\n $this->line(\"Failed: {$failed}\");\n $this->newLine();\n\n $totalTime = array_sum(array_column($this->results, 'elapsed'));\n $this->line(\"Total Execution Time: {$totalTime}ms\");\n $this->newLine();\n\n if ($failed > 0) {\n $this->warn('Failed Tests:');\n foreach ($this->results as $result) {\n if (! $result['success']) {\n $this->line(\" - {$result['test']}: {$result['message']}\");\n }\n }\n $this->newLine();\n }\n\n $this->info('=== Detailed Results ===');\n foreach ($this->results as $result) {\n $status = $result['success'] ? '✅' : '❌';\n $this->line(\"{$status} {$result['test']} ({$result['elapsed']}ms): {$result['message']}\");\n }\n\n $this->newLine();\n $this->info('=== Recommendations ===');\n\n if ($passed === $total) {\n $this->info('✅ All tests passed! The official SDK is viable for migration.');\n $this->info('Next steps: Proceed with full migration planning.');\n } else {\n $this->warn('⚠️ Some tests failed. Review the failures above before proceeding.');\n $this->warn('Consider hybrid approach or investigate alternatives if critical operations fail.');\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}]...
|
5365831007722038384
|
61370652661533572
|
visual_change
|
accessibility
|
NULL
|
Project: faVsco.js, menu
pipedrive-sdk-poc, menu
S Project: faVsco.js, menu
pipedrive-sdk-poc, 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\Console\Commands\Crm;
use Jiminny\Console\Commands\Command;
use Jiminny\Console\Commands\Crm\Traits\SyncObjectsCommandTrait;
use Jiminny\Jobs\Crm\SyncObjects as SyncObjectsJob;
use Jiminny\Jobs\Job;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Models\Team;
class SyncObjects extends Command
{
use SyncObjectsCommandTrait;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'crm:sync-objects {team?} {--sync}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Sync remote CRM objects.';
/**
* Execute the console command.
*/
public function handle(): void
{
$teamId = $this->argument('team');
$teams = [];
if ($teamId) {
$team = Team::idOrUuId($teamId);
if ($team && $team->crm?->provider === Configuration::PROVIDER_HUBSPOT) {
$this->error(sprintf(
'Team %s (%s) uses HubSpot. Use crm:sync-hubspot-objects instead.',
$team->getName(),
$team->getUuid()
));
return;
}
if ($team) {
$teams[] = $team;
}
} else {
// Exclude HubSpot teams - handled by crm:sync-hubspot-objects
$teams = Team::where('status', Team::STATUS_ACTIVE)
->whereHas(
'crm',
fn ($q) => $q
->where('provider', '!=', Configuration::PROVIDER_HUBSPOT)
->where('sync_objects', true)
)
->get();
}
$this->dispatchSyncJobsForTeams($teams);
}
protected function getStaggerDelaySeconds(): float
{
return 2;
}
protected function getMaxDelaySeconds(): int
{
// Cap delay at 15 minutes (SQS max delay limit)
// capacity of 450 teams before reach
return 900;
}
protected function getLogPrefix(): string
{
return '';
}
protected function createSyncJob(Team $team): Job
{
return new SyncObjectsJob($team);
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
1
51
1
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Console\Commands\Crm;
use Carbon\Carbon;
use Exception;
use Illuminate\Console\Command;
use Jiminny\Component\Encryption\EncryptedTokenManager;
use Jiminny\Models\SocialAccount;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Pipedrive\versions\v1\Api\ActivitiesApi;
use Pipedrive\versions\v1\Api\ActivityFieldsApi;
use Pipedrive\versions\v1\Api\DealFieldsApi;
use Pipedrive\versions\v1\Api\DealsApi;
use Pipedrive\versions\v1\Api\PersonFieldsApi;
use Pipedrive\versions\v1\Api\PersonsApi;
use Pipedrive\versions\v1\Configuration;
class TestPipedriveOfficialSdkCommand extends Command
{
protected $signature = 'jiminny:test-pipedrive-official-sdk {teamId}';
protected $description = 'Test official Pipedrive SDK (pipedrive/pipedrive) with existing OAuth tokens';
private array $results = [];
private EncryptedTokenManager $tokenManager;
public function __construct(EncryptedTokenManager $tokenManager)
{
parent::__construct();
$this->tokenManager = $tokenManager;
}
public function handle(): int
{
$teamId = (int) $this->argument('teamId');
$this->info("Starting Pipedrive Official SDK POC for Team ID: {$teamId}");
$this->newLine();
try {
$this->runTests($teamId);
} catch (Exception $e) {
$this->error("POC failed with error: {$e->getMessage()}");
$this->error($e->getTraceAsString());
return 1;
}
$this->displayResults();
return 0;
}
private function runTests(int $teamId): void
{
$team = Team::find($teamId);
if (! $team) {
throw new Exception("Team with ID {$teamId} not found");
}
$socialAccount = SocialAccount::where('provider', SocialAccount::PROVIDER_PIPEDRIVE)
->whereHas('sociable', function ($query) use ($team) {
$query->where('team_id', $team->id);
})
->orderByDesc('expires') // Get the most recently expiring token
->first();
if (! $socialAccount) {
throw new Exception("No Pipedrive social account found for team {$teamId}");
}
$this->info("Found Pipedrive account for team: {$team->name}");
$this->newLine();
// Test 1: OAuth Initialization
$this->testOAuthInitialization($socialAccount);
// Test 2: Basic CRUD Operations
$this->testBasicCrudOperations($socialAccount);
// Test 3: Field Metadata
$this->testFieldMetadata($socialAccount);
// Test 4: v2 API Support
$this->testV2ApiSupport($socialAccount);
// Test 5: Error Handling
$this->testErrorHandling($socialAccount);
}
private function testOAuthInitialization(SocialAccount $socialAccount): void
{
$this->info('Test 1: OAuth Initialization');
$startTime = microtime(true);
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
$client = new DealsApi(null, $config);
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('OAuth Initialization', true, "Client initialized successfully in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('OAuth Initialization', false, $e->getMessage(), $elapsed);
}
$this->newLine();
}
private function testBasicCrudOperations(SocialAccount $socialAccount): void
{
$this->info('Test 2: Basic CRUD Operations');
try {
// Get the raw token from database
$rawDbToken = $socialAccount->getProviderUserToken();
$this->info("DB token (length: " . strlen($rawDbToken) . ")");
$this->info("DB token preview: " . substr($rawDbToken, 0, 20) . "...");
// Check token expiry
$expires = $socialAccount->expires;
$this->info("Token expires at: " . ($expires ? Carbon::createFromTimestamp($expires)->toDateTimeString() : 'null'));
$this->info("Token is expired: " . ($expires && $expires < time() ? 'YES' : 'NO'));
// Test with the DB token directly
$this->info("\n--- Testing with DB token ---");
$config = new Configuration();
$config->setAccessToken($rawDbToken);
// Test 2a: Get Deals
$this->testGetDeals($config);
// Test 2b: Get Persons
$this->testGetPersons($config);
// Test 2c: Get Activities
$this->testGetActivities($config);
} catch (Exception $e) {
$this->recordResult('Basic CRUD Operations', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testCurrentSystemApproach(SocialAccount $socialAccount): void
{
$startTime = microtime(true);
try {
// Simulate how current system makes requests
$token = $socialAccount->getProviderUserToken();
$url = 'https://api.pipedrive.com/api/v2/deals';
$headers = [
'Authorization' => 'Bearer ' . $token,
'Accept' => 'application/json',
];
$client = new \GuzzleHttp\Client();
$response = $client->get($url, [
'headers' => $headers,
'timeout' => 30,
]);
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$statusCode = $response->getStatusCode();
$this->recordResult('Current System v2 API', $statusCode === 200, "Status: {$statusCode} in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Current System v2 API', false, $e->getMessage(), $elapsed);
}
}
private function testGetDeals(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new DealsApi(null, $config);
// Official SDK uses getDealsSummary instead of getDeals
$result = $apiInstance->getDealsSummary();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Deals Summary', true, "Retrieved deals summary in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Deals Summary', false, $e->getMessage(), $elapsed);
}
}
private function testGetPersons(Configuration $config): void
{
$startTime = microtime(true);
try {
// Official SDK doesn't have a simple getPersons method
// Skip this test for now as the API structure is different
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Persons', false, "Method not available in official SDK - API structure different", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Persons', false, $e->getMessage(), $elapsed);
}
}
private function testGetActivities(Configuration $config): void
{
$startTime = microtime(true);
try {
// Official SDK doesn't have a simple getActivities method
// Skip this test for now as the API structure is different
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Activities', false, "Method not available in official SDK - API structure different", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Activities', false, $e->getMessage(), $elapsed);
}
}
private function testFieldMetadata(SocialAccount $socialAccount): void
{
$this->info('Test 3: Field Metadata');
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
// Test 3a: Get Deal Fields
$this->testGetDealFields($config);
// Test 3b: Get Person Fields
$this->testGetPersonFields($config);
// Test 3c: Get Activity Fields
$this->testGetActivityFields($config);
} catch (Exception $e) {
$this->recordResult('Field Metadata', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testGetDealFields(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new DealFieldsApi(null, $config);
$result = $apiInstance->getDealFields();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$count = $result->getData() ? count($result->getData()) : 0;
$this->recordResult('Get Deal Fields', true, "Retrieved {$count} deal fields in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Deal Fields', false, $e->getMessage(), $elapsed);
}
}
private function testGetPersonFields(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new PersonFieldsApi(null, $config);
$result = $apiInstance->getPersonFields();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$count = $result->getData() ? count($result->getData()) : 0;
$this->recordResult('Get Person Fields', true, "Retrieved {$count} person fields in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Person Fields', false, $e->getMessage(), $elapsed);
}
}
private function testGetActivityFields(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new ActivityFieldsApi(null, $config);
$result = $apiInstance->getActivityFields();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$count = $result->getData() ? count($result->getData()) : 0;
$this->recordResult('Get Activity Fields', true, "Retrieved {$count} activity fields in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Activity Fields', false, $e->getMessage(), $elapsed);
}
}
private function testV2ApiSupport(SocialAccount $socialAccount): void
{
$this->info('Test 4: v2 API Support (Cursor-based Pagination)');
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
$startTime = microtime(true);
// Test v2 deals endpoint with cursor pagination
// Note: The official SDK may or may not have v2 API support
// This test will determine if v2 is available
try {
$apiInstance = new DealsApi(null, $config);
$updatedSince = Carbon::now()->subDays(30)->toRfc3339String();
$result = $apiInstance->getDealsSummary();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
// Check if response has cursor information (v2 indicator)
// The response object doesn't have getAdditionalData method
// Just test if the call works - v2 API support is limited in official SDK v1
$this->recordResult('v2 API Support', false, "v1 API works but v2 cursor pagination not available in official SDK v1. Retrieved summary in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('v2 API Support', false, $e->getMessage(), $elapsed);
}
} catch (Exception $e) {
$this->recordResult('v2 API Support', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testErrorHandling(SocialAccount $socialAccount): void
{
$this->info('Test 5: Error Handling');
try {
// Test 5a: Invalid Token
$this->testInvalidToken();
// Test 5b: Non-existent Resource
$this->testNonExistentResource($socialAccount);
} catch (Exception $e) {
$this->recordResult('Error Handling', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testInvalidToken(): void
{
$startTime = microtime(true);
try {
$config = new Configuration();
$config->setAccessToken('invalid_token');
$client = new DealsApi(null, $config);
$client->getDealsSummary();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Invalid Token Error', false, "Expected error but request succeeded in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Invalid Token Error', true, "Correctly threw exception: {$e->getMessage()} in {$elapsed}ms", $elapsed);
}
}
private function testNonExistentResource(SocialAccount $socialAccount): void
{
$startTime = microtime(true);
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
$client = new DealsApi(null, $config);
// Try to get a deal with a very high ID that likely doesn't exist
// Note: Official SDK doesn't have a simple getDeal method, so skip this
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Non-existent Resource Error', false, "Method not available in official SDK - API structure different", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Non-existent Resource Error', true, "Correctly threw exception: {$e->getMessage()} in {$elapsed}ms", $elapsed);
}
}
private function recordResult(string $testName, bool $success, string $message, float $elapsed): void
{
$this->results[] = [
'test' => $testName,
'success' => $success,
'message' => $message,
'elapsed' => $elapsed,
];
$status = $success ? '✅ PASS' : '❌ FAIL';
$this->line(" {$status} - {$testName}: {$message}");
}
private function displayResults(): void
{
$this->newLine();
$this->info('=== POC Test Results Summary ===');
$this->newLine();
$total = count($this->results);
$passed = count(array_filter($this->results, fn ($r) => $r['success']));
$failed = $total - $passed;
$this->line("Total Tests: {$total}");
$this->line("Passed: {$passed}");
$this->line("Failed: {$failed}");
$this->newLine();
$totalTime = array_sum(array_column($this->results, 'elapsed'));
$this->line("Total Execution Time: {$totalTime}ms");
$this->newLine();
if ($failed > 0) {
$this->warn('Failed Tests:');
foreach ($this->results as $result) {
if (! $result['success']) {
$this->line(" - {$result['test']}: {$result['message']}");
}
}
$this->newLine();
}
$this->info('=== Detailed Results ===');
foreach ($this->results as $result) {
$status = $result['success'] ? '✅' : '❌';
$this->line("{$status} {$result['test']} ({$result['elapsed']}ms): {$result['message']}");
}
$this->newLine();
$this->info('=== Recommendations ===');
if ($passed === $total) {
$this->info('✅ All tests passed! The official SDK is viable for migration.');
$this->info('Next steps: Proceed with full migration planning.');
} else {
$this->warn('⚠️ Some tests failed. Review the failures above before proceeding.');
$this->warn('Consider hybrid approach or investigate alternatives if critical operations fail.');
}
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
49395
|
NULL
|
NULL
|
NULL
|
|
49395
|
1762
|
20
|
2026-05-18T06:39:45.852201+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779086385852_m1.jpg...
|
PhpStorm
|
faVsco.js – TestPipedriveOfficialSdkCommand.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
pipedrive-sdk-poc, menu
S Project: faVsco.js, menu
pipedrive-sdk-poc, 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...
|
[{"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":"pipedrive-sdk-poc, menu","depth":5,"on_screen":true,"help_text":"Git Branch: pipedrive-sdk-poc","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}]...
|
-8603436982613086966
|
-8708828999581586494
|
click
|
hybrid
|
NULL
|
Project: faVsco.js, menu
pipedrive-sdk-poc, menu
S Project: faVsco.js, menu
pipedrive-sdk-poc, 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
Term2ShellEditSessionProfilesWindowHelpDOCKER (docker-compose)O ₴2DOCKER81DEV (-zsh)11DOCKER (docker-compose)NFO""component" ::"e802ad473a4f""o.e.p.PluginsService","cluster. name" :"docker-cluster""node. name""message" :module [x-pack-ql]"elasticsearch"timestamp":"2026-05-18T06:39:35,926Z"NFO""component": "o.e.p.PluginsService""cluster.name" :"docker-cluster""level": "I"node .name""e802ad473a4f""message":elasticsearch"loaded module [x-pack-rollup]" }"timestamp":NFO""2026-05-18T06:39:35,926Z""component": "o.e.p.PluginsService""node. name""e802ad473a4f""message" :"loaded module [x-pack-security]" }elasticsearch"timestamp" :"2026-05-18T06:39:35,926Z"NFO","component" :"o.e.p.PluginsService""cluster.name":"docker-cluster","node. name": "e802ad473a4f""message":"Loaded module [x-pack-sql]" }elasticsearchNFO",1 {"type":"server""timestamp" :"2026-05-18T06:39:35,926Z""component":"o.e.p.PluginsService","cluster.name":"docker-cluster""level":"I"node. name""e802ad473a4f""message" :"loadedmodule [x-pack-stack]"}elasticsearchI {"type":"server""timestamp""2026-05-18T06:39:35,926Z"NFO""component":"o.e.p.PluginsService"cluster.name":"docker-cluster""level":"I"node. name""e802ad473a4f""message" :"loadedmodule[x-pack-voting-only-node]" }elasticsearch | {"type": "server""timestamp": "2026-05-18T06:39:35,926Z"NFO","component": "o.e.p.PluginsService","cluster.name":"docker-cluster""level":"I"node. name": "e802ad473a4f"', "message": "loaded module [x-pack-watcher]" }elasticsearch1 {"type": "server""timestamp": "2026-05-18T06:39:35,926Z"NFO", "component": "o.e.p.PluginsService", "cluster.name":"docker-cluster""level":"I"node. name": "e802ad473a4f"', "message": "no plugins loaded" 3elasticsearchI {"type": "deprecation", "timestamp": "2026-05-18T06:39:35,995Z", "level": "DEPRECATION",node.name":"component":"o.e.d.c.s.Settings", "cluster.name":"docker-cluster""e802ad473a4f", "message":"[node.data] setting was deprecated in Elasticsearch and will be removed in a future release! See the breakingdocumentation forthe next major version." }elasticsearch1 {"type": "server"NFO""component": "o.e.e.NodeEnvironment",ments, uster-n-m5-18 Tdocker-clusten","Level name"node. name":"e80Zad473a4f", "message": "using [1] data paths, mounts [[/usr/share/elasticsearch/data (/dev/vda1)]], net usable_space [14gb], net total_space [58.3gb], types [ext4]" }elasticsearch1 {"type": "server""timestamp": "2026-05-18T06:39:36,015Z","level": "INFO", "component": "o.e.e.NodeEnvironment""cluster.name": "docker-cluster""node.name": "e802ad473a4f", "message": "heap size [700mb], compressed ordinary object pointers [true]" }elasticsearchNFO",1 {"type": "server"', "timestamp": "2026-05-18T06:39:36,106Z""level": "I"component": "o.e.n.Node""cluster.name": "docker-cluster""node.name": "e802ad473a4f""message": "node name [e802ad473a4f], node ID [e2ZKzgw4Q4aCf2w51jWr1A], clustername [docker-cluster], roles [transform, master, remote_cluster_client, data, ml, data_content, data_hot, data_warm, data_cold, ingest]" }100% <8• Mon 18 May 9:39:45screenpipe"181O &4APP (-zsh)*3Y2PROD (-zsh)Last login: Sat May 16 18:04:33 on ttys001Poetry could not find a pyproject.toml file in /Users/lukas or its parentsPoetry could not find a pyproject.toml file in /Users/lukas or its parentslukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ |X L3 EU (-zsh)Last login: Sat May 16 18:04:33 on ttys001Poetry could not find a pyproject.toml file in /Users/lukas or its parentsPoetry could not find a pyproject.tomlfile in /Users/lukas or its parents@Lukas-Kovaliks-MacBook-Pro-Jiminny~$ IX T4STAGE (-zsh)Last login: Sat May 16 18:04:33 on ttys001Poetry could not find a pyproject.toml file in /Users/lukas or its parentsPoetry could not find a pyproject.toml file in /Users/lukas or its parentslukas@Lukas-Kovaliks-MacBook-Pro-JiminnyT5QA (-zsh)Last login: Mon May 18 09:17:28on ttys003Poetry could not find a pyproject.toml file in /Users/lukas or its parentsPoetry could not find a pyproject.tomlfile in /Users/lukas or its parentsX T6FE (-zsh)Last login: Mon May 18 09:17:28on ttys004Poetry could not find a pyproject.toml file in /Users/lukas or its parentsPoetry couldnotfind a pyproject.tomlfile in /Users/lukas or its parentslukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ IEXT(-zsh)Poetry could not find a pyproject.toml file in /Users/lukas or its parentsPoetry couldnot find a pyproject.tomlfile in /Users/lukas or its parentslukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ [|PRODSTAGEFRONTENDEXTENSIONV View in Docker Desktop• View ConfigEnable Watch...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
49394
|
1763
|
22
|
2026-05-18T06:39:45.879363+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779086385879_m2.jpg...
|
PhpStorm
|
faVsco.js – TestPipedriveOfficialSdkCommand.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
pipedrive-sdk-poc, menu
S Project: faVsco.js, menu
pipedrive-sdk-poc, 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\Console\Commands\Crm;
use Jiminny\Console\Commands\Command;
use Jiminny\Console\Commands\Crm\Traits\SyncObjectsCommandTrait;
use Jiminny\Jobs\Crm\SyncObjects as SyncObjectsJob;
use Jiminny\Jobs\Job;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Models\Team;
class SyncObjects extends Command
{
use SyncObjectsCommandTrait;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'crm:sync-objects {team?} {--sync}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Sync remote CRM objects.';
/**
* Execute the console command.
*/
public function handle(): void
{
$teamId = $this->argument('team');
$teams = [];
if ($teamId) {
$team = Team::idOrUuId($teamId);
if ($team && $team->crm?->provider === Configuration::PROVIDER_HUBSPOT) {
$this->error(sprintf(
'Team %s (%s) uses HubSpot. Use crm:sync-hubspot-objects instead.',
$team->getName(),
$team->getUuid()
));
return;
}
if ($team) {
$teams[] = $team;
}
} else {
// Exclude HubSpot teams - handled by crm:sync-hubspot-objects
$teams = Team::where('status', Team::STATUS_ACTIVE)
->whereHas(
'crm',
fn ($q) => $q
->where('provider', '!=', Configuration::PROVIDER_HUBSPOT)
->where('sync_objects', true)
)
->get();
}
$this->dispatchSyncJobsForTeams($teams);
}
protected function getStaggerDelaySeconds(): float
{
return 2;
}
protected function getMaxDelaySeconds(): int
{
// Cap delay at 15 minutes (SQS max delay limit)
// capacity of 450 teams before reach
return 900;
}
protected function getLogPrefix(): string
{
return '';
}
protected function createSyncJob(Team $team): Job
{
return new SyncObjectsJob($team);
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
1
51
1
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Console\Commands\Crm;
use Carbon\Carbon;
use Exception;
use Illuminate\Console\Command;
use Jiminny\Component\Encryption\EncryptedTokenManager;
use Jiminny\Models\SocialAccount;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Pipedrive\versions\v1\Api\ActivitiesApi;
use Pipedrive\versions\v1\Api\ActivityFieldsApi;
use Pipedrive\versions\v1\Api\DealFieldsApi;
use Pipedrive\versions\v1\Api\DealsApi;
use Pipedrive\versions\v1\Api\PersonFieldsApi;
use Pipedrive\versions\v1\Api\PersonsApi;
use Pipedrive\versions\v1\Configuration;
class TestPipedriveOfficialSdkCommand extends Command
{
protected $signature = 'jiminny:test-pipedrive-official-sdk {teamId}';
protected $description = 'Test official Pipedrive SDK (pipedrive/pipedrive) with existing OAuth tokens';
private array $results = [];
private EncryptedTokenManager $tokenManager;
public function __construct(EncryptedTokenManager $tokenManager)
{
parent::__construct();
$this->tokenManager = $tokenManager;
}
public function handle(): int
{
$teamId = (int) $this->argument('teamId');
$this->info("Starting Pipedrive Official SDK POC for Team ID: {$teamId}");
$this->newLine();
try {
$this->runTests($teamId);
} catch (Exception $e) {
$this->error("POC failed with error: {$e->getMessage()}");
$this->error($e->getTraceAsString());
return 1;
}
$this->displayResults();
return 0;
}
private function runTests(int $teamId): void
{
$team = Team::find($teamId);
if (! $team) {
throw new Exception("Team with ID {$teamId} not found");
}
$socialAccount = SocialAccount::where('provider', SocialAccount::PROVIDER_PIPEDRIVE)
->whereHas('sociable', function ($query) use ($team) {
$query->where('team_id', $team->id);
})
->orderByDesc('expires') // Get the most recently expiring token
->first();
if (! $socialAccount) {
throw new Exception("No Pipedrive social account found for team {$teamId}");
}
$this->info("Found Pipedrive account for team: {$team->name}");
$this->newLine();
// Test 1: OAuth Initialization
$this->testOAuthInitialization($socialAccount);
// Test 2: Basic CRUD Operations
$this->testBasicCrudOperations($socialAccount);
// Test 3: Field Metadata
$this->testFieldMetadata($socialAccount);
// Test 4: v2 API Support
$this->testV2ApiSupport($socialAccount);
// Test 5: Error Handling
$this->testErrorHandling($socialAccount);
}
private function testOAuthInitialization(SocialAccount $socialAccount): void
{
$this->info('Test 1: OAuth Initialization');
$startTime = microtime(true);
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
$client = new DealsApi(null, $config);
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('OAuth Initialization', true, "Client initialized successfully in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('OAuth Initialization', false, $e->getMessage(), $elapsed);
}
$this->newLine();
}
private function testBasicCrudOperations(SocialAccount $socialAccount): void
{
$this->info('Test 2: Basic CRUD Operations');
try {
// Get the raw token from database
$rawDbToken = $socialAccount->getProviderUserToken();
$this->info("DB token (length: " . strlen($rawDbToken) . ")");
$this->info("DB token preview: " . substr($rawDbToken, 0, 20) . "...");
// Check token expiry
$expires = $socialAccount->expires;
$this->info("Token expires at: " . ($expires ? Carbon::createFromTimestamp($expires)->toDateTimeString() : 'null'));
$this->info("Token is expired: " . ($expires && $expires < time() ? 'YES' : 'NO'));
// Test with the DB token directly
$this->info("\n--- Testing with DB token ---");
$config = new Configuration();
$config->setAccessToken($rawDbToken);
// Test 2a: Get Deals
$this->testGetDeals($config);
// Test 2b: Get Persons
$this->testGetPersons($config);
// Test 2c: Get Activities
$this->testGetActivities($config);
} catch (Exception $e) {
$this->recordResult('Basic CRUD Operations', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testCurrentSystemApproach(SocialAccount $socialAccount): void
{
$startTime = microtime(true);
try {
// Simulate how current system makes requests
$token = $socialAccount->getProviderUserToken();
$url = 'https://api.pipedrive.com/api/v2/deals';
$headers = [
'Authorization' => 'Bearer ' . $token,
'Accept' => 'application/json',
];
$client = new \GuzzleHttp\Client();
$response = $client->get($url, [
'headers' => $headers,
'timeout' => 30,
]);
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$statusCode = $response->getStatusCode();
$this->recordResult('Current System v2 API', $statusCode === 200, "Status: {$statusCode} in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Current System v2 API', false, $e->getMessage(), $elapsed);
}
}
private function testGetDeals(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new DealsApi(null, $config);
// Official SDK uses getDealsSummary instead of getDeals
$result = $apiInstance->getDealsSummary();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Deals Summary', true, "Retrieved deals summary in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Deals Summary', false, $e->getMessage(), $elapsed);
}
}
private function testGetPersons(Configuration $config): void
{
$startTime = microtime(true);
try {
// Official SDK doesn't have a simple getPersons method
// Skip this test for now as the API structure is different
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Persons', false, "Method not available in official SDK - API structure different", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Persons', false, $e->getMessage(), $elapsed);
}
}
private function testGetActivities(Configuration $config): void
{
$startTime = microtime(true);
try {
// Official SDK doesn't have a simple getActivities method
// Skip this test for now as the API structure is different
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Activities', false, "Method not available in official SDK - API structure different", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Activities', false, $e->getMessage(), $elapsed);
}
}
private function testFieldMetadata(SocialAccount $socialAccount): void
{
$this->info('Test 3: Field Metadata');
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
// Test 3a: Get Deal Fields
$this->testGetDealFields($config);
// Test 3b: Get Person Fields
$this->testGetPersonFields($config);
// Test 3c: Get Activity Fields
$this->testGetActivityFields($config);
} catch (Exception $e) {
$this->recordResult('Field Metadata', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testGetDealFields(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new DealFieldsApi(null, $config);
$result = $apiInstance->getDealFields();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$count = $result->getData() ? count($result->getData()) : 0;
$this->recordResult('Get Deal Fields', true, "Retrieved {$count} deal fields in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Deal Fields', false, $e->getMessage(), $elapsed);
}
}
private function testGetPersonFields(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new PersonFieldsApi(null, $config);
$result = $apiInstance->getPersonFields();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$count = $result->getData() ? count($result->getData()) : 0;
$this->recordResult('Get Person Fields', true, "Retrieved {$count} person fields in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Person Fields', false, $e->getMessage(), $elapsed);
}
}
private function testGetActivityFields(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new ActivityFieldsApi(null, $config);
$result = $apiInstance->getActivityFields();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$count = $result->getData() ? count($result->getData()) : 0;
$this->recordResult('Get Activity Fields', true, "Retrieved {$count} activity fields in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Activity Fields', false, $e->getMessage(), $elapsed);
}
}
private function testV2ApiSupport(SocialAccount $socialAccount): void
{
$this->info('Test 4: v2 API Support (Cursor-based Pagination)');
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
$startTime = microtime(true);
// Test v2 deals endpoint with cursor pagination
// Note: The official SDK may or may not have v2 API support
// This test will determine if v2 is available
try {
$apiInstance = new DealsApi(null, $config);
$updatedSince = Carbon::now()->subDays(30)->toRfc3339String();
$result = $apiInstance->getDealsSummary();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
// Check if response has cursor information (v2 indicator)
// The response object doesn't have getAdditionalData method
// Just test if the call works - v2 API support is limited in official SDK v1
$this->recordResult('v2 API Support', false, "v1 API works but v2 cursor pagination not available in official SDK v1. Retrieved summary in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('v2 API Support', false, $e->getMessage(), $elapsed);
}
} catch (Exception $e) {
$this->recordResult('v2 API Support', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testErrorHandling(SocialAccount $socialAccount): void
{
$this->info('Test 5: Error Handling');
try {
// Test 5a: Invalid Token
$this->testInvalidToken();
// Test 5b: Non-existent Resource
$this->testNonExistentResource($socialAccount);
} catch (Exception $e) {
$this->recordResult('Error Handling', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testInvalidToken(): void
{
$startTime = microtime(true);
try {
$config = new Configuration();
$config->setAccessToken('invalid_token');
$client = new DealsApi(null, $config);
$client->getDealsSummary();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Invalid Token Error', false, "Expected error but request succeeded in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Invalid Token Error', true, "Correctly threw exception: {$e->getMessage()} in {$elapsed}ms", $elapsed);
}
}
private function testNonExistentResource(SocialAccount $socialAccount): void
{
$startTime = microtime(true);
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
$client = new DealsApi(null, $config);
// Try to get a deal with a very high ID that likely doesn't exist
// Note: Official SDK doesn't have a simple getDeal method, so skip this
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Non-existent Resource Error', false, "Method not available in official SDK - API structure different", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Non-existent Resource Error', true, "Correctly threw exception: {$e->getMessage()} in {$elapsed}ms", $elapsed);
}
}
private function recordResult(string $testName, bool $success, string $message, float $elapsed): void
{
$this->results[] = [
'test' => $testName,
'success' => $success,
'message' => $message,
'elapsed' => $elapsed,
];
$status = $success ? '✅ PASS' : '❌ FAIL';
$this->line(" {$status} - {$testName}: {$message}");
}
private function displayResults(): void
{
$this->newLine();
$this->info('=== POC Test Results Summary ===');
$this->newLine();
$total = count($this->results);
$passed = count(array_filter($this->results, fn ($r) => $r['success']));
$failed = $total - $passed;
$this->line("Total Tests: {$total}");
$this->line("Passed: {$passed}");
$this->line("Failed: {$failed}");
$this->newLine();
$totalTime = array_sum(array_column($this->results, 'elapsed'));
$this->line("Total Execution Time: {$totalTime}ms");
$this->newLine();
if ($failed > 0) {
$this->warn('Failed Tests:');
foreach ($this->results as $result) {
if (! $result['success']) {
$this->line(" - {$result['test']}: {$result['message']}");
}
}
$this->newLine();
}
$this->info('=== Detailed Results ===');
foreach ($this->results as $result) {
$status = $result['success'] ? '✅' : '❌';
$this->line("{$status} {$result['test']} ({$result['elapsed']}ms): {$result['message']}");
}
$this->newLine();
$this->info('=== Recommendations ===');
if ($passed === $total) {
$this->info('✅ All tests passed! The official SDK is viable for migration.');
$this->info('Next steps: Proceed with full migration planning.');
} else {
$this->warn('⚠️ Some tests failed. Review the failures above before proceeding.');
$this->warn('Consider hybrid approach or investigate alternatives if critical operations fail.');
}
}
}
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":"pipedrive-sdk-poc, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.05618351,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: pipedrive-sdk-poc","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\\Console\\Commands\\Crm;\n\nuse Jiminny\\Console\\Commands\\Command;\nuse Jiminny\\Console\\Commands\\Crm\\Traits\\SyncObjectsCommandTrait;\nuse Jiminny\\Jobs\\Crm\\SyncObjects as SyncObjectsJob;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Models\\Team;\n\nclass SyncObjects extends Command\n{\n use SyncObjectsCommandTrait;\n\n /**\n * The name and signature of the console command.\n *\n * @var string\n */\n protected $signature = 'crm:sync-objects {team?} {--sync}';\n\n /**\n * The console command description.\n *\n * @var string\n */\n protected $description = 'Sync remote CRM objects.';\n\n /**\n * Execute the console command.\n */\n public function handle(): void\n {\n $teamId = $this->argument('team');\n $teams = [];\n\n if ($teamId) {\n $team = Team::idOrUuId($teamId);\n if ($team && $team->crm?->provider === Configuration::PROVIDER_HUBSPOT) {\n $this->error(sprintf(\n 'Team %s (%s) uses HubSpot. Use crm:sync-hubspot-objects instead.',\n $team->getName(),\n $team->getUuid()\n ));\n\n return;\n }\n if ($team) {\n $teams[] = $team;\n }\n } else {\n // Exclude HubSpot teams - handled by crm:sync-hubspot-objects\n $teams = Team::where('status', Team::STATUS_ACTIVE)\n ->whereHas(\n 'crm',\n fn ($q) => $q\n ->where('provider', '!=', Configuration::PROVIDER_HUBSPOT)\n ->where('sync_objects', true)\n )\n ->get();\n }\n\n $this->dispatchSyncJobsForTeams($teams);\n }\n\n protected function getStaggerDelaySeconds(): float\n {\n return 2;\n }\n\n protected function getMaxDelaySeconds(): int\n {\n // Cap delay at 15 minutes (SQS max delay limit)\n // capacity of 450 teams before reach\n return 900;\n }\n\n protected function getLogPrefix(): string\n {\n return '';\n }\n\n protected function createSyncJob(Team $team): Job\n {\n return new SyncObjectsJob($team);\n }\n}","depth":4,"bounds":{"left":0.15724733,"top":0.2952913,"width":0.21476063,"height":0.7047087},"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Console\\Commands\\Crm;\n\nuse Jiminny\\Console\\Commands\\Command;\nuse Jiminny\\Console\\Commands\\Crm\\Traits\\SyncObjectsCommandTrait;\nuse Jiminny\\Jobs\\Crm\\SyncObjects as SyncObjectsJob;\nuse Jiminny\\Jobs\\Job;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Models\\Team;\n\nclass SyncObjects extends Command\n{\n use SyncObjectsCommandTrait;\n\n /**\n * The name and signature of the console command.\n *\n * @var string\n */\n protected $signature = 'crm:sync-objects {team?} {--sync}';\n\n /**\n * The console command description.\n *\n * @var string\n */\n protected $description = 'Sync remote CRM objects.';\n\n /**\n * Execute the console command.\n */\n public function handle(): void\n {\n $teamId = $this->argument('team');\n $teams = [];\n\n if ($teamId) {\n $team = Team::idOrUuId($teamId);\n if ($team && $team->crm?->provider === Configuration::PROVIDER_HUBSPOT) {\n $this->error(sprintf(\n 'Team %s (%s) uses HubSpot. Use crm:sync-hubspot-objects instead.',\n $team->getName(),\n $team->getUuid()\n ));\n\n return;\n }\n if ($team) {\n $teams[] = $team;\n }\n } else {\n // Exclude HubSpot teams - handled by crm:sync-hubspot-objects\n $teams = Team::where('status', Team::STATUS_ACTIVE)\n ->whereHas(\n 'crm',\n fn ($q) => $q\n ->where('provider', '!=', Configuration::PROVIDER_HUBSPOT)\n ->where('sync_objects', true)\n )\n ->get();\n }\n\n $this->dispatchSyncJobsForTeams($teams);\n }\n\n protected function getStaggerDelaySeconds(): float\n {\n return 2;\n }\n\n protected function getMaxDelaySeconds(): int\n {\n // Cap delay at 15 minutes (SQS max delay limit)\n // capacity of 450 teams before reach\n return 900;\n }\n\n protected function getLogPrefix(): string\n {\n return '';\n }\n\n protected function createSyncJob(Team $team): Job\n {\n return new SyncObjectsJob($team);\n }\n}","role_description":"text entry area","is_enabled":true,"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":"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":"AXStaticText","text":"1","depth":4,"bounds":{"left":0.57912236,"top":0.10055866,"width":0.00731383,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"51","depth":4,"bounds":{"left":0.5884308,"top":0.10055866,"width":0.009640957,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"1","depth":4,"bounds":{"left":0.6000665,"top":0.10055866,"width":0.00731383,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.6090425,"top":0.09896249,"width":0.00731383,"height":0.018355945},"on_screen":true,"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.6163564,"top":0.09896249,"width":0.006981383,"height":0.018355945},"on_screen":true,"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\\Console\\Commands\\Crm;\n\nuse Carbon\\Carbon;\nuse Exception;\nuse Illuminate\\Console\\Command;\nuse Jiminny\\Component\\Encryption\\EncryptedTokenManager;\nuse Jiminny\\Models\\SocialAccount;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Pipedrive\\versions\\v1\\Api\\ActivitiesApi;\nuse Pipedrive\\versions\\v1\\Api\\ActivityFieldsApi;\nuse Pipedrive\\versions\\v1\\Api\\DealFieldsApi;\nuse Pipedrive\\versions\\v1\\Api\\DealsApi;\nuse Pipedrive\\versions\\v1\\Api\\PersonFieldsApi;\nuse Pipedrive\\versions\\v1\\Api\\PersonsApi;\nuse Pipedrive\\versions\\v1\\Configuration;\n\nclass TestPipedriveOfficialSdkCommand extends Command\n{\n protected $signature = 'jiminny:test-pipedrive-official-sdk {teamId}';\n\n protected $description = 'Test official Pipedrive SDK (pipedrive/pipedrive) with existing OAuth tokens';\n\n private array $results = [];\n\n private EncryptedTokenManager $tokenManager;\n\n public function __construct(EncryptedTokenManager $tokenManager)\n {\n parent::__construct();\n $this->tokenManager = $tokenManager;\n }\n\n public function handle(): int\n {\n $teamId = (int) $this->argument('teamId');\n\n $this->info(\"Starting Pipedrive Official SDK POC for Team ID: {$teamId}\");\n $this->newLine();\n\n try {\n $this->runTests($teamId);\n } catch (Exception $e) {\n $this->error(\"POC failed with error: {$e->getMessage()}\");\n $this->error($e->getTraceAsString());\n\n return 1;\n }\n\n $this->displayResults();\n\n return 0;\n }\n\n private function runTests(int $teamId): void\n {\n $team = Team::find($teamId);\n if (! $team) {\n throw new Exception(\"Team with ID {$teamId} not found\");\n }\n\n $socialAccount = SocialAccount::where('provider', SocialAccount::PROVIDER_PIPEDRIVE)\n ->whereHas('sociable', function ($query) use ($team) {\n $query->where('team_id', $team->id);\n })\n ->orderByDesc('expires') // Get the most recently expiring token\n ->first();\n\n if (! $socialAccount) {\n throw new Exception(\"No Pipedrive social account found for team {$teamId}\");\n }\n\n $this->info(\"Found Pipedrive account for team: {$team->name}\");\n $this->newLine();\n\n // Test 1: OAuth Initialization\n $this->testOAuthInitialization($socialAccount);\n\n // Test 2: Basic CRUD Operations\n $this->testBasicCrudOperations($socialAccount);\n\n // Test 3: Field Metadata\n $this->testFieldMetadata($socialAccount);\n\n // Test 4: v2 API Support\n $this->testV2ApiSupport($socialAccount);\n\n // Test 5: Error Handling\n $this->testErrorHandling($socialAccount);\n }\n\n private function testOAuthInitialization(SocialAccount $socialAccount): void\n {\n $this->info('Test 1: OAuth Initialization');\n $startTime = microtime(true);\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n\n $client = new DealsApi(null, $config);\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('OAuth Initialization', true, \"Client initialized successfully in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('OAuth Initialization', false, $e->getMessage(), $elapsed);\n }\n\n $this->newLine();\n }\n\n private function testBasicCrudOperations(SocialAccount $socialAccount): void\n {\n $this->info('Test 2: Basic CRUD Operations');\n\n try {\n // Get the raw token from database\n $rawDbToken = $socialAccount->getProviderUserToken();\n $this->info(\"DB token (length: \" . strlen($rawDbToken) . \")\");\n $this->info(\"DB token preview: \" . substr($rawDbToken, 0, 20) . \"...\");\n\n // Check token expiry\n $expires = $socialAccount->expires;\n $this->info(\"Token expires at: \" . ($expires ? Carbon::createFromTimestamp($expires)->toDateTimeString() : 'null'));\n $this->info(\"Token is expired: \" . ($expires && $expires < time() ? 'YES' : 'NO'));\n\n // Test with the DB token directly\n $this->info(\"\\n--- Testing with DB token ---\");\n $config = new Configuration();\n $config->setAccessToken($rawDbToken);\n\n // Test 2a: Get Deals\n $this->testGetDeals($config);\n\n // Test 2b: Get Persons\n $this->testGetPersons($config);\n\n // Test 2c: Get Activities\n $this->testGetActivities($config);\n } catch (Exception $e) {\n $this->recordResult('Basic CRUD Operations', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testCurrentSystemApproach(SocialAccount $socialAccount): void\n {\n $startTime = microtime(true);\n\n try {\n // Simulate how current system makes requests\n $token = $socialAccount->getProviderUserToken();\n $url = 'https://api.pipedrive.com/api/v2/deals';\n\n $headers = [\n 'Authorization' => 'Bearer ' . $token,\n 'Accept' => 'application/json',\n ];\n\n $client = new \\GuzzleHttp\\Client();\n $response = $client->get($url, [\n 'headers' => $headers,\n 'timeout' => 30,\n ]);\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $statusCode = $response->getStatusCode();\n $this->recordResult('Current System v2 API', $statusCode === 200, \"Status: {$statusCode} in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Current System v2 API', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetDeals(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new DealsApi(null, $config);\n // Official SDK uses getDealsSummary instead of getDeals\n $result = $apiInstance->getDealsSummary();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Deals Summary', true, \"Retrieved deals summary in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Deals Summary', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetPersons(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n // Official SDK doesn't have a simple getPersons method\n // Skip this test for now as the API structure is different\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Persons', false, \"Method not available in official SDK - API structure different\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Persons', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetActivities(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n // Official SDK doesn't have a simple getActivities method\n // Skip this test for now as the API structure is different\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Activities', false, \"Method not available in official SDK - API structure different\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Activities', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testFieldMetadata(SocialAccount $socialAccount): void\n {\n $this->info('Test 3: Field Metadata');\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n\n // Test 3a: Get Deal Fields\n $this->testGetDealFields($config);\n\n // Test 3b: Get Person Fields\n $this->testGetPersonFields($config);\n\n // Test 3c: Get Activity Fields\n $this->testGetActivityFields($config);\n } catch (Exception $e) {\n $this->recordResult('Field Metadata', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testGetDealFields(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new DealFieldsApi(null, $config);\n $result = $apiInstance->getDealFields();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $count = $result->getData() ? count($result->getData()) : 0;\n $this->recordResult('Get Deal Fields', true, \"Retrieved {$count} deal fields in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Deal Fields', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetPersonFields(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new PersonFieldsApi(null, $config);\n $result = $apiInstance->getPersonFields();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $count = $result->getData() ? count($result->getData()) : 0;\n $this->recordResult('Get Person Fields', true, \"Retrieved {$count} person fields in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Person Fields', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetActivityFields(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new ActivityFieldsApi(null, $config);\n $result = $apiInstance->getActivityFields();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $count = $result->getData() ? count($result->getData()) : 0;\n $this->recordResult('Get Activity Fields', true, \"Retrieved {$count} activity fields in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Activity Fields', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testV2ApiSupport(SocialAccount $socialAccount): void\n {\n $this->info('Test 4: v2 API Support (Cursor-based Pagination)');\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n\n $startTime = microtime(true);\n\n // Test v2 deals endpoint with cursor pagination\n // Note: The official SDK may or may not have v2 API support\n // This test will determine if v2 is available\n try {\n $apiInstance = new DealsApi(null, $config);\n $updatedSince = Carbon::now()->subDays(30)->toRfc3339String();\n $result = $apiInstance->getDealsSummary();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n\n // Check if response has cursor information (v2 indicator)\n // The response object doesn't have getAdditionalData method\n // Just test if the call works - v2 API support is limited in official SDK v1\n $this->recordResult('v2 API Support', false, \"v1 API works but v2 cursor pagination not available in official SDK v1. Retrieved summary in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('v2 API Support', false, $e->getMessage(), $elapsed);\n }\n } catch (Exception $e) {\n $this->recordResult('v2 API Support', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testErrorHandling(SocialAccount $socialAccount): void\n {\n $this->info('Test 5: Error Handling');\n\n try {\n // Test 5a: Invalid Token\n $this->testInvalidToken();\n\n // Test 5b: Non-existent Resource\n $this->testNonExistentResource($socialAccount);\n } catch (Exception $e) {\n $this->recordResult('Error Handling', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testInvalidToken(): void\n {\n $startTime = microtime(true);\n\n try {\n $config = new Configuration();\n $config->setAccessToken('invalid_token');\n $client = new DealsApi(null, $config);\n\n $client->getDealsSummary();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Invalid Token Error', false, \"Expected error but request succeeded in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Invalid Token Error', true, \"Correctly threw exception: {$e->getMessage()} in {$elapsed}ms\", $elapsed);\n }\n }\n\n private function testNonExistentResource(SocialAccount $socialAccount): void\n {\n $startTime = microtime(true);\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n $client = new DealsApi(null, $config);\n\n // Try to get a deal with a very high ID that likely doesn't exist\n // Note: Official SDK doesn't have a simple getDeal method, so skip this\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Non-existent Resource Error', false, \"Method not available in official SDK - API structure different\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Non-existent Resource Error', true, \"Correctly threw exception: {$e->getMessage()} in {$elapsed}ms\", $elapsed);\n }\n }\n\n private function recordResult(string $testName, bool $success, string $message, float $elapsed): void\n {\n $this->results[] = [\n 'test' => $testName,\n 'success' => $success,\n 'message' => $message,\n 'elapsed' => $elapsed,\n ];\n\n $status = $success ? '✅ PASS' : '❌ FAIL';\n $this->line(\" {$status} - {$testName}: {$message}\");\n }\n\n private function displayResults(): void\n {\n $this->newLine();\n $this->info('=== POC Test Results Summary ===');\n $this->newLine();\n\n $total = count($this->results);\n $passed = count(array_filter($this->results, fn ($r) => $r['success']));\n $failed = $total - $passed;\n\n $this->line(\"Total Tests: {$total}\");\n $this->line(\"Passed: {$passed}\");\n $this->line(\"Failed: {$failed}\");\n $this->newLine();\n\n $totalTime = array_sum(array_column($this->results, 'elapsed'));\n $this->line(\"Total Execution Time: {$totalTime}ms\");\n $this->newLine();\n\n if ($failed > 0) {\n $this->warn('Failed Tests:');\n foreach ($this->results as $result) {\n if (! $result['success']) {\n $this->line(\" - {$result['test']}: {$result['message']}\");\n }\n }\n $this->newLine();\n }\n\n $this->info('=== Detailed Results ===');\n foreach ($this->results as $result) {\n $status = $result['success'] ? '✅' : '❌';\n $this->line(\"{$status} {$result['test']} ({$result['elapsed']}ms): {$result['message']}\");\n }\n\n $this->newLine();\n $this->info('=== Recommendations ===');\n\n if ($passed === $total) {\n $this->info('✅ All tests passed! The official SDK is viable for migration.');\n $this->info('Next steps: Proceed with full migration planning.');\n } else {\n $this->warn('⚠️ Some tests failed. Review the failures above before proceeding.');\n $this->warn('Consider hybrid approach or investigate alternatives if critical operations fail.');\n }\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Console\\Commands\\Crm;\n\nuse Carbon\\Carbon;\nuse Exception;\nuse Illuminate\\Console\\Command;\nuse Jiminny\\Component\\Encryption\\EncryptedTokenManager;\nuse Jiminny\\Models\\SocialAccount;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Pipedrive\\versions\\v1\\Api\\ActivitiesApi;\nuse Pipedrive\\versions\\v1\\Api\\ActivityFieldsApi;\nuse Pipedrive\\versions\\v1\\Api\\DealFieldsApi;\nuse Pipedrive\\versions\\v1\\Api\\DealsApi;\nuse Pipedrive\\versions\\v1\\Api\\PersonFieldsApi;\nuse Pipedrive\\versions\\v1\\Api\\PersonsApi;\nuse Pipedrive\\versions\\v1\\Configuration;\n\nclass TestPipedriveOfficialSdkCommand extends Command\n{\n protected $signature = 'jiminny:test-pipedrive-official-sdk {teamId}';\n\n protected $description = 'Test official Pipedrive SDK (pipedrive/pipedrive) with existing OAuth tokens';\n\n private array $results = [];\n\n private EncryptedTokenManager $tokenManager;\n\n public function __construct(EncryptedTokenManager $tokenManager)\n {\n parent::__construct();\n $this->tokenManager = $tokenManager;\n }\n\n public function handle(): int\n {\n $teamId = (int) $this->argument('teamId');\n\n $this->info(\"Starting Pipedrive Official SDK POC for Team ID: {$teamId}\");\n $this->newLine();\n\n try {\n $this->runTests($teamId);\n } catch (Exception $e) {\n $this->error(\"POC failed with error: {$e->getMessage()}\");\n $this->error($e->getTraceAsString());\n\n return 1;\n }\n\n $this->displayResults();\n\n return 0;\n }\n\n private function runTests(int $teamId): void\n {\n $team = Team::find($teamId);\n if (! $team) {\n throw new Exception(\"Team with ID {$teamId} not found\");\n }\n\n $socialAccount = SocialAccount::where('provider', SocialAccount::PROVIDER_PIPEDRIVE)\n ->whereHas('sociable', function ($query) use ($team) {\n $query->where('team_id', $team->id);\n })\n ->orderByDesc('expires') // Get the most recently expiring token\n ->first();\n\n if (! $socialAccount) {\n throw new Exception(\"No Pipedrive social account found for team {$teamId}\");\n }\n\n $this->info(\"Found Pipedrive account for team: {$team->name}\");\n $this->newLine();\n\n // Test 1: OAuth Initialization\n $this->testOAuthInitialization($socialAccount);\n\n // Test 2: Basic CRUD Operations\n $this->testBasicCrudOperations($socialAccount);\n\n // Test 3: Field Metadata\n $this->testFieldMetadata($socialAccount);\n\n // Test 4: v2 API Support\n $this->testV2ApiSupport($socialAccount);\n\n // Test 5: Error Handling\n $this->testErrorHandling($socialAccount);\n }\n\n private function testOAuthInitialization(SocialAccount $socialAccount): void\n {\n $this->info('Test 1: OAuth Initialization');\n $startTime = microtime(true);\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n\n $client = new DealsApi(null, $config);\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('OAuth Initialization', true, \"Client initialized successfully in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('OAuth Initialization', false, $e->getMessage(), $elapsed);\n }\n\n $this->newLine();\n }\n\n private function testBasicCrudOperations(SocialAccount $socialAccount): void\n {\n $this->info('Test 2: Basic CRUD Operations');\n\n try {\n // Get the raw token from database\n $rawDbToken = $socialAccount->getProviderUserToken();\n $this->info(\"DB token (length: \" . strlen($rawDbToken) . \")\");\n $this->info(\"DB token preview: \" . substr($rawDbToken, 0, 20) . \"...\");\n\n // Check token expiry\n $expires = $socialAccount->expires;\n $this->info(\"Token expires at: \" . ($expires ? Carbon::createFromTimestamp($expires)->toDateTimeString() : 'null'));\n $this->info(\"Token is expired: \" . ($expires && $expires < time() ? 'YES' : 'NO'));\n\n // Test with the DB token directly\n $this->info(\"\\n--- Testing with DB token ---\");\n $config = new Configuration();\n $config->setAccessToken($rawDbToken);\n\n // Test 2a: Get Deals\n $this->testGetDeals($config);\n\n // Test 2b: Get Persons\n $this->testGetPersons($config);\n\n // Test 2c: Get Activities\n $this->testGetActivities($config);\n } catch (Exception $e) {\n $this->recordResult('Basic CRUD Operations', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testCurrentSystemApproach(SocialAccount $socialAccount): void\n {\n $startTime = microtime(true);\n\n try {\n // Simulate how current system makes requests\n $token = $socialAccount->getProviderUserToken();\n $url = 'https://api.pipedrive.com/api/v2/deals';\n\n $headers = [\n 'Authorization' => 'Bearer ' . $token,\n 'Accept' => 'application/json',\n ];\n\n $client = new \\GuzzleHttp\\Client();\n $response = $client->get($url, [\n 'headers' => $headers,\n 'timeout' => 30,\n ]);\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $statusCode = $response->getStatusCode();\n $this->recordResult('Current System v2 API', $statusCode === 200, \"Status: {$statusCode} in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Current System v2 API', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetDeals(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new DealsApi(null, $config);\n // Official SDK uses getDealsSummary instead of getDeals\n $result = $apiInstance->getDealsSummary();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Deals Summary', true, \"Retrieved deals summary in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Deals Summary', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetPersons(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n // Official SDK doesn't have a simple getPersons method\n // Skip this test for now as the API structure is different\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Persons', false, \"Method not available in official SDK - API structure different\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Persons', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetActivities(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n // Official SDK doesn't have a simple getActivities method\n // Skip this test for now as the API structure is different\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Activities', false, \"Method not available in official SDK - API structure different\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Activities', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testFieldMetadata(SocialAccount $socialAccount): void\n {\n $this->info('Test 3: Field Metadata');\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n\n // Test 3a: Get Deal Fields\n $this->testGetDealFields($config);\n\n // Test 3b: Get Person Fields\n $this->testGetPersonFields($config);\n\n // Test 3c: Get Activity Fields\n $this->testGetActivityFields($config);\n } catch (Exception $e) {\n $this->recordResult('Field Metadata', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testGetDealFields(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new DealFieldsApi(null, $config);\n $result = $apiInstance->getDealFields();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $count = $result->getData() ? count($result->getData()) : 0;\n $this->recordResult('Get Deal Fields', true, \"Retrieved {$count} deal fields in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Deal Fields', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetPersonFields(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new PersonFieldsApi(null, $config);\n $result = $apiInstance->getPersonFields();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $count = $result->getData() ? count($result->getData()) : 0;\n $this->recordResult('Get Person Fields', true, \"Retrieved {$count} person fields in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Person Fields', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testGetActivityFields(Configuration $config): void\n {\n $startTime = microtime(true);\n\n try {\n $apiInstance = new ActivityFieldsApi(null, $config);\n $result = $apiInstance->getActivityFields();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $count = $result->getData() ? count($result->getData()) : 0;\n $this->recordResult('Get Activity Fields', true, \"Retrieved {$count} activity fields in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Get Activity Fields', false, $e->getMessage(), $elapsed);\n }\n }\n\n private function testV2ApiSupport(SocialAccount $socialAccount): void\n {\n $this->info('Test 4: v2 API Support (Cursor-based Pagination)');\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n\n $startTime = microtime(true);\n\n // Test v2 deals endpoint with cursor pagination\n // Note: The official SDK may or may not have v2 API support\n // This test will determine if v2 is available\n try {\n $apiInstance = new DealsApi(null, $config);\n $updatedSince = Carbon::now()->subDays(30)->toRfc3339String();\n $result = $apiInstance->getDealsSummary();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n\n // Check if response has cursor information (v2 indicator)\n // The response object doesn't have getAdditionalData method\n // Just test if the call works - v2 API support is limited in official SDK v1\n $this->recordResult('v2 API Support', false, \"v1 API works but v2 cursor pagination not available in official SDK v1. Retrieved summary in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('v2 API Support', false, $e->getMessage(), $elapsed);\n }\n } catch (Exception $e) {\n $this->recordResult('v2 API Support', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testErrorHandling(SocialAccount $socialAccount): void\n {\n $this->info('Test 5: Error Handling');\n\n try {\n // Test 5a: Invalid Token\n $this->testInvalidToken();\n\n // Test 5b: Non-existent Resource\n $this->testNonExistentResource($socialAccount);\n } catch (Exception $e) {\n $this->recordResult('Error Handling', false, $e->getMessage(), 0);\n }\n\n $this->newLine();\n }\n\n private function testInvalidToken(): void\n {\n $startTime = microtime(true);\n\n try {\n $config = new Configuration();\n $config->setAccessToken('invalid_token');\n $client = new DealsApi(null, $config);\n\n $client->getDealsSummary();\n\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Invalid Token Error', false, \"Expected error but request succeeded in {$elapsed}ms\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Invalid Token Error', true, \"Correctly threw exception: {$e->getMessage()} in {$elapsed}ms\", $elapsed);\n }\n }\n\n private function testNonExistentResource(SocialAccount $socialAccount): void\n {\n $startTime = microtime(true);\n\n try {\n $config = new Configuration();\n $config->setAccessToken($socialAccount->provider_user_token);\n $client = new DealsApi(null, $config);\n\n // Try to get a deal with a very high ID that likely doesn't exist\n // Note: Official SDK doesn't have a simple getDeal method, so skip this\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Non-existent Resource Error', false, \"Method not available in official SDK - API structure different\", $elapsed);\n } catch (Exception $e) {\n $elapsed = round((microtime(true) - $startTime) * 1000, 2);\n $this->recordResult('Non-existent Resource Error', true, \"Correctly threw exception: {$e->getMessage()} in {$elapsed}ms\", $elapsed);\n }\n }\n\n private function recordResult(string $testName, bool $success, string $message, float $elapsed): void\n {\n $this->results[] = [\n 'test' => $testName,\n 'success' => $success,\n 'message' => $message,\n 'elapsed' => $elapsed,\n ];\n\n $status = $success ? '✅ PASS' : '❌ FAIL';\n $this->line(\" {$status} - {$testName}: {$message}\");\n }\n\n private function displayResults(): void\n {\n $this->newLine();\n $this->info('=== POC Test Results Summary ===');\n $this->newLine();\n\n $total = count($this->results);\n $passed = count(array_filter($this->results, fn ($r) => $r['success']));\n $failed = $total - $passed;\n\n $this->line(\"Total Tests: {$total}\");\n $this->line(\"Passed: {$passed}\");\n $this->line(\"Failed: {$failed}\");\n $this->newLine();\n\n $totalTime = array_sum(array_column($this->results, 'elapsed'));\n $this->line(\"Total Execution Time: {$totalTime}ms\");\n $this->newLine();\n\n if ($failed > 0) {\n $this->warn('Failed Tests:');\n foreach ($this->results as $result) {\n if (! $result['success']) {\n $this->line(\" - {$result['test']}: {$result['message']}\");\n }\n }\n $this->newLine();\n }\n\n $this->info('=== Detailed Results ===');\n foreach ($this->results as $result) {\n $status = $result['success'] ? '✅' : '❌';\n $this->line(\"{$status} {$result['test']} ({$result['elapsed']}ms): {$result['message']}\");\n }\n\n $this->newLine();\n $this->info('=== Recommendations ===');\n\n if ($passed === $total) {\n $this->info('✅ All tests passed! The official SDK is viable for migration.');\n $this->info('Next steps: Proceed with full migration planning.');\n } else {\n $this->warn('⚠️ Some tests failed. Review the failures above before proceeding.');\n $this->warn('Consider hybrid approach or investigate alternatives if critical operations fail.');\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}]...
|
5365831007722038384
|
61370652661533572
|
click
|
accessibility
|
NULL
|
Project: faVsco.js, menu
pipedrive-sdk-poc, menu
S Project: faVsco.js, menu
pipedrive-sdk-poc, 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\Console\Commands\Crm;
use Jiminny\Console\Commands\Command;
use Jiminny\Console\Commands\Crm\Traits\SyncObjectsCommandTrait;
use Jiminny\Jobs\Crm\SyncObjects as SyncObjectsJob;
use Jiminny\Jobs\Job;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Models\Team;
class SyncObjects extends Command
{
use SyncObjectsCommandTrait;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'crm:sync-objects {team?} {--sync}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Sync remote CRM objects.';
/**
* Execute the console command.
*/
public function handle(): void
{
$teamId = $this->argument('team');
$teams = [];
if ($teamId) {
$team = Team::idOrUuId($teamId);
if ($team && $team->crm?->provider === Configuration::PROVIDER_HUBSPOT) {
$this->error(sprintf(
'Team %s (%s) uses HubSpot. Use crm:sync-hubspot-objects instead.',
$team->getName(),
$team->getUuid()
));
return;
}
if ($team) {
$teams[] = $team;
}
} else {
// Exclude HubSpot teams - handled by crm:sync-hubspot-objects
$teams = Team::where('status', Team::STATUS_ACTIVE)
->whereHas(
'crm',
fn ($q) => $q
->where('provider', '!=', Configuration::PROVIDER_HUBSPOT)
->where('sync_objects', true)
)
->get();
}
$this->dispatchSyncJobsForTeams($teams);
}
protected function getStaggerDelaySeconds(): float
{
return 2;
}
protected function getMaxDelaySeconds(): int
{
// Cap delay at 15 minutes (SQS max delay limit)
// capacity of 450 teams before reach
return 900;
}
protected function getLogPrefix(): string
{
return '';
}
protected function createSyncJob(Team $team): Job
{
return new SyncObjectsJob($team);
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
1
51
1
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Console\Commands\Crm;
use Carbon\Carbon;
use Exception;
use Illuminate\Console\Command;
use Jiminny\Component\Encryption\EncryptedTokenManager;
use Jiminny\Models\SocialAccount;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Pipedrive\versions\v1\Api\ActivitiesApi;
use Pipedrive\versions\v1\Api\ActivityFieldsApi;
use Pipedrive\versions\v1\Api\DealFieldsApi;
use Pipedrive\versions\v1\Api\DealsApi;
use Pipedrive\versions\v1\Api\PersonFieldsApi;
use Pipedrive\versions\v1\Api\PersonsApi;
use Pipedrive\versions\v1\Configuration;
class TestPipedriveOfficialSdkCommand extends Command
{
protected $signature = 'jiminny:test-pipedrive-official-sdk {teamId}';
protected $description = 'Test official Pipedrive SDK (pipedrive/pipedrive) with existing OAuth tokens';
private array $results = [];
private EncryptedTokenManager $tokenManager;
public function __construct(EncryptedTokenManager $tokenManager)
{
parent::__construct();
$this->tokenManager = $tokenManager;
}
public function handle(): int
{
$teamId = (int) $this->argument('teamId');
$this->info("Starting Pipedrive Official SDK POC for Team ID: {$teamId}");
$this->newLine();
try {
$this->runTests($teamId);
} catch (Exception $e) {
$this->error("POC failed with error: {$e->getMessage()}");
$this->error($e->getTraceAsString());
return 1;
}
$this->displayResults();
return 0;
}
private function runTests(int $teamId): void
{
$team = Team::find($teamId);
if (! $team) {
throw new Exception("Team with ID {$teamId} not found");
}
$socialAccount = SocialAccount::where('provider', SocialAccount::PROVIDER_PIPEDRIVE)
->whereHas('sociable', function ($query) use ($team) {
$query->where('team_id', $team->id);
})
->orderByDesc('expires') // Get the most recently expiring token
->first();
if (! $socialAccount) {
throw new Exception("No Pipedrive social account found for team {$teamId}");
}
$this->info("Found Pipedrive account for team: {$team->name}");
$this->newLine();
// Test 1: OAuth Initialization
$this->testOAuthInitialization($socialAccount);
// Test 2: Basic CRUD Operations
$this->testBasicCrudOperations($socialAccount);
// Test 3: Field Metadata
$this->testFieldMetadata($socialAccount);
// Test 4: v2 API Support
$this->testV2ApiSupport($socialAccount);
// Test 5: Error Handling
$this->testErrorHandling($socialAccount);
}
private function testOAuthInitialization(SocialAccount $socialAccount): void
{
$this->info('Test 1: OAuth Initialization');
$startTime = microtime(true);
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
$client = new DealsApi(null, $config);
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('OAuth Initialization', true, "Client initialized successfully in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('OAuth Initialization', false, $e->getMessage(), $elapsed);
}
$this->newLine();
}
private function testBasicCrudOperations(SocialAccount $socialAccount): void
{
$this->info('Test 2: Basic CRUD Operations');
try {
// Get the raw token from database
$rawDbToken = $socialAccount->getProviderUserToken();
$this->info("DB token (length: " . strlen($rawDbToken) . ")");
$this->info("DB token preview: " . substr($rawDbToken, 0, 20) . "...");
// Check token expiry
$expires = $socialAccount->expires;
$this->info("Token expires at: " . ($expires ? Carbon::createFromTimestamp($expires)->toDateTimeString() : 'null'));
$this->info("Token is expired: " . ($expires && $expires < time() ? 'YES' : 'NO'));
// Test with the DB token directly
$this->info("\n--- Testing with DB token ---");
$config = new Configuration();
$config->setAccessToken($rawDbToken);
// Test 2a: Get Deals
$this->testGetDeals($config);
// Test 2b: Get Persons
$this->testGetPersons($config);
// Test 2c: Get Activities
$this->testGetActivities($config);
} catch (Exception $e) {
$this->recordResult('Basic CRUD Operations', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testCurrentSystemApproach(SocialAccount $socialAccount): void
{
$startTime = microtime(true);
try {
// Simulate how current system makes requests
$token = $socialAccount->getProviderUserToken();
$url = 'https://api.pipedrive.com/api/v2/deals';
$headers = [
'Authorization' => 'Bearer ' . $token,
'Accept' => 'application/json',
];
$client = new \GuzzleHttp\Client();
$response = $client->get($url, [
'headers' => $headers,
'timeout' => 30,
]);
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$statusCode = $response->getStatusCode();
$this->recordResult('Current System v2 API', $statusCode === 200, "Status: {$statusCode} in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Current System v2 API', false, $e->getMessage(), $elapsed);
}
}
private function testGetDeals(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new DealsApi(null, $config);
// Official SDK uses getDealsSummary instead of getDeals
$result = $apiInstance->getDealsSummary();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Deals Summary', true, "Retrieved deals summary in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Deals Summary', false, $e->getMessage(), $elapsed);
}
}
private function testGetPersons(Configuration $config): void
{
$startTime = microtime(true);
try {
// Official SDK doesn't have a simple getPersons method
// Skip this test for now as the API structure is different
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Persons', false, "Method not available in official SDK - API structure different", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Persons', false, $e->getMessage(), $elapsed);
}
}
private function testGetActivities(Configuration $config): void
{
$startTime = microtime(true);
try {
// Official SDK doesn't have a simple getActivities method
// Skip this test for now as the API structure is different
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Activities', false, "Method not available in official SDK - API structure different", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Activities', false, $e->getMessage(), $elapsed);
}
}
private function testFieldMetadata(SocialAccount $socialAccount): void
{
$this->info('Test 3: Field Metadata');
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
// Test 3a: Get Deal Fields
$this->testGetDealFields($config);
// Test 3b: Get Person Fields
$this->testGetPersonFields($config);
// Test 3c: Get Activity Fields
$this->testGetActivityFields($config);
} catch (Exception $e) {
$this->recordResult('Field Metadata', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testGetDealFields(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new DealFieldsApi(null, $config);
$result = $apiInstance->getDealFields();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$count = $result->getData() ? count($result->getData()) : 0;
$this->recordResult('Get Deal Fields', true, "Retrieved {$count} deal fields in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Deal Fields', false, $e->getMessage(), $elapsed);
}
}
private function testGetPersonFields(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new PersonFieldsApi(null, $config);
$result = $apiInstance->getPersonFields();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$count = $result->getData() ? count($result->getData()) : 0;
$this->recordResult('Get Person Fields', true, "Retrieved {$count} person fields in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Person Fields', false, $e->getMessage(), $elapsed);
}
}
private function testGetActivityFields(Configuration $config): void
{
$startTime = microtime(true);
try {
$apiInstance = new ActivityFieldsApi(null, $config);
$result = $apiInstance->getActivityFields();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$count = $result->getData() ? count($result->getData()) : 0;
$this->recordResult('Get Activity Fields', true, "Retrieved {$count} activity fields in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Get Activity Fields', false, $e->getMessage(), $elapsed);
}
}
private function testV2ApiSupport(SocialAccount $socialAccount): void
{
$this->info('Test 4: v2 API Support (Cursor-based Pagination)');
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
$startTime = microtime(true);
// Test v2 deals endpoint with cursor pagination
// Note: The official SDK may or may not have v2 API support
// This test will determine if v2 is available
try {
$apiInstance = new DealsApi(null, $config);
$updatedSince = Carbon::now()->subDays(30)->toRfc3339String();
$result = $apiInstance->getDealsSummary();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
// Check if response has cursor information (v2 indicator)
// The response object doesn't have getAdditionalData method
// Just test if the call works - v2 API support is limited in official SDK v1
$this->recordResult('v2 API Support', false, "v1 API works but v2 cursor pagination not available in official SDK v1. Retrieved summary in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('v2 API Support', false, $e->getMessage(), $elapsed);
}
} catch (Exception $e) {
$this->recordResult('v2 API Support', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testErrorHandling(SocialAccount $socialAccount): void
{
$this->info('Test 5: Error Handling');
try {
// Test 5a: Invalid Token
$this->testInvalidToken();
// Test 5b: Non-existent Resource
$this->testNonExistentResource($socialAccount);
} catch (Exception $e) {
$this->recordResult('Error Handling', false, $e->getMessage(), 0);
}
$this->newLine();
}
private function testInvalidToken(): void
{
$startTime = microtime(true);
try {
$config = new Configuration();
$config->setAccessToken('invalid_token');
$client = new DealsApi(null, $config);
$client->getDealsSummary();
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Invalid Token Error', false, "Expected error but request succeeded in {$elapsed}ms", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Invalid Token Error', true, "Correctly threw exception: {$e->getMessage()} in {$elapsed}ms", $elapsed);
}
}
private function testNonExistentResource(SocialAccount $socialAccount): void
{
$startTime = microtime(true);
try {
$config = new Configuration();
$config->setAccessToken($socialAccount->provider_user_token);
$client = new DealsApi(null, $config);
// Try to get a deal with a very high ID that likely doesn't exist
// Note: Official SDK doesn't have a simple getDeal method, so skip this
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Non-existent Resource Error', false, "Method not available in official SDK - API structure different", $elapsed);
} catch (Exception $e) {
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
$this->recordResult('Non-existent Resource Error', true, "Correctly threw exception: {$e->getMessage()} in {$elapsed}ms", $elapsed);
}
}
private function recordResult(string $testName, bool $success, string $message, float $elapsed): void
{
$this->results[] = [
'test' => $testName,
'success' => $success,
'message' => $message,
'elapsed' => $elapsed,
];
$status = $success ? '✅ PASS' : '❌ FAIL';
$this->line(" {$status} - {$testName}: {$message}");
}
private function displayResults(): void
{
$this->newLine();
$this->info('=== POC Test Results Summary ===');
$this->newLine();
$total = count($this->results);
$passed = count(array_filter($this->results, fn ($r) => $r['success']));
$failed = $total - $passed;
$this->line("Total Tests: {$total}");
$this->line("Passed: {$passed}");
$this->line("Failed: {$failed}");
$this->newLine();
$totalTime = array_sum(array_column($this->results, 'elapsed'));
$this->line("Total Execution Time: {$totalTime}ms");
$this->newLine();
if ($failed > 0) {
$this->warn('Failed Tests:');
foreach ($this->results as $result) {
if (! $result['success']) {
$this->line(" - {$result['test']}: {$result['message']}");
}
}
$this->newLine();
}
$this->info('=== Detailed Results ===');
foreach ($this->results as $result) {
$status = $result['success'] ? '✅' : '❌';
$this->line("{$status} {$result['test']} ({$result['elapsed']}ms): {$result['message']}");
}
$this->newLine();
$this->info('=== Recommendations ===');
if ($passed === $total) {
$this->info('✅ All tests passed! The official SDK is viable for migration.');
$this->info('Next steps: Proceed with full migration planning.');
} else {
$this->warn('⚠️ Some tests failed. Review the failures above before proceeding.');
$this->warn('Consider hybrid approach or investigate alternatives if critical operations fail.');
}
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
49393
|
1763
|
21
|
2026-05-18T06:39:40.960366+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779086380960_m2.jpg...
|
PhpStorm
|
faVsco.js – TestPipedriveOfficialSdkCommand.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
PhostormVIewINavicarecodeLaravelKeractorFV faVsco. PhostormVIewINavicarecodeLaravelKeractorFV faVsco.js?° pipedrive-sdk-pocProiect© SyncProfileMetadata.phpCConvertLeadActivities.ong© SyncPlanhat.php©syncleammetadata.ong1estPlpeariveomricialsakcommand.orUpoateopponunityspecrications.onocreatePlaybookcreatedevent.ong>@ Dealinsights>@Dev> 0 Dialersc) IntearationApp/Service.php(C) LeadConverted.php© CreateSelfCoachedEvent.phpD DTOS©) CreateCommentedEvent.phpC) CreateSmsSentEvent.ohpC) PlanhatActivityListener.phoC Elasticsearch(C)AskAnvthinaPromotService.ohd› EnqagementStatsш GeckoExport(C)AutomatedReportsCommand.ohvphp api y2.ohoC) RequestGenerateReport.Job.oho/>D Livestream(C) AutomatedReportkesulconp© AutomatedReport.phpA console [STAGING]Mallboxes• MidratePlavbackithemes• M Plavbooksdectarelstrict_types-ty,Playlists> M Postmark> M PronhetAuse…..v M Renorto© AutomatedReportsCommand.phpC) AutomatedRenortsRetentionPolicvCor14 Dclass syncubnects extends command© AutomatedReportsSendCommand.pht© CreateMockAskJiminnyReportResultC© DeleteReportCommand.php© GenerateMarketingReport.php* The name and sianature of the console commandi© Team.php© Usage.php>C Slack* ovar strind›_ Teams>C Tracks7301orotected ssionature = crm:sunc-ohnects «team?, <--svnct'*w Transcription>C Twilio* The console command descrintion.w Users> M Vocabularv>Zoom*Avan strinaC) Command.oho© CreateDatabaseUsers.phpprotected $description = 'Sync remote CRM objects.';C)DatabaseTiableCount.oho© DeleteOIdAiCrmNotesCommand.ohv* Eyecuto the concole commandlCDeleteS?Leftoverscommand.ohn@ DevPostmanCommand.phpC) DiarizeViaAiParticinantldentificationCompublic function handle: voidf...;@ EncrvntTokensCommand.nhn(C EnaadementStatsRedenerateCommand.r( FeatureFlaasHelner nhr69 6t)protected function getStaggerDelavSeconds@: floatf...© FixCrossTenantlssues.php© FlushRolesPermissionsCache.php74 6tprotected function getMaxDelavSeconds@: int{...}© GeneratelnternalWebhookToken.php9 GrounSetDefaultLanguageCommand.php81 6t>protected function getLoqPrefix@: stringf...;l© HelperTruncateCoachingTables.phpHelner Code will hoin INF to underctand vour Laravel ann code II Generate II Don't Show Anvmore (4 miAskJiminnyReportActivityServiceTest v=custom.log=laravel.log4 SF jiminny@localhost]A HS_local (jiminny@localhost]A console [PROD]© TestPipedriveOfficialSdkCoand.php x © Kernel.phpA console [EU]119class TestPipedrive0fficialSdkCommand extends Commandprivate function testBasicCrudOperations(SocialAccount $socialAccount): void'Test 2: Basic CRUD Operations');Analyzing...^vtryfGet the raw token from database$rawDbToken = $socialAccount->getProviderUserToken();Sthis->infod strino: "DB token Genath." strlenScawihToken? "")$this->info( string: "DB token preview:.substr(SrawDbToken// Check token expirySexnines = ScocialAccount->exninps.$this->info( string: "Token expires at: " . (Sexpires ? Carbon:: createFromTimes$this->info( string: "Token is expired: " . (Sexpires && $expires ‹ timeO ? 'Y// Test with the DB token directlySthis->info( string: "n--- Testing with DB token ---"):Sconfig = new ConfiqurationO:Sconfig->setAccessToken(SrawDbToken):// Test 2a: Get DealsSthis->testGetDeals(Sconfig):II Test 2b: Get PersonsSthis->testGetPersons(Sconfig)I/ Test 2c: Get ActivitiesSthis->testGetActivities(Sconfig):} catch (Exception $e) {sthis->recordResultd testNan•'Basic CRUD Onerations'success: fallse. Se->aet$this->newLine():private function testCurrentSystemApproach(SocialAccount $socialAccount): voidf...}-private function testGetDeals(Configuration $config): voidf….;private function testGetPersons(Configuration $config): void{...}private function testGetActivities(Configuration Sconfig): void{...}private function testFieldMetadata(SocialAccount SsocialAccount): void{...}private function testGetDealFields(Confiquration Sconfiq): void{...}lprivate function testGetPersonFields(Configuration Sconfiq): voidf...}private function testGetActivitvFields(Confiquration Sconfig): void{...}100% Lz• Mon 18 May 9:39:40+0 •wDebugging Pipedrive sdocker exec docker lamp_ 1 pho artisan 11minnv:test-o1pedrive-ott1c1al-sdk 19Starting Pipedrive Official SDK POC for Team ID: 19Found Pipedrive account for team: Pipedrive, IncTeSt PASS - OAuth Inatialization: Client initialized successfully in 32.85msDe t 2e Bsen cRu Soperations6/6 tasks done ›POC Results: Official SDK Works SuccessfullySummarvThe official Pipedrive SDK IS compatible with your tokens. The issue was simply that the token had expired. After refreshing, all core operations work perfectlyTest ResultsSuccessful (6/10 tests):• ®Auth Initialization: 32.85ms•Get Deals Summary: 1197.89ms• Ger Deal Fields: 711.68ms (49 tields)•Get Person Fields: 697.38ms (27 fields)•Get Activity Fields: 889.77ms (35 fields)• Invalid Token Error: 709.55ms (correct error handling)X Expected Failures (4/10):•Get Persons/Activities: Different API structure in official SDK•v2 APl Supnort: v1 SDK doesn't have v2 cursor padination.Nan-ayictent Dacourca. Mothad name differancasKey Findinas1. Token format is compatible: The official SDK accepts the same token format as the community SDK7. Core onerations work: Deals. field metadata. and error handilina all function correctivi3. Architecture differences: Method names and API structure differ between SDKs4. Token retresh works. Your token refresh mechanism uodated the token successfullvConclucionThe official SDK is viable for migration. The main work will be:•Mapping method names between SDKs• Adapting to different API response structuresImplementing v2 API support if needed (may require direct HTTP calls)SWF.16Cal ae* Reiect allAccent allWN Windsurf Teamo127-11Po 4 spaces...
|
NULL
|
6145865064454473855
|
NULL
|
visual_change
|
ocr
|
NULL
|
PhostormVIewINavicarecodeLaravelKeractorFV faVsco. PhostormVIewINavicarecodeLaravelKeractorFV faVsco.js?° pipedrive-sdk-pocProiect© SyncProfileMetadata.phpCConvertLeadActivities.ong© SyncPlanhat.php©syncleammetadata.ong1estPlpeariveomricialsakcommand.orUpoateopponunityspecrications.onocreatePlaybookcreatedevent.ong>@ Dealinsights>@Dev> 0 Dialersc) IntearationApp/Service.php(C) LeadConverted.php© CreateSelfCoachedEvent.phpD DTOS©) CreateCommentedEvent.phpC) CreateSmsSentEvent.ohpC) PlanhatActivityListener.phoC Elasticsearch(C)AskAnvthinaPromotService.ohd› EnqagementStatsш GeckoExport(C)AutomatedReportsCommand.ohvphp api y2.ohoC) RequestGenerateReport.Job.oho/>D Livestream(C) AutomatedReportkesulconp© AutomatedReport.phpA console [STAGING]Mallboxes• MidratePlavbackithemes• M Plavbooksdectarelstrict_types-ty,Playlists> M Postmark> M PronhetAuse…..v M Renorto© AutomatedReportsCommand.phpC) AutomatedRenortsRetentionPolicvCor14 Dclass syncubnects extends command© AutomatedReportsSendCommand.pht© CreateMockAskJiminnyReportResultC© DeleteReportCommand.php© GenerateMarketingReport.php* The name and sianature of the console commandi© Team.php© Usage.php>C Slack* ovar strind›_ Teams>C Tracks7301orotected ssionature = crm:sunc-ohnects «team?, <--svnct'*w Transcription>C Twilio* The console command descrintion.w Users> M Vocabularv>Zoom*Avan strinaC) Command.oho© CreateDatabaseUsers.phpprotected $description = 'Sync remote CRM objects.';C)DatabaseTiableCount.oho© DeleteOIdAiCrmNotesCommand.ohv* Eyecuto the concole commandlCDeleteS?Leftoverscommand.ohn@ DevPostmanCommand.phpC) DiarizeViaAiParticinantldentificationCompublic function handle: voidf...;@ EncrvntTokensCommand.nhn(C EnaadementStatsRedenerateCommand.r( FeatureFlaasHelner nhr69 6t)protected function getStaggerDelavSeconds@: floatf...© FixCrossTenantlssues.php© FlushRolesPermissionsCache.php74 6tprotected function getMaxDelavSeconds@: int{...}© GeneratelnternalWebhookToken.php9 GrounSetDefaultLanguageCommand.php81 6t>protected function getLoqPrefix@: stringf...;l© HelperTruncateCoachingTables.phpHelner Code will hoin INF to underctand vour Laravel ann code II Generate II Don't Show Anvmore (4 miAskJiminnyReportActivityServiceTest v=custom.log=laravel.log4 SF jiminny@localhost]A HS_local (jiminny@localhost]A console [PROD]© TestPipedriveOfficialSdkCoand.php x © Kernel.phpA console [EU]119class TestPipedrive0fficialSdkCommand extends Commandprivate function testBasicCrudOperations(SocialAccount $socialAccount): void'Test 2: Basic CRUD Operations');Analyzing...^vtryfGet the raw token from database$rawDbToken = $socialAccount->getProviderUserToken();Sthis->infod strino: "DB token Genath." strlenScawihToken? "")$this->info( string: "DB token preview:.substr(SrawDbToken// Check token expirySexnines = ScocialAccount->exninps.$this->info( string: "Token expires at: " . (Sexpires ? Carbon:: createFromTimes$this->info( string: "Token is expired: " . (Sexpires && $expires ‹ timeO ? 'Y// Test with the DB token directlySthis->info( string: "n--- Testing with DB token ---"):Sconfig = new ConfiqurationO:Sconfig->setAccessToken(SrawDbToken):// Test 2a: Get DealsSthis->testGetDeals(Sconfig):II Test 2b: Get PersonsSthis->testGetPersons(Sconfig)I/ Test 2c: Get ActivitiesSthis->testGetActivities(Sconfig):} catch (Exception $e) {sthis->recordResultd testNan•'Basic CRUD Onerations'success: fallse. Se->aet$this->newLine():private function testCurrentSystemApproach(SocialAccount $socialAccount): voidf...}-private function testGetDeals(Configuration $config): voidf….;private function testGetPersons(Configuration $config): void{...}private function testGetActivities(Configuration Sconfig): void{...}private function testFieldMetadata(SocialAccount SsocialAccount): void{...}private function testGetDealFields(Confiquration Sconfiq): void{...}lprivate function testGetPersonFields(Configuration Sconfiq): voidf...}private function testGetActivitvFields(Confiquration Sconfig): void{...}100% Lz• Mon 18 May 9:39:40+0 •wDebugging Pipedrive sdocker exec docker lamp_ 1 pho artisan 11minnv:test-o1pedrive-ott1c1al-sdk 19Starting Pipedrive Official SDK POC for Team ID: 19Found Pipedrive account for team: Pipedrive, IncTeSt PASS - OAuth Inatialization: Client initialized successfully in 32.85msDe t 2e Bsen cRu Soperations6/6 tasks done ›POC Results: Official SDK Works SuccessfullySummarvThe official Pipedrive SDK IS compatible with your tokens. The issue was simply that the token had expired. After refreshing, all core operations work perfectlyTest ResultsSuccessful (6/10 tests):• ®Auth Initialization: 32.85ms•Get Deals Summary: 1197.89ms• Ger Deal Fields: 711.68ms (49 tields)•Get Person Fields: 697.38ms (27 fields)•Get Activity Fields: 889.77ms (35 fields)• Invalid Token Error: 709.55ms (correct error handling)X Expected Failures (4/10):•Get Persons/Activities: Different API structure in official SDK•v2 APl Supnort: v1 SDK doesn't have v2 cursor padination.Nan-ayictent Dacourca. Mothad name differancasKey Findinas1. Token format is compatible: The official SDK accepts the same token format as the community SDK7. Core onerations work: Deals. field metadata. and error handilina all function correctivi3. Architecture differences: Method names and API structure differ between SDKs4. Token retresh works. Your token refresh mechanism uodated the token successfullvConclucionThe official SDK is viable for migration. The main work will be:•Mapping method names between SDKs• Adapting to different API response structuresImplementing v2 API support if needed (may require direct HTTP calls)SWF.16Cal ae* Reiect allAccent allWN Windsurf Teamo127-11Po 4 spaces...
|
49391
|
NULL
|
NULL
|
NULL
|
|
49392
|
1762
|
19
|
2026-05-18T06:39:39.271722+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779086379271_m1.jpg...
|
PhpStorm
|
faVsco.js – TestPipedriveOfficialSdkCommand.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
|
[{"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}]...
|
8043719072324535154
|
-8628527368849355612
|
visual_change
|
hybrid
|
NULL
|
Project: faVsco.js, menu
Term2ShellEditSessionProf Project: faVsco.js, menu
Term2ShellEditSessionProfilesWindowHelpDOCKER (docker-compose)O 82DOCKER81DEV (-zsh)11DOCKER (docker-compose)NFO""component" ::"e802ad473a4f""o.e.p.PluginsService","cluster. name" :"docker-cluster""node. name""message" :module [x-pack-ql]"elasticsearch"timestamp" :"2026-05-18T06:39:35,926Z"NFO""component": "o.e.p.PluginsService""cluster.name":"docker-cluster""level": "I"node .name""e802ad473a4f""message":elasticsearch"loaded module [x-pack-rollup]" }"timestamp":NFO""2026-05-18T06:39:35,926Z""component": "o.e.p.PluginsService""node. name""e802ad473a4f""message" :"loaded module [x-pack-security]" }elasticsearch"timestamp" :"2026-05-18T06:39:35,926Z"NFO","component" :"o.e.p.PluginsService""cluster.name":"docker-cluster","node. name": "e802ad473a4f""message":"Loaded module [x-pack-sql]" }elasticsearchNFO",1 {"type":"server""timestamp" :"2026-05-18T06:39:35,926Z""component":"o.e.p.PluginsService","cluster.name":"docker-cluster""level":"I"node. name": "e802ad473a4f""message" :"loadedmodule [x-pack-stack]"}elasticsearchI {"type":"server""timestamp""2026-05-18T06:39:35,926Z"NFO""component":"o.e.p.PluginsService"cluster.name":"docker-cluster""level":"I"node. name""e802ad473a4f""message":"loadedmodule[x-pack-voting-only-node]" }elasticsearch | {"type": "server""timestamp": "2026-05-18T06:39:35,926Z"NFO","component": "o.e.p.PluginsService","cluster.name":"docker-cluster""level":"I"node. name": "e802ad473a4f"', "message": "loaded module [x-pack-watcher]" }elasticsearch1 {"type": "server""timestamp": "2026-05-18T06:39:35,926Z"NFO", "component": "o.e.p.PluginsService", "cluster.name":"docker-cluster""level":"I"node. name": "e802ad473a4f"', "message": "no plugins loaded" 3elasticsearchI {"type": "deprecation", "timestamp": "2026-05-18T06:39:35,995Z", "level": "DEPRECATION","component": "o.e.d.c.s.Settings", "cluster.name";"docker-cluster"node.name":"e802ad473a4f", "message":"[node.data] setting was deprecated in Elasticsearch and will be removed in a future release! See the breakingdocumentation forthe next major version." }elasticsearch1 {"type": "server"NFO""component": "o.e.e.NodeEnvironment",ments, uster-n-m5-18 Tdocker-clusten","Level name"node. name":"e80Zad473a4f", "message": "using [1] data paths, mounts [[/usr/share/elasticsearch/data (/dev/vda1)]], net usable_space [14gb], net total_space [58.3gb], types [ext4]" }elasticsearch1 {"type": "server""timestamp": "2026-05-18T06:39:36,015Z","level": "INFO", "component": "o.e.e.NodeEnvironment""cluster.name": "docker-cluster""node.name": "e802ad473a4f", "message": "heap size [700mb], compressed ordinary object pointers [true]" }elasticsearchNFO",1 {"type": "server"', "timestamp": "2026-05-18T06:39:36,106Z""level": "I"component": "o.e.n.Node""cluster.name": "docker-cluster""node.name": "e802ad473a4f""message": "node name [e802ad473a4f], node ID [e2ZKzgw4Q4aCf2w51jWr1A], clustername [docker-cluster], roles [transform, master, remote_cluster_client, data, ml, data_content, data_hot, data_warm, data_cold, ingest]" }100% <8• Mon 18 May 9:39:39screenpipe"181O $4APP (-zsh)*3T2PROD (-zsh)Last login: Sat May 16 18:04:33 on ttys001Poetry could not find a pyproject.toml file in /Users/lukas or its parentsPoetry could not find a pyproject.toml file in /Users/lukas or its parentslukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ |X L3 EU (-zsh)Last login: Sat May 16 18:04:33 on ttys001Poetry could not find a pyproject.toml file in /Users/lukas or its parentsPoetry could not find a pyproject.tomlfile in /Users/lukas or its parents@Lukas-Kovaliks-MacBook-Pro-Jiminny~$ IX T4STAGE (-zsh)Last login: Sat May 16 18:04:33 on ttys001Poetry could not find a pyproject.toml file in /Users/lukas or its parentsPoetry could not find a pyproject.toml file in /Users/lukas or its parentslukas@Lukas-Kovaliks-MacBook-Pro-JiminnyT5QA (-zsh)Last login: Mon May 18 09:17:28on ttys003Poetry could not find a pyproject.toml file in /Users/lukas or its parentsPoetry could not find a pyproject.tomlfile in /Users/lukas or its parentsX T6FE (-zsh)Last login: Mon May 18 09:17:28on ttys004Poetry could not find a pyproject.toml file in /Users/lukas or its parentsPoetry couldnotfind a pyproject.tomlfile in /Users/lukas or its parentslukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ IEXT (-zsh)Poetry could not find a pyproject.toml file in /Users/lukas or its parentsPoetry couldnot find a pyproject.tomlfile in /Users/lukas or its parentslukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ [|PRODSTAGEFRONTENDEXTENSIONV View in Docker Desktop• View ConfigEnable Watch...
|
49390
|
NULL
|
NULL
|
NULL
|
|
35171
|
1316
|
20
|
2026-05-13T12:16:30.957396+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-13/1778 /Users/lukas/.screenpipe/data/data/2026-05-13/1778674590957_m2.jpg...
|
PhpStorm
|
faVsco.js – TeamInsightsRepository.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20891-improve-sms-text Project: faVsco.js, menu
JY-20891-improve-sms-text-relays, menu...
|
[{"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":"JY-20891-improve-sms-text-relays, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.08843085,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: JY-20891-improve-sms-text-relays","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-1362947798257697316
|
-2575675244731720149
|
click
|
hybrid
|
NULL
|
Project: faVsco.js, menu
JY-20891-improve-sms-text Project: faVsco.js, menu
JY-20891-improve-sms-text-relays, menu
PhpStormFV faVsco.js~INavigarecode(C) ActivitvCommentRenosita©ActivityLogRepository-phy© ActivityMessageRepositol©ActivityMomentRepositor© ActivityProviderRepositor©ActivityRepository.php© ActivitySearchFilterReposActivityShareRepository.F© ActivityUploadSettingRepC AIPromptkepository.onc© AskAnythingRepository.plC) DeviceRerositorv.oho© ElasticActivityRepository.(c) EmailMessadeRenositorv.(C) GenericA PromotRenosito© GroupRepository.php© InboxEmailBatchRepositoC InboxRepositorv.oho© InvitationRepository.php© JobRepository.php© LanguageRepository.php© MomentRepository.phpNotificationRepository.ph© ParticipantSpeechReposit© ParticipantStatsRepositor© PlaybookCategoryReposit© PlaybookRepository.phpPlaylistActivityRepository.© PlaylistRepository.phpPlaylistShareRepository.p© QuestionRepository.php© RoleChangeEventRepositi© RoleRepository.phpc) SearchRenositorv.onv© SnapshotRepository.phpC) SocialAccountRevositorv.© StageRepository.php(C) SubscriotionSetRevositon©TaskRepository.php(C) TeamA ContextRenositon© TeamDomainsRepository.(c) TeaminciahtcRenositorvr©TeamRepository.php(C) ThemeRenositorv nhn655675677679© MailboxController.php© SsoController.php©smsmessage.ong© SmsLength.phpQ- CONTEXT_TEAM.INSIGHTS ACTIVITY X 5 Cc w .*TU8:class leamenszencskepos.corypublic function getDashboardActivity0verTime(-›setSize( size: self::AGG_TERMS_MAX_SIZE)A16Y1 A->addAggregation(new rilterIclone sbooluuery)->addFilter((new lermo->setlerm key: "type.keyword",value: Activity::TYPE_SMS_OUTBOUND)(new DateHistogram( name: 'over time' field: 'created at'. ShistogramInterval))at: 'Suuuu-MM-dd")->settimezone (Stimezonelffset).->addAdaredation(new AggregationTerms( name:'by_channel'))->setFieldd field'type.keyword')-›setSize( size: self::AGG_TERMS_MAX_SIZE)SaggregationData = $this-›searchService-›search($query, $this-›model, queryName: 'getDashboardActivitySactivityData = Collection: :make([array_get($aggregationData,array get saggregaclonvaca.key: 'voice_activities.over_time.buckets'),key:|'text_activities.over_time.buckets'),->collapse()->reoucestatic function (array $carry, array $bucketData): array f...7,return Collection::make(SactivityData):oublic function aetDashboardCoachina0vertimedE custom.logA console [STAGING]E laravel.logA SF jiminny@localhost]A HS_Jocal [jiminny@localhost]A console (PROD] X A console (EU]644645646648649650652-653654655656657665-666667=668669—0/0Tx: AutovORDER BY t.name, calendar domain:So jiminny040 A1 A40 V 65 ^select * trom users u jo1n calendars c 1<->I.n. on c.user 1d = U.1dwhere u.team_id = 882;select * from activities where id = 14049485: # team 563 crm 53select * from activities where id = 73272382; # team 563 crm 537select * from activities where id = 64400389: # team 563 crm 537select * from activities where id = 58081273; # team 563 crm 537select * from activities where id = 54520297; # team 563 crm 537select * from participants where activity_id = 58081273;select * from activities where crm_configuration_id = 537 and provider = 'aircall'and account_id = 19003658 order by updated_at desc;select * from contacts where crm_configuration_id = 537 and id = 35957759;select * from accounts where crm_configuration_id = 537 and id = 19003658;select * from automated_report_results where id = 1976;select * from automated_reports where id = 583;select * from activity_searches where id = 87714;select * from activity_search_filters where activity_search_id = 87714;SELECT * FROM activities WHERE uuid_to_bin('8827f672-202d-4162-9d04-73ff5f0566a9') = uuidor uvid_to_bin(*47842446-af51-4bcb-854f-CC6560290101') = uuid;SELECT * FROM crm_configurations WHERE provider = 'hubspot';select * from rate_limits;select * from alated_report_results where nedia,tyRe = 'pdf' and status = 2and id IN (18,1872):select * from automated_reports where id = 54;SELECT * FROM users WHERE id IN (24623,29443,29613);SELECT * FROM automated_report_results WHERE uuid_to_bin('822fa41b-afd3-43a9-a248-86b0e36f3131') = uuid;select * from text pelavs where created at 12026-05-014# and id IN (32415, 32416);and id = 72412.select * from users where team_id = 2 and email like '%scott%' and id = 29510;672vSELECT * FROM activities WHERE uvid_to_bin('67cebfc2-ed56-44a2-8C68-7a0286ed8618') = uuid; # 79763436sonnt Keview • In 44m100% L28• Wed 13 May 15:16:30U AskJiminnyReportActivityServiceTest~CascadeTrial Owner Role SeleRetactoring User EmaDashboard Activity De+0 ..Give me the whole flowComplete Dashboard Activity Flow1.AP Request Entrv PointFile: DashboardController.php:33-68• phppubuic tunction Terchtkequest srequest): JsonkesponseSuser = $this->getUserFromRequest (Srequest);Scriteria = Criteria::createFromRequest(Srequest-sallo. Suser->aetTimezone@)•Srequest→>val1date(Stilterser→>getva Lidaclonku les()→>auuo;return sthis->response->withArray([= StilterSet->renderon'activity-over-time' = Sthis->getActivityOverTime($user, $filterSet),Context set: CONTEXT_TEAM_ INSIGHTS_ACTIVITY = 'tean-insights-activity'2. Filter Set CreationFile: ActivitySearch.php: 132-244private function getteaminsightsPageFilters(bool SisExport = false): Filterdefinitioncollectionreturn FilterDefinitionCollection::make(Collection: :make(lFilterDefinition Teaminsiahts DateRangeFilter::class.FilterDefinition TeamInsiahts|Exists:.class.Ask anvthina (84L)« Code SWF-1.6f 4 spaces...
|
NULL
|
NULL
|
NULL
|
NULL
|