Passed
Push — main ( e868ec...9f95cc )
by smiley
12:00
created

MusicBrainz::request()   A

Complexity

Conditions 5
Paths 8

Size

Total Lines 24
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 10
c 1
b 0
f 0
dl 0
loc 24
rs 9.6111
cc 5
nc 8
nop 5
1
<?php
2
/**
3
 * Class MusicBrainz
4
 *
5
 * @created      31.07.2018
6
 * @author       Smiley <[email protected]>
7
 * @copyright    2017 Smiley
8
 * @license      MIT
9
 */
10
11
namespace chillerlan\OAuth\Providers;
12
13
use chillerlan\OAuth\Core\{AccessToken, CSRFToken, OAuth2Provider, ProviderException, TokenRefresh};
14
use chillerlan\HTTP\Utils\QueryUtil;
15
use Psr\Http\Message\ResponseInterface;
16
17
use Psr\Http\Message\StreamInterface;
18
use function date, explode, in_array, sprintf, strtoupper;
19
20
use const PHP_QUERY_RFC1738;
21
22
/**
23
 * @see https://musicbrainz.org/doc/Development
24
 * @see https://musicbrainz.org/doc/Development/OAuth2
25
 */
26
class MusicBrainz extends OAuth2Provider implements CSRFToken, TokenRefresh{
27
28
	const SCOPE_PROFILE               = 'profile';
29
	const SCOPE_EMAIL                 = 'email';
30
	const SCOPE_TAG                   = 'tag';
31
	const SCOPE_RATING                = 'rating';
32
	const SCOPE_COLLECTION            = 'collection';
33
	const SCOPE_SUBMIT_ISRC           = 'submit_isrc';
34
	const SCOPE_SUBMIT_BARCODE        = 'submit_barcode';
35
36
	protected string  $authURL        = 'https://musicbrainz.org/oauth2/authorize';
37
	protected string  $accessTokenURL = 'https://musicbrainz.org/oauth2/token';
38
	protected string  $apiURL         = 'https://musicbrainz.org/ws/2';
39
	protected ?string $userRevokeURL  = 'https://musicbrainz.org/account/applications';
40
	protected ?string $apiDocs        = 'https://musicbrainz.org/doc/Development';
41
	protected ?string $applicationURL = 'https://musicbrainz.org/account/applications';
42
43
	protected array   $defaultScopes  = [
44
		self::SCOPE_PROFILE,
45
		self::SCOPE_EMAIL,
46
		self::SCOPE_TAG,
47
		self::SCOPE_RATING,
48
		self::SCOPE_COLLECTION,
49
	];
50
51
	/**
52
	 * @inheritdoc
53
	 * @throws \chillerlan\OAuth\Core\ProviderException
54
	 */
55
	public function refreshAccessToken(AccessToken $token = null):AccessToken{
56
57
		if($token === null){
58
			$token = $this->storage->getAccessToken($this->serviceName);
59
		}
60
61
		$refreshToken = $token->refreshToken;
62
63
		if(empty($refreshToken)){
64
			throw new ProviderException(
65
				sprintf('no refresh token available, token expired [%s]', date('Y-m-d h:i:s A', $token->expires))
66
			);
67
		}
68
69
		$body = [
70
			'client_id'     => $this->options->key,
71
			'client_secret' => $this->options->secret,
72
			'grant_type'    => 'refresh_token',
73
			'refresh_token' => $refreshToken,
74
		];
75
76
		$request = $this->requestFactory
77
			->createRequest('POST', $this->refreshTokenURL ?? $this->accessTokenURL) // refreshTokenURL is used in tests
78
			->withHeader('Content-Type', 'application/x-www-form-urlencoded')
79
			->withHeader('Accept-Encoding', 'identity')
80
			->withBody($this->streamFactory->createStream(QueryUtil::build($body, PHP_QUERY_RFC1738)))
81
		;
82
83
		$newToken = $this->parseTokenResponse($this->http->sendRequest($request));
84
85
		if(empty($newToken->refreshToken)){
86
			$newToken->refreshToken = $refreshToken;
87
		}
88
89
		$this->storage->storeAccessToken($newToken, $this->serviceName);
90
91
		return $newToken;
92
	}
93
94
	/**
95
	 * @inheritDoc
96
	 */
97
	public function request(
98
		string $path,
99
		array $params = null,
100
		string $method = null,
101
		StreamInterface|array|string $body = null,
102
		array $headers = null
103
	):ResponseInterface{
104
		$params = $params ?? [];
105
		$method = strtoupper($method ?? 'GET');
106
		$token  = $this->storage->getAccessToken($this->serviceName);
107
108
		if($token->isExpired()){
109
			$this->refreshAccessToken($token);
110
		}
111
112
		if(!isset($params['fmt'])){
113
			$params['fmt'] = 'json';
114
		}
115
116
		if(in_array($method, ['POST', 'PUT', 'DELETE']) && !isset($params['client'])){
117
			$params['client'] = $this->options->user_agent; // @codeCoverageIgnore
118
		}
119
120
		return parent::request(explode('?', $path)[0], $params, $method, $body, $headers);
121
	}
122
123
}
124