Use /oauth2/authorise redirect and simpler avatar_url format in /v1/me.

This commit is contained in:
Pachira 2024-11-22 21:36:01 +00:00
parent 6661830422
commit 2d6c135fad
6 changed files with 71 additions and 178 deletions

View file

@ -1 +1 @@
0.2.2
0.3.0

10
composer.lock generated
View file

@ -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",

View file

@ -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);
}
}

View file

@ -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
);
}
/**

View file

@ -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
);
}

View file

@ -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, '');
}
}