Skip to content

Commit 8f6e08d

Browse files
authored
[CommerceExtension] Handle errors caused by invalidated access token (#497)
* [CommerceExtension] Handle errors caused by invalidated access token * Add docblocks; fix test; fix some lints * More lint * Fix jquery race condition in commerce_extension.js * define -> require * Different approach for jquery race condition: get rid of allinone
1 parent d20855b commit 8f6e08d

File tree

9 files changed

+369
-313
lines changed

9 files changed

+369
-313
lines changed

app/code/Meta/BusinessExtension/Block/Adminhtml/Setup.php

Lines changed: 26 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,13 @@
2222

2323
use Magento\Backend\Block\Template;
2424
use Magento\Backend\Block\Template\Context;
25+
use GuzzleHttp\Exception\GuzzleException;
2526
use Magento\Framework\App\RequestInterface;
2627
use Magento\Framework\Exception\NoSuchEntityException;
2728
use Magento\Store\Api\StoreRepositoryInterface;
2829
use Magento\Store\Model\ResourceModel\Website\CollectionFactory as WebsiteCollectionFactory;
2930
use Meta\BusinessExtension\Helper\FBEHelper;
30-
use Meta\BusinessExtension\Helper\GraphAPIAdapter;
31+
use Meta\BusinessExtension\Helper\CommerceExtensionHelper;
3132
use Meta\BusinessExtension\Model\Api\CustomApiKey\ApiKeyService;
3233
use Meta\BusinessExtension\Model\System\Config as SystemConfig;
3334

@@ -45,11 +46,6 @@ class Setup extends Template
4546
*/
4647
private $fbeHelper;
4748

48-
/**
49-
* @var GraphAPIAdapter
50-
*/
51-
private $graphAPIAdapter;
52-
5349
/**
5450
* @var RequestInterface
5551
*/
@@ -70,14 +66,19 @@ class Setup extends Template
7066
*/
7167
private $websiteCollectionFactory;
7268

69+
/**
70+
* @var CommerceExtensionHelper
71+
*/
72+
private $commerceExtensionHelper;
73+
7374
/**
7475
* @param Context $context
7576
* @param RequestInterface $request
7677
* @param FBEHelper $fbeHelper
7778
* @param SystemConfig $systemConfig
78-
* @param GraphAPIAdapter $graphAPIAdapter
7979
* @param StoreRepositoryInterface $storeRepo
8080
* @param WebsiteCollectionFactory $websiteCollectionFactory
81+
* @param CommerceExtensionHelper $commerceExtensionHelper
8182
* @param ApiKeyService $apiKeyService
8283
* @param array $data
8384
*/
@@ -86,19 +87,19 @@ public function __construct(
8687
RequestInterface $request,
8788
FBEHelper $fbeHelper,
8889
SystemConfig $systemConfig,
89-
GraphAPIAdapter $graphAPIAdapter,
9090
StoreRepositoryInterface $storeRepo,
9191
WebsiteCollectionFactory $websiteCollectionFactory,
92+
CommerceExtensionHelper $commerceExtensionHelper,
9293
ApiKeyService $apiKeyService,
9394
array $data = []
9495
) {
9596
$this->fbeHelper = $fbeHelper;
9697
parent::__construct($context, $data);
9798
$this->request = $request;
9899
$this->systemConfig = $systemConfig;
99-
$this->graphAPIAdapter = $graphAPIAdapter;
100100
$this->storeRepo = $storeRepo;
101101
$this->websiteCollectionFactory = $websiteCollectionFactory;
102+
$this->commerceExtensionHelper = $commerceExtensionHelper;
102103
$this->apiKeyService = $apiKeyService;
103104
}
104105

@@ -194,9 +195,8 @@ public function fetchPixelId($storeId)
194195
*/
195196
public function isCommerceExtensionEnabled()
196197
{
197-
$storeID = $this->getSelectedStoreId();
198-
$storeHasCommercePartnerIntegration = !!$this->systemConfig->getCommercePartnerIntegrationId($storeID);
199-
return $storeHasCommercePartnerIntegration || $this->systemConfig->isCommerceExtensionEnabled();
198+
$storeId = $this->getSelectedStoreId();
199+
return $this->commerceExtensionHelper->isCommerceExtensionEnabled($storeId);
200200
}
201201

202202
/**
@@ -206,11 +206,7 @@ public function isCommerceExtensionEnabled()
206206
*/
207207
public function getPopupOrigin()
208208
{
209-
if (!$this->systemConfig->isCommerceExtensionEnabled()) {
210-
return 'https://business.facebook.com';
211-
}
212-
213-
return $this->systemConfig->getCommerceExtensionBaseURL();
209+
return $this->commerceExtensionHelper->getPopupOrigin();
214210
}
215211

216212
/**
@@ -220,12 +216,7 @@ public function getPopupOrigin()
220216
*/
221217
public function getSplashPageURL()
222218
{
223-
if (!$this->systemConfig->isCommerceExtensionEnabled()) {
224-
return 'https://business.facebook.com/fbe-iframe-get-started/?';
225-
}
226-
227-
$base_url = $this->systemConfig->getCommerceExtensionBaseURL();
228-
return $base_url . 'commerce_extension/splash/?';
219+
return $this->commerceExtensionHelper->getSplashPageURL();
229220
}
230221

231222
/**
@@ -315,10 +306,18 @@ public function isFBEInstalled($storeId)
315306
*/
316307
public function getCommerceExtensionIFrameURL($storeId)
317308
{
318-
return $this->graphAPIAdapter->getCommerceExtensionIFrameURL(
319-
$this->systemConfig->getExternalBusinessId($storeId),
320-
$this->systemConfig->getAccessToken($storeId),
321-
);
309+
return $this->commerceExtensionHelper->getCommerceExtensionIFrameURL($storeId);
310+
}
311+
312+
/**
313+
* Get a URL to use to render the CommerceExtension IFrame for an onboarded Store.
314+
*
315+
* @param int $storeId
316+
* @return string
317+
*/
318+
public function hasCommerceExtensionIFramePermissionError($storeId)
319+
{
320+
return $this->commerceExtensionHelper->hasCommerceExtensionPermissionError($storeId);
322321
}
323322

324323
/**
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* Copyright (c) Meta Platforms, Inc. and affiliates.
7+
*
8+
* Licensed under the Apache License, Version 2.0 (the "License");
9+
* you may not use this file except in compliance with the License.
10+
* You may obtain a copy of the License at
11+
*
12+
* http://www.apache.org/licenses/LICENSE-2.0
13+
*
14+
* Unless required by applicable law or agreed to in writing, software
15+
* distributed under the License is distributed on an "AS IS" BASIS,
16+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17+
* See the License for the specific language governing permissions and
18+
* limitations under the License.
19+
*/
20+
21+
namespace Meta\BusinessExtension\Helper;
22+
23+
use GuzzleHttp\Exception\GuzzleException;
24+
use Meta\BusinessExtension\Model\System\Config as SystemConfig;
25+
26+
class CommerceExtensionHelper
27+
{
28+
/**
29+
* @var SystemConfig
30+
*/
31+
private $systemConfig;
32+
33+
/**
34+
* @var GraphAPIAdapter
35+
*/
36+
private GraphAPIAdapter $graphAPIAdapter;
37+
38+
/**
39+
* @var array
40+
*/
41+
private array $resultCacheByStoreID = [];
42+
43+
/**
44+
* FBEHelper constructor
45+
*
46+
* @param SystemConfig $systemConfig
47+
* @param GraphAPIAdapter $graphAPIAdapter
48+
*/
49+
public function __construct(
50+
SystemConfig $systemConfig,
51+
GraphAPIAdapter $graphAPIAdapter
52+
) {
53+
$this->systemConfig = $systemConfig;
54+
$this->graphAPIAdapter = $graphAPIAdapter;
55+
}
56+
57+
/**
58+
* The URL to load the iframe splash page for non-onboarded stores.
59+
*
60+
* @return string
61+
*/
62+
public function getSplashPageURL()
63+
{
64+
if (!$this->systemConfig->isCommerceExtensionEnabled()) {
65+
return 'https://business.facebook.com/fbe-iframe-get-started/?';
66+
}
67+
68+
$base_url = $this->systemConfig->getCommerceExtensionBaseURL();
69+
return $base_url . 'commerce_extension/splash/?';
70+
}
71+
72+
/**
73+
* The expected origin for the Messages received from the FBE iframe/popup.
74+
*
75+
* @return string
76+
*/
77+
public function getPopupOrigin()
78+
{
79+
if (!$this->systemConfig->isCommerceExtensionEnabled()) {
80+
return 'https://business.facebook.com';
81+
}
82+
83+
return $this->systemConfig->getCommerceExtensionBaseURL();
84+
}
85+
86+
/**
87+
* Whether to enable the new Commerce Extension UI
88+
*
89+
* @param int $storeId
90+
* @return bool
91+
*/
92+
public function isCommerceExtensionEnabled($storeId)
93+
{
94+
$storeHasCommercePartnerIntegration = !!$this->systemConfig->getCommercePartnerIntegrationId($storeId);
95+
return $storeHasCommercePartnerIntegration || $this->systemConfig->isCommerceExtensionEnabled();
96+
}
97+
98+
/**
99+
* Whether there is an error blocking usage of the Commerce Extension.
100+
*
101+
* @param int $storeId
102+
* @return bool
103+
*/
104+
public function hasCommerceExtensionPermissionError($storeId)
105+
{
106+
$this->ensureCacheIsPopulated($storeId);
107+
return $this->resultCacheByStoreID[$storeId]['permission_exception'] != null;
108+
}
109+
110+
/**
111+
* Get a URL to use to render the CommerceExtension IFrame for an onboarded Store.
112+
*
113+
* @param int $storeId
114+
* @return string
115+
*/
116+
public function getCommerceExtensionIFrameURL($storeId)
117+
{
118+
$this->ensureCacheIsPopulated($storeId);
119+
return $this->resultCacheByStoreID[$storeId]['url'];
120+
}
121+
122+
/**
123+
* And adds the iframe URL or fetch error to a private cache.
124+
*
125+
* @param int $storeId
126+
*/
127+
private function ensureCacheIsPopulated($storeId)
128+
{
129+
if (array_key_exists($storeId, $this->resultCacheByStoreID)) {
130+
return;
131+
}
132+
133+
try {
134+
$url = $this->graphAPIAdapter->getCommerceExtensionIFrameURL(
135+
$this->systemConfig->getExternalBusinessId($storeId),
136+
$this->systemConfig->getAccessToken($storeId),
137+
);
138+
$this->resultCacheByStoreID[$storeId] = ['permission_exception' => null, 'url' => $url];
139+
} catch (GuzzleException $ex) {
140+
if ($ex->getCode() !== 400) {
141+
throw $ex;
142+
}
143+
144+
$this->resultCacheByStoreID[$storeId] = ['permission_exception' => $ex, 'url' => null];
145+
}
146+
}
147+
}

app/code/Meta/BusinessExtension/Test/Unit/Block/Adminhtml/SetupTest.php

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
use Magento\Store\Api\StoreRepositoryInterface;
88
use Magento\Store\Model\ResourceModel\Website\CollectionFactory as WebsiteCollectionFactory;
99
use Meta\BusinessExtension\Block\Adminhtml\Setup;
10+
use Meta\BusinessExtension\Helper\CommerceExtensionHelper;
1011
use Meta\BusinessExtension\Helper\FBEHelper;
11-
use Meta\BusinessExtension\Helper\GraphAPIAdapter;
1212
use Meta\BusinessExtension\Model\Api\CustomApiKey\ApiKeyService;
1313
use Meta\BusinessExtension\Model\System\Config as SystemConfig;
1414
use PHPUnit\Framework\MockObject\MockObject;
@@ -29,7 +29,7 @@ class SetupTest extends TestCase
2929
/**
3030
* @var MockObject
3131
*/
32-
private $graphAPIAdapter;
32+
private $commerceExtensionHelper;
3333

3434
/**
3535
* @var MockObject
@@ -68,10 +68,6 @@ public function testGetApiKey()
6868
->disableOriginalConstructor()
6969
->getMock();
7070

71-
$this->graphAPIAdapter = $this->getMockBuilder(GraphAPIAdapter::class)
72-
->disableOriginalConstructor()
73-
->getMock();
74-
7571
$this->systemConfig = $this->getMockBuilder(SystemConfig::class)
7672
->disableOriginalConstructor()
7773
->getMock();
@@ -92,6 +88,10 @@ public function testGetApiKey()
9288
->disableOriginalConstructor()
9389
->getMock();
9490

91+
$this->commerceExtensionHelper = $this->getMockBuilder(CommerceExtensionHelper::class)
92+
->disableOriginalConstructor()
93+
->getMock();
94+
9595
$this->apiKeyService->method('upsertApiKey')
9696
->willReturn($apiKey);
9797

@@ -100,9 +100,9 @@ public function testGetApiKey()
100100
$this->requestInterface,
101101
$this->fbeHelper,
102102
$this->systemConfig,
103-
$this->graphAPIAdapter,
104103
$this->storeRepo,
105104
$this->websiteCollectionFactory,
105+
$this->commerceExtensionHelper,
106106
$this->apiKeyService,
107107
[]
108108
);

0 commit comments

Comments
 (0)