-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathClientFactory.php
More file actions
149 lines (125 loc) · 4.04 KB
/
Copy pathClientFactory.php
File metadata and controls
149 lines (125 loc) · 4.04 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
<?php
/**
* Assinafy API client construction.
*
* @package Assinafy\WP
*/
declare(strict_types=1);
namespace Assinafy\WP;
defined( 'ABSPATH' ) || exit;
use Assinafy\SDK\AssinafyClient;
use Assinafy\SDK\Configuration;
use Assinafy\WP\Http\WpHttpClient;
use WP_Error;
/**
* Builds and memoises the SDK client.
*
* This is the plugin's only construction site for `AssinafyClient`. The SDK's static
* factories (`create()`, `fromArray()`, `forAuth()`, `forBearer()`) all fall back to the
* bundled Guzzle transport, and Guzzle is not installed — the plugin replaces it with
* `WpHttpClient`. Calling one of them is therefore a fatal error, not a style choice.
*
* The base URL is derived from the environment setting and never from user input:
* `Configuration` rejects a non-HTTPS base URL outright unless the host is loopback, and a
* free-text field is an invitation to point a credentialled request at another origin.
*/
final class ClientFactory {
/**
* Memoised client for this request.
*/
private ?AssinafyClient $client = null;
/**
* Why the last `client()` call returned null, when that reason is worth showing.
*/
private ?WP_Error $error = null;
/**
* @param Credentials $credentials Credential store.
* @param Log $log Plugin event log.
*/
public function __construct(
private readonly Credentials $credentials,
private readonly Log $log
) {
}
/**
* The configured client, or null when the plugin has no usable credentials.
*
* Callers must handle null. Missing credentials are an ordinary state — the plugin is
* installed but not yet set up — and must not fatal a page load.
*/
public function client(): ?AssinafyClient {
if ( null !== $this->client ) {
return $this->client;
}
$this->error = null;
$oauth_mode = $this->credentials->uses_oauth();
$credential = $this->credential();
if ( $credential instanceof WP_Error ) {
$this->error = $credential;
return null;
}
$account_id = $this->credentials->account_id();
if ( '' === $credential || '' === $account_id ) {
return null;
}
try {
$config = $oauth_mode
? Configuration::forBearer( $credential, $account_id, $this->base_url(), 30, 10 )
: new Configuration( $credential, $account_id, $this->base_url(), 30, 10 );
$this->client = new AssinafyClient( $config, new WpHttpClient( $config, null, $oauth_mode ? $this->renewal( $credential ) : null ) );
} catch ( \Throwable $e ) {
$this->error = new WP_Error( 'assinafy_client_unavailable', $e->getMessage() );
$this->log->add(
'client_unavailable',
array(
'error' => $e->getMessage(),
'type' => get_debug_type( $e ),
)
);
return null;
}
return $this->client;
}
/** @return string|WP_Error Active credential. */
private function credential(): string|WP_Error {
if ( ! $this->credentials->uses_oauth() ) {
return $this->credentials->api_key();
}
return ( new OAuthTokens( $this->credentials ) )->access_token();
}
/**
* OAuth: when the API answers 401, refresh once. Later calls in this request build a new
* client with the result.
*
* @param string $rejected Access token this client sends.
* @return \Closure(): (string|WP_Error)
*/
private function renewal( #[\SensitiveParameter] string $rejected ): \Closure {
return function () use ( $rejected ): string|WP_Error {
$this->reset();
return ( new OAuthTokens( $this->credentials ) )->access_token( $rejected );
};
}
/**
* The reason the last `client()` call failed, or null when it simply is not configured.
*/
public function error(): ?WP_Error {
return $this->error;
}
/**
* The API base URL for the selected environment.
*/
public function base_url(): string {
return 'sandbox' === (string) Settings::get( Settings::OPTION_ENVIRONMENT )
? Configuration::SANDBOX_BASE_URL
: Configuration::DEFAULT_BASE_URL;
}
/**
* Drop the memoised client so a credential or environment change takes effect
* within the same request that saved it.
*/
public function reset(): void {
$this->client = null;
$this->error = null;
}
}