Use /oauth2/authorise redirect and simpler avatar_url format in /v1/me.
This commit is contained in:
parent
6661830422
commit
2d6c135fad
6 changed files with 71 additions and 178 deletions
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
|||
0.2.2
|
||||
0.3.0
|
||||
|
|
10
composer.lock
generated
10
composer.lock
generated
|
@ -245,16 +245,16 @@
|
|||
},
|
||||
{
|
||||
"name": "phpstan/phpstan",
|
||||
"version": "1.12.10",
|
||||
"version": "1.12.11",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpstan/phpstan.git",
|
||||
"reference": "fc463b5d0fe906dcf19689be692c65c50406a071"
|
||||
"reference": "0d1fc20a962a91be578bcfe7cf939e6e1a2ff733"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/fc463b5d0fe906dcf19689be692c65c50406a071",
|
||||
"reference": "fc463b5d0fe906dcf19689be692c65c50406a071",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/0d1fc20a962a91be578bcfe7cf939e6e1a2ff733",
|
||||
"reference": "0d1fc20a962a91be578bcfe7cf939e6e1a2ff733",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -299,7 +299,7 @@
|
|||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2024-11-11T15:37:09+00:00"
|
||||
"time": "2024-11-17T14:08:01+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/php-code-coverage",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?php declare(strict_types=1);
|
||||
// FlashiiUrls.php
|
||||
// Created: 2024-11-16
|
||||
// Updated: 2024-11-16
|
||||
// Updated: 2024-11-22
|
||||
|
||||
namespace Flashii;
|
||||
|
||||
|
@ -16,30 +16,22 @@ final class FlashiiUrls {
|
|||
*/
|
||||
public const PROD_API_URL = 'https://api.flashii.net';
|
||||
|
||||
/**
|
||||
* Production ID service base URL.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public const PROD_ID_URL = 'https://id.flashii.net';
|
||||
|
||||
/**
|
||||
* @param string $apiUrl API base URL, without trailing /.
|
||||
* @param string $idUrl ID service base URL, without trailing /.
|
||||
*/
|
||||
public function __construct(
|
||||
private readonly string $apiUrl,
|
||||
private readonly string $idUrl
|
||||
private readonly string $apiUrl
|
||||
) {}
|
||||
|
||||
/**
|
||||
* @param string $url
|
||||
* @param string $path
|
||||
* @param array<string, mixed> $args
|
||||
* Retrieves the API base URL.
|
||||
*
|
||||
* @param string $path Path to append to the URL.
|
||||
* @param array<string, mixed> $args Query arguments to append to the URL.
|
||||
* @return string
|
||||
*/
|
||||
private static function buildUrl(string $url, string $path, array $args): string {
|
||||
$url .= $path;
|
||||
public function getApiUrl(string $path = '', array $args = []): string {
|
||||
$url = $this->apiUrl . $path;
|
||||
|
||||
if(!empty($args)) {
|
||||
$url .= str_contains($url, '?') ? '&' : '?';
|
||||
|
@ -49,37 +41,12 @@ final class FlashiiUrls {
|
|||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the API base URL.
|
||||
*
|
||||
* @param string $path Path to append to the URL.
|
||||
* @param array<string, mixed> $args Query arguments to append to the URL.
|
||||
* @return string
|
||||
*/
|
||||
public function getApiUrl(string $path = '', array $args = []): string {
|
||||
return self::buildUrl($this->apiUrl, $path, $args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the ID service base URL.
|
||||
*
|
||||
* @param string $path Path to append to the URL.
|
||||
* @param array<string, mixed> $args Query arguments to append to the URL.
|
||||
* @return string
|
||||
*/
|
||||
public function getIdUrl(string $path = '', array $args = []): string {
|
||||
return self::buildUrl($this->idUrl, $path, $args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance with the production URLs preset.
|
||||
*
|
||||
* @return FlashiiUrls
|
||||
*/
|
||||
public static function production(): self {
|
||||
return new static(
|
||||
self::PROD_API_URL,
|
||||
self::PROD_ID_URL
|
||||
);
|
||||
return new static(self::PROD_API_URL);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?php declare(strict_types=1);
|
||||
// OAuth2Client.php
|
||||
// Created: 2024-11-16
|
||||
// Updated: 2024-11-16
|
||||
// Updated: 2024-11-22
|
||||
|
||||
namespace Flashii\OAuth2;
|
||||
|
||||
|
@ -39,7 +39,7 @@ class OAuth2Client {
|
|||
* @throws InvalidArgumentException If the Credentials instance passed to the constructor is not an instance of BasicCredentials and $clientId was left as null.
|
||||
* @return string Authorisation URL that the user can be redirected to.
|
||||
*/
|
||||
public function createAuthorizeUrl(
|
||||
public function createAuthoriseUrl(
|
||||
string $codeVerifier,
|
||||
?string $state = null,
|
||||
?string $scope = null,
|
||||
|
@ -66,7 +66,37 @@ class OAuth2Client {
|
|||
if($redirectUri !== null)
|
||||
$args['redirect_uri'] = $redirectUri;
|
||||
|
||||
return $this->urls->getIdUrl('/oauth2/authorise', $args);
|
||||
return $this->urls->getApiUrl('/oauth2/authorise', $args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias for createAuthoriseUrl.
|
||||
*
|
||||
* @param string $codeVerifier Code verifier to hash and encode as a code challenge.
|
||||
* @param ?string $state State variable, null to omit.
|
||||
* @param ?string $scope Scope to request, null to omit.
|
||||
* @param ?string $redirectUri URI to redirect to, required if the app has more than one redirect URI registered.
|
||||
* @param ?string $clientId Client ID of the app attempting to authorise.
|
||||
* @param array<string, mixed> $args Additional arguments to add to the URL.
|
||||
* @throws InvalidArgumentException If the Credentials instance passed to the constructor is not an instance of BasicCredentials and $clientId was left as null.
|
||||
* @return string Authorisation URL that the user can be redirected to.
|
||||
*/
|
||||
public function createAuthorizeUrl(
|
||||
string $codeVerifier,
|
||||
?string $state = null,
|
||||
?string $scope = null,
|
||||
?string $redirectUri = null,
|
||||
?string $clientId = null,
|
||||
array $args = []
|
||||
): string {
|
||||
return $this->createAuthoriseUrl(
|
||||
$codeVerifier,
|
||||
$state,
|
||||
$scope,
|
||||
$redirectUri,
|
||||
$clientId,
|
||||
$args
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?php declare(strict_types=1);
|
||||
// V1User.php
|
||||
// Created: 2024-11-16
|
||||
// Updated: 2024-11-21
|
||||
// Updated: 2024-11-22
|
||||
|
||||
namespace Flashii\V1\Users;
|
||||
|
||||
|
@ -12,9 +12,6 @@ use DateTimeInterface;
|
|||
* Represents an APIv1 user.
|
||||
*/
|
||||
final class V1User {
|
||||
/** @var ?V1UserAvatar[] */
|
||||
private ?array $avatarsSorted = null;
|
||||
|
||||
/**
|
||||
* @param string $id User ID.
|
||||
* @param string $name User name.
|
||||
|
@ -29,7 +26,7 @@ final class V1User {
|
|||
* @param DateTimeInterface $createdAt User registration date.
|
||||
* @param ?DateTimeInterface $lastActiveAt Last user activity, null for none or hidden.
|
||||
* @param string $profileUrl Profile URI.
|
||||
* @param V1UserAvatar[] $avatarUrls Avatar URIs.
|
||||
* @param string $avatarUrl Avatar URI.
|
||||
* @param bool $deleted Deleted status.
|
||||
*/
|
||||
public function __construct(
|
||||
|
@ -46,7 +43,7 @@ final class V1User {
|
|||
private readonly DateTimeInterface $createdAt,
|
||||
private readonly ?DateTimeInterface $lastActiveAt,
|
||||
private readonly string $profileUrl,
|
||||
private readonly array $avatarUrls,
|
||||
private readonly string $avatarUrl,
|
||||
private readonly bool $deleted
|
||||
) {}
|
||||
|
||||
|
@ -125,60 +122,29 @@ final class V1User {
|
|||
}
|
||||
|
||||
/**
|
||||
* Gets all avatar URIs.
|
||||
*
|
||||
* @return V1UserAvatar[]
|
||||
*/
|
||||
public function getAvatarUrls(): array {
|
||||
return $this->avatarUrls;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets sorted avatar list URIs without the original URI.
|
||||
*
|
||||
* @return V1UserAvatar[]
|
||||
*/
|
||||
private function getAvatarSorted(): array {
|
||||
if($this->avatarsSorted === null) {
|
||||
$avatars = [];
|
||||
foreach($this->avatarUrls as $avatarUrl)
|
||||
if(!$avatarUrl->isOriginalImage())
|
||||
$avatars[] = $avatarUrl;
|
||||
|
||||
usort($avatars, fn($a, $b) => $a->getResolution() - $b->getResolution());
|
||||
$this->avatarsSorted = $avatars;
|
||||
}
|
||||
|
||||
return $this->avatarsSorted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Selected the most appropriate avatar resolution URI.
|
||||
*
|
||||
* @param int $res Target resolution
|
||||
* @return string Avatar URI, empty if none found.
|
||||
*/
|
||||
public function selectAvatarUrl(int $res): string {
|
||||
$selected = null;
|
||||
$avatars = $this->getAvatarSorted();
|
||||
foreach($avatars as $avatar)
|
||||
if($selected === null || abs($res - $selected->getResolution()) >= abs($avatar->getResolution() - $res))
|
||||
$selected = $avatar;
|
||||
|
||||
return $selected?->getUrl() ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the URI of the original avatar image, empty if none found.
|
||||
* Gets avatar URI.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getOriginalAvatarUrl(): string {
|
||||
foreach($this->avatarUrls as $avatarUrl)
|
||||
if($avatarUrl->isOriginalImage())
|
||||
return $avatarUrl->getUrl();
|
||||
public function getAvatarUrl(): string {
|
||||
return $this->avatarUrl;
|
||||
}
|
||||
|
||||
return '';
|
||||
/**
|
||||
* Attempts to build a URI to a resized avatar.
|
||||
*
|
||||
* If the exact requested size is unavailable, the closest supported resized image is returned.
|
||||
*
|
||||
* @param int $dims Desired width and height.
|
||||
* @return string Resized avatar URI.
|
||||
*/
|
||||
public function getResizedAvatarUrl(int $dims): string {
|
||||
return sprintf(
|
||||
'%s%sres=%d',
|
||||
$this->avatarUrl,
|
||||
str_contains($this->avatarUrl, '?') ? '&' : '?',
|
||||
$dims
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -284,7 +250,7 @@ final class V1User {
|
|||
new DateTimeImmutable(isset($info->created_at) && is_string($info->created_at) ? $info->created_at : '@0'),
|
||||
isset($info->last_active_at) && is_string($info->last_active_at) ? new DateTimeImmutable($info->last_active_at) : null,
|
||||
isset($info->profile_url) && is_string($info->profile_url) ? $info->profile_url : '',
|
||||
isset($info->avatar_urls) && is_array($info->avatar_urls) ? array_map(fn(mixed $item) => (is_object($item) ? V1UserAvatar::decode($item) : V1UserAvatar::empty()), $info->avatar_urls) : [],
|
||||
isset($info->avatar_url) && is_string($info->avatar_url) ? $info->avatar_url : '',
|
||||
isset($info->is_deleted) && is_bool($info->is_deleted) ? $info->is_deleted : false
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,70 +0,0 @@
|
|||
<?php declare(strict_types=1);
|
||||
// V1UserAvatar.php
|
||||
// Created: 2024-11-16
|
||||
// Updated: 2024-11-16
|
||||
|
||||
namespace Flashii\V1\Users;
|
||||
|
||||
/**
|
||||
* Represents an APIv1 user avatar.
|
||||
*/
|
||||
final class V1UserAvatar {
|
||||
/**
|
||||
* @param int $res Width and height value of the avatar image.
|
||||
* @param string $url URI of the avatar image.
|
||||
*/
|
||||
public function __construct(
|
||||
private readonly int $res,
|
||||
private readonly string $url
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Checks whether this avatar image is the original upload (not guaranteed to be square).
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isOriginalImage(): bool {
|
||||
return $this->res === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the width and height value of the avatar image.
|
||||
* 0 for the original image, possibly not a square.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getResolution(): int {
|
||||
return $this->res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets URI of the avatar image.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUrl(): string {
|
||||
return $this->url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes an object into this proper object.
|
||||
*
|
||||
* @param object $info
|
||||
* @return V1UserAvatar
|
||||
*/
|
||||
public static function decode(object $info): self {
|
||||
return new static(
|
||||
isset($info->res) && is_int($info->res) ? $info->res : 0,
|
||||
isset($info->url) && is_string($info->url) ? $info->url : ''
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a blank emote instance.
|
||||
*
|
||||
* @return V1UserAvatar
|
||||
*/
|
||||
public static function empty(): self {
|
||||
return new static(0, '');
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue