mirror of
https://github.com/appwrite/appwrite.git
synced 2026-05-26 13:51:13 +00:00
Merge branch 'appwrite:master' into feat-etsy-auth
This commit is contained in:
+70
-35
@@ -10,38 +10,41 @@ class Auth
|
||||
/**
|
||||
* User Roles.
|
||||
*/
|
||||
const USER_ROLE_ALL = 'all';
|
||||
const USER_ROLE_GUEST = 'guest';
|
||||
const USER_ROLE_MEMBER = 'member';
|
||||
const USER_ROLE_ADMIN = 'admin';
|
||||
const USER_ROLE_DEVELOPER = 'developer';
|
||||
const USER_ROLE_OWNER = 'owner';
|
||||
const USER_ROLE_APP = 'app';
|
||||
const USER_ROLE_SYSTEM = 'system';
|
||||
public const USER_ROLE_ALL = 'all';
|
||||
public const USER_ROLE_GUEST = 'guest';
|
||||
public const USER_ROLE_MEMBER = 'member';
|
||||
public const USER_ROLE_ADMIN = 'admin';
|
||||
public const USER_ROLE_DEVELOPER = 'developer';
|
||||
public const USER_ROLE_OWNER = 'owner';
|
||||
public const USER_ROLE_APP = 'app';
|
||||
public const USER_ROLE_SYSTEM = 'system';
|
||||
|
||||
/**
|
||||
* Token Types.
|
||||
*/
|
||||
const TOKEN_TYPE_LOGIN = 1; // Deprecated
|
||||
const TOKEN_TYPE_VERIFICATION = 2;
|
||||
const TOKEN_TYPE_RECOVERY = 3;
|
||||
const TOKEN_TYPE_INVITE = 4;
|
||||
const TOKEN_TYPE_MAGIC_URL = 5;
|
||||
public const TOKEN_TYPE_LOGIN = 1; // Deprecated
|
||||
public const TOKEN_TYPE_VERIFICATION = 2;
|
||||
public const TOKEN_TYPE_RECOVERY = 3;
|
||||
public const TOKEN_TYPE_INVITE = 4;
|
||||
public const TOKEN_TYPE_MAGIC_URL = 5;
|
||||
public const TOKEN_TYPE_PHONE = 6;
|
||||
|
||||
/**
|
||||
* Session Providers.
|
||||
*/
|
||||
const SESSION_PROVIDER_EMAIL = 'email';
|
||||
const SESSION_PROVIDER_ANONYMOUS = 'anonymous';
|
||||
const SESSION_PROVIDER_MAGIC_URL = 'magic-url';
|
||||
public const SESSION_PROVIDER_EMAIL = 'email';
|
||||
public const SESSION_PROVIDER_ANONYMOUS = 'anonymous';
|
||||
public const SESSION_PROVIDER_MAGIC_URL = 'magic-url';
|
||||
public const SESSION_PROVIDER_PHONE = 'phone';
|
||||
|
||||
/**
|
||||
* Token Expiration times.
|
||||
*/
|
||||
const TOKEN_EXPIRATION_LOGIN_LONG = 31536000; /* 1 year */
|
||||
const TOKEN_EXPIRATION_LOGIN_SHORT = 3600; /* 1 hour */
|
||||
const TOKEN_EXPIRATION_RECOVERY = 3600; /* 1 hour */
|
||||
const TOKEN_EXPIRATION_CONFIRM = 3600 * 24 * 7; /* 7 days */
|
||||
public const TOKEN_EXPIRATION_LOGIN_LONG = 31536000; /* 1 year */
|
||||
public const TOKEN_EXPIRATION_LOGIN_SHORT = 3600; /* 1 hour */
|
||||
public const TOKEN_EXPIRATION_RECOVERY = 3600; /* 1 hour */
|
||||
public const TOKEN_EXPIRATION_CONFIRM = 3600 * 24 * 7; /* 7 days */
|
||||
public const TOKEN_EXPIRATION_PHONE = 60 * 15; /* 15 minutes */
|
||||
|
||||
/**
|
||||
* @var string
|
||||
@@ -163,7 +166,7 @@ class Auth
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function passwordGenerator(int $length = 20):string
|
||||
public static function passwordGenerator(int $length = 20): string
|
||||
{
|
||||
return \bin2hex(\random_bytes($length));
|
||||
}
|
||||
@@ -179,7 +182,7 @@ class Auth
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function tokenGenerator(int $length = 128):string
|
||||
public static function tokenGenerator(int $length = 128): string
|
||||
{
|
||||
return \bin2hex(\random_bytes($length));
|
||||
}
|
||||
@@ -195,13 +198,16 @@ class Auth
|
||||
*/
|
||||
public static function tokenVerify(array $tokens, int $type, string $secret)
|
||||
{
|
||||
foreach ($tokens as $token) { /** @var Document $token */
|
||||
if ($token->isSet('type') &&
|
||||
foreach ($tokens as $token) {
|
||||
/** @var Document $token */
|
||||
if (
|
||||
$token->isSet('type') &&
|
||||
$token->isSet('secret') &&
|
||||
$token->isSet('expire') &&
|
||||
$token->getAttribute('type') == $type &&
|
||||
$token->getAttribute('secret') === self::hash($secret) &&
|
||||
$token->getAttribute('expire') >= \time()) {
|
||||
$token->getAttribute('expire') >= \time()
|
||||
) {
|
||||
return (string)$token->getId();
|
||||
}
|
||||
}
|
||||
@@ -209,6 +215,25 @@ class Auth
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function phoneTokenVerify(array $tokens, string $secret)
|
||||
{
|
||||
foreach ($tokens as $token) {
|
||||
/** @var Document $token */
|
||||
if (
|
||||
$token->isSet('type') &&
|
||||
$token->isSet('secret') &&
|
||||
$token->isSet('expire') &&
|
||||
$token->getAttribute('type') == Auth::TOKEN_TYPE_PHONE &&
|
||||
$token->getAttribute('secret') === $secret &&
|
||||
$token->getAttribute('expire') >= \time()
|
||||
) {
|
||||
return (string) $token->getId();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify session and check that its not expired.
|
||||
*
|
||||
@@ -219,12 +244,15 @@ class Auth
|
||||
*/
|
||||
public static function sessionVerify(array $sessions, string $secret)
|
||||
{
|
||||
foreach ($sessions as $session) { /** @var Document $session */
|
||||
if ($session->isSet('secret') &&
|
||||
foreach ($sessions as $session) {
|
||||
/** @var Document $session */
|
||||
if (
|
||||
$session->isSet('secret') &&
|
||||
$session->isSet('expire') &&
|
||||
$session->isSet('provider') &&
|
||||
$session->getAttribute('secret') === self::hash($secret) &&
|
||||
$session->getAttribute('expire') >= \time()) {
|
||||
$session->getAttribute('expire') >= \time()
|
||||
) {
|
||||
return (string)$session->getId();
|
||||
}
|
||||
}
|
||||
@@ -242,9 +270,9 @@ class Auth
|
||||
public static function isPrivilegedUser(array $roles): bool
|
||||
{
|
||||
if (
|
||||
in_array('role:'.self::USER_ROLE_OWNER, $roles) ||
|
||||
in_array('role:'.self::USER_ROLE_DEVELOPER, $roles) ||
|
||||
in_array('role:'.self::USER_ROLE_ADMIN, $roles)
|
||||
in_array('role:' . self::USER_ROLE_OWNER, $roles) ||
|
||||
in_array('role:' . self::USER_ROLE_DEVELOPER, $roles) ||
|
||||
in_array('role:' . self::USER_ROLE_ADMIN, $roles)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
@@ -261,7 +289,7 @@ class Auth
|
||||
*/
|
||||
public static function isAppUser(array $roles): bool
|
||||
{
|
||||
if (in_array('role:'.self::USER_ROLE_APP, $roles)) {
|
||||
if (in_array('role:' . self::USER_ROLE_APP, $roles)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -280,10 +308,10 @@ class Auth
|
||||
|
||||
if (!self::isPrivilegedUser(Authorization::getRoles()) && !self::isAppUser(Authorization::getRoles())) {
|
||||
if ($user->getId()) {
|
||||
$roles[] = 'user:'.$user->getId();
|
||||
$roles[] = 'role:'.Auth::USER_ROLE_MEMBER;
|
||||
$roles[] = 'user:' . $user->getId();
|
||||
$roles[] = 'role:' . Auth::USER_ROLE_MEMBER;
|
||||
} else {
|
||||
return ['role:'.Auth::USER_ROLE_GUEST];
|
||||
return ['role:' . Auth::USER_ROLE_GUEST];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -299,4 +327,11 @@ class Auth
|
||||
|
||||
return $roles;
|
||||
}
|
||||
|
||||
public static function isAnonymousUser(Document $user): bool
|
||||
{
|
||||
return (is_null($user->getAttribute('email'))
|
||||
|| is_null($user->getAttribute('phone'))
|
||||
) && is_null($user->getAttribute('password'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,27 +7,27 @@ abstract class OAuth2
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $appID;
|
||||
protected string $appID;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $appSecret;
|
||||
protected string $appSecret;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $callback;
|
||||
protected string $callback;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $state;
|
||||
protected array $state;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $scopes;
|
||||
protected array $scopes;
|
||||
|
||||
/**
|
||||
* OAuth2 constructor.
|
||||
@@ -52,66 +52,69 @@ abstract class OAuth2
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
abstract public function getName():string;
|
||||
abstract public function getName(): string;
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
abstract public function getLoginURL():string;
|
||||
abstract public function getLoginURL(): string;
|
||||
|
||||
/**
|
||||
* @param string $code
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
abstract protected function getTokens(string $code):array;
|
||||
abstract protected function getTokens(string $code): array;
|
||||
|
||||
/**
|
||||
* @param string $refreshToken
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
abstract public function refreshTokens(string $refreshToken):array;
|
||||
abstract public function refreshTokens(string $refreshToken): array;
|
||||
|
||||
/**
|
||||
* @param $accessToken
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract public function getUserID(string $accessToken):string;
|
||||
abstract public function getUserEmail(string $accessToken): string;
|
||||
|
||||
/**
|
||||
* @param $accessToken
|
||||
* Check if the OAuth email is verified
|
||||
*
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
abstract public function isEmailVerified(string $accessToken): bool;
|
||||
|
||||
/**
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract public function getUserEmail(string $accessToken):string;
|
||||
|
||||
/**
|
||||
* @param $accessToken
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract public function getUserName(string $accessToken):string;
|
||||
abstract public function getUserName(string $accessToken): string;
|
||||
|
||||
/**
|
||||
* @param $scope
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
protected function addScope(string $scope):OAuth2
|
||||
protected function addScope(string $scope): OAuth2
|
||||
{
|
||||
// Add a scope to the scopes array if it isn't already present
|
||||
if (!\in_array($scope, $this->scopes)) {
|
||||
$this->scopes[] = $scope;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
protected function getScopes():array
|
||||
protected function getScopes(): array
|
||||
{
|
||||
return $this->scopes;
|
||||
}
|
||||
@@ -121,9 +124,10 @@ abstract class OAuth2
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getAccessToken(string $code):string
|
||||
public function getAccessToken(string $code): string
|
||||
{
|
||||
$tokens = $this->getTokens($code);
|
||||
|
||||
return $tokens['access_token'] ?? '';
|
||||
}
|
||||
|
||||
@@ -132,9 +136,10 @@ abstract class OAuth2
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getRefreshToken(string $code):string
|
||||
public function getRefreshToken(string $code): string
|
||||
{
|
||||
$tokens = $this->getTokens($code);
|
||||
|
||||
return $tokens['refresh_token'] ?? '';
|
||||
}
|
||||
|
||||
@@ -143,9 +148,10 @@ abstract class OAuth2
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getAccessTokenExpiry(string $code):string
|
||||
public function getAccessTokenExpiry(string $code): string
|
||||
{
|
||||
$tokens = $this->getTokens($code);
|
||||
|
||||
return $tokens['expires_in'] ?? '';
|
||||
}
|
||||
|
||||
@@ -170,7 +176,7 @@ abstract class OAuth2
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function request(string $method, string $url = '', array $headers = [], string $payload = ''):string
|
||||
protected function request(string $method, string $url = '', array $headers = [], string $payload = ''): string
|
||||
{
|
||||
$ch = \curl_init($url);
|
||||
|
||||
@@ -183,7 +189,7 @@ abstract class OAuth2
|
||||
\curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
|
||||
}
|
||||
|
||||
$headers[] = 'Content-length: '.\strlen($payload);
|
||||
$headers[] = 'Content-length: ' . \strlen($payload);
|
||||
\curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
|
||||
|
||||
// Send the request & save response to $response
|
||||
|
||||
@@ -14,17 +14,17 @@ class Amazon extends OAuth2
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $user = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $tokens = [];
|
||||
protected array $user = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $scopes = [
|
||||
protected array $tokens = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $scopes = [
|
||||
"profile"
|
||||
];
|
||||
|
||||
@@ -37,7 +37,7 @@ class Amazon extends OAuth2
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $state
|
||||
* @param string $state
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
@@ -52,13 +52,13 @@ class Amazon extends OAuth2
|
||||
*/
|
||||
public function getLoginURL(): string
|
||||
{
|
||||
return 'https://www.amazon.com/ap/oa?'.\http_build_query([
|
||||
'response_type' => 'code',
|
||||
'client_id' => $this->appID,
|
||||
'scope' => \implode(' ', $this->getScopes()),
|
||||
'state' => \json_encode($this->state),
|
||||
'redirect_uri' => $this->callback
|
||||
]);
|
||||
return 'https://www.amazon.com/ap/oa?' . \http_build_query([
|
||||
'response_type' => 'code',
|
||||
'client_id' => $this->appID,
|
||||
'scope' => \implode(' ', $this->getScopes()),
|
||||
'state' => \json_encode($this->state),
|
||||
'redirect_uri' => $this->callback
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -68,7 +68,7 @@ class Amazon extends OAuth2
|
||||
*/
|
||||
protected function getTokens(string $code): array
|
||||
{
|
||||
if(empty($this->tokens)) {
|
||||
if (empty($this->tokens)) {
|
||||
$headers = ['Content-Type: application/x-www-form-urlencoded;charset=UTF-8'];
|
||||
$this->tokens = \json_decode($this->request(
|
||||
'POST',
|
||||
@@ -92,7 +92,7 @@ class Amazon extends OAuth2
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function refreshTokens(string $refreshToken):array
|
||||
public function refreshTokens(string $refreshToken): array
|
||||
{
|
||||
$headers = ['Content-Type: application/x-www-form-urlencoded;charset=UTF-8'];
|
||||
$this->tokens = \json_decode($this->request(
|
||||
@@ -107,7 +107,7 @@ class Amazon extends OAuth2
|
||||
])
|
||||
), true);
|
||||
|
||||
if(empty($this->tokens['refresh_token'])) {
|
||||
if (empty($this->tokens['refresh_token'])) {
|
||||
$this->tokens['refresh_token'] = $refreshToken;
|
||||
}
|
||||
|
||||
@@ -123,11 +123,7 @@ class Amazon extends OAuth2
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if (isset($user['user_id'])) {
|
||||
return $user['user_id'];
|
||||
}
|
||||
|
||||
return '';
|
||||
return $user['user_id'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -139,11 +135,23 @@ class Amazon extends OAuth2
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if (isset($user['email'])) {
|
||||
return $user['email'];
|
||||
}
|
||||
return $user['email'] ?? '';
|
||||
}
|
||||
|
||||
return '';
|
||||
/**
|
||||
* Check if the OAuth email is verified
|
||||
*
|
||||
* If present, the email is verified. This was verfied through a manual Amazon sign up process
|
||||
*
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmailVerified(string $accessToken): bool
|
||||
{
|
||||
$email = $this->getUserEmail($accessToken);
|
||||
|
||||
return !empty($email);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -155,11 +163,7 @@ class Amazon extends OAuth2
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if (isset($user['name'])) {
|
||||
return $user['name'];
|
||||
}
|
||||
|
||||
return '';
|
||||
return $user['name'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -170,7 +174,7 @@ class Amazon extends OAuth2
|
||||
protected function getUser(string $accessToken): array
|
||||
{
|
||||
if (empty($this->user)) {
|
||||
$user = $this->request('GET', 'https://api.amazon.com/user/profile?access_token='.\urlencode($accessToken));
|
||||
$user = $this->request('GET', 'https://api.amazon.com/user/profile?access_token=' . \urlencode($accessToken));
|
||||
$this->user = \json_decode($user, true);
|
||||
}
|
||||
return $this->user;
|
||||
|
||||
@@ -13,17 +13,17 @@ class Apple extends OAuth2
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $user = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $tokens = [];
|
||||
protected array $user = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $scopes = [
|
||||
protected array $tokens = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $scopes = [
|
||||
"name",
|
||||
"email"
|
||||
];
|
||||
@@ -31,7 +31,7 @@ class Apple extends OAuth2
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $claims = [];
|
||||
protected array $claims = [];
|
||||
|
||||
/**
|
||||
* @return string
|
||||
@@ -40,13 +40,13 @@ class Apple extends OAuth2
|
||||
{
|
||||
return 'apple';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getLoginURL(): string
|
||||
{
|
||||
return 'https://appleid.apple.com/auth/authorize?'.\http_build_query([
|
||||
return 'https://appleid.apple.com/auth/authorize?' . \http_build_query([
|
||||
'client_id' => $this->appID,
|
||||
'redirect_uri' => $this->callback,
|
||||
'state' => \json_encode($this->state),
|
||||
@@ -63,7 +63,7 @@ class Apple extends OAuth2
|
||||
*/
|
||||
protected function getTokens(string $code): array
|
||||
{
|
||||
if(empty($this->tokens)) {
|
||||
if (empty($this->tokens)) {
|
||||
$headers = ['Content-Type: application/x-www-form-urlencoded'];
|
||||
$this->tokens = \json_decode($this->request(
|
||||
'POST',
|
||||
@@ -90,7 +90,7 @@ class Apple extends OAuth2
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function refreshTokens(string $refreshToken):array
|
||||
public function refreshTokens(string $refreshToken): array
|
||||
{
|
||||
$headers = ['Content-Type: application/x-www-form-urlencoded'];
|
||||
$this->tokens = \json_decode($this->request(
|
||||
@@ -105,7 +105,7 @@ class Apple extends OAuth2
|
||||
])
|
||||
), true);
|
||||
|
||||
if(empty($this->tokens['refresh_token'])) {
|
||||
if (empty($this->tokens['refresh_token'])) {
|
||||
$this->tokens['refresh_token'] = $refreshToken;
|
||||
}
|
||||
|
||||
@@ -122,11 +122,7 @@ class Apple extends OAuth2
|
||||
*/
|
||||
public function getUserID(string $accessToken): string
|
||||
{
|
||||
if (isset($this->claims['sub']) && !empty($this->claims['sub'])) {
|
||||
return $this->claims['sub'];
|
||||
}
|
||||
|
||||
return '';
|
||||
return $this->claims['sub'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -136,14 +132,25 @@ class Apple extends OAuth2
|
||||
*/
|
||||
public function getUserEmail(string $accessToken): string
|
||||
{
|
||||
if (isset($this->claims['email']) &&
|
||||
!empty($this->claims['email']) &&
|
||||
isset($this->claims['email_verified']) &&
|
||||
$this->claims['email_verified'] === 'true') {
|
||||
return $this->claims['email'];
|
||||
return $this->claims['email'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the OAuth email is verified
|
||||
*
|
||||
* @link https://developer.apple.com/forums/thread/121411
|
||||
*
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmailVerified(string $accessToken): bool
|
||||
{
|
||||
if ($this->claims['email_verified'] ?? false) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return '';
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -153,17 +160,19 @@ class Apple extends OAuth2
|
||||
*/
|
||||
public function getUserName(string $accessToken): string
|
||||
{
|
||||
if (isset($this->claims['email']) &&
|
||||
if (
|
||||
isset($this->claims['email']) &&
|
||||
!empty($this->claims['email']) &&
|
||||
isset($this->claims['email_verified']) &&
|
||||
$this->claims['email_verified'] === 'true') {
|
||||
$this->claims['email_verified'] === 'true'
|
||||
) {
|
||||
return $this->claims['email'];
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
protected function getAppSecret():string
|
||||
protected function getAppSecret(): string
|
||||
{
|
||||
try {
|
||||
$secret = \json_decode($this->appSecret, true);
|
||||
@@ -180,18 +189,18 @@ class Apple extends OAuth2
|
||||
'alg' => 'ES256',
|
||||
'kid' => $keyID,
|
||||
];
|
||||
|
||||
|
||||
$claims = [
|
||||
'iss' => $teamID,
|
||||
'iat' => \time(),
|
||||
'exp' => \time() + 86400*180,
|
||||
'exp' => \time() + 86400 * 180,
|
||||
'aud' => 'https://appleid.apple.com',
|
||||
'sub' => $bundleID,
|
||||
];
|
||||
|
||||
$pkey = \openssl_pkey_get_private($keyfile);
|
||||
|
||||
$payload = $this->encode(\json_encode($headers)).'.'.$this->encode(\json_encode($claims));
|
||||
$payload = $this->encode(\json_encode($headers)) . '.' . $this->encode(\json_encode($claims));
|
||||
|
||||
$signature = '';
|
||||
|
||||
@@ -201,7 +210,7 @@ class Apple extends OAuth2
|
||||
return '';
|
||||
}
|
||||
|
||||
return $payload.'.'.$this->encode($this->fromDER($signature, 64));
|
||||
return $payload . '.' . $this->encode($this->fromDER($signature, 64));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -230,10 +239,10 @@ class Apple extends OAuth2
|
||||
* @param string $der
|
||||
* @param int $partLength
|
||||
*/
|
||||
protected function fromDER(string $der, int $partLength):string
|
||||
protected function fromDER(string $der, int $partLength): string
|
||||
{
|
||||
$hex = \unpack('H*', $der)[1];
|
||||
|
||||
|
||||
if ('30' !== \mb_substr($hex, 0, 2, '8bit')) { // SEQUENCE
|
||||
throw new \RuntimeException();
|
||||
}
|
||||
@@ -252,7 +261,7 @@ class Apple extends OAuth2
|
||||
$R = \str_pad($R, $partLength, '0', STR_PAD_LEFT);
|
||||
|
||||
$hex = \mb_substr($hex, 4 + $Rl * 2, null, '8bit');
|
||||
|
||||
|
||||
if ('02' !== \mb_substr($hex, 0, 2, '8bit')) { // INTEGER
|
||||
throw new \RuntimeException();
|
||||
}
|
||||
@@ -261,6 +270,6 @@ class Apple extends OAuth2
|
||||
$S = $this->retrievePositiveInteger(\mb_substr($hex, 4, $Sl * 2, '8bit'));
|
||||
$S = \str_pad($S, $partLength, '0', STR_PAD_LEFT);
|
||||
|
||||
return \pack('H*', $R.$S);
|
||||
return \pack('H*', $R . $S);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,219 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Auth\OAuth2;
|
||||
|
||||
use Appwrite\Auth\OAuth2;
|
||||
|
||||
// Reference Material
|
||||
// https://auth0.com/docs/api/authentication
|
||||
|
||||
class Auth0 extends OAuth2
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $scopes = [
|
||||
'openid',
|
||||
'profile',
|
||||
'email',
|
||||
'offline_access'
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $user = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $tokens = [];
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getName(): string
|
||||
{
|
||||
return 'auth0';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getLoginURL(): string
|
||||
{
|
||||
return 'https://' . $this->getAuth0Domain() . '/authorize?' . \http_build_query([
|
||||
'client_id' => $this->appID,
|
||||
'redirect_uri' => $this->callback,
|
||||
'state' => \json_encode($this->state),
|
||||
'scope' => \implode(' ', $this->getScopes()),
|
||||
'response_type' => 'code'
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $code
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getTokens(string $code): array
|
||||
{
|
||||
if (empty($this->tokens)) {
|
||||
$headers = ['Content-Type: application/x-www-form-urlencoded'];
|
||||
$this->tokens = \json_decode($this->request(
|
||||
'POST',
|
||||
'https://' . $this->getAuth0Domain() . '/oauth/token',
|
||||
$headers,
|
||||
\http_build_query([
|
||||
'code' => $code,
|
||||
'client_id' => $this->appID,
|
||||
'client_secret' => $this->getClientSecret(),
|
||||
'redirect_uri' => $this->callback,
|
||||
'scope' => \implode(' ', $this->getScopes()),
|
||||
'grant_type' => 'authorization_code'
|
||||
])
|
||||
), true);
|
||||
}
|
||||
|
||||
return $this->tokens;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $refreshToken
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function refreshTokens(string $refreshToken): array
|
||||
{
|
||||
$headers = ['Content-Type: application/x-www-form-urlencoded'];
|
||||
$this->tokens = \json_decode($this->request(
|
||||
'POST',
|
||||
'https://' . $this->getAuth0Domain() . '/oauth/token',
|
||||
$headers,
|
||||
\http_build_query([
|
||||
'refresh_token' => $refreshToken,
|
||||
'client_id' => $this->appID,
|
||||
'client_secret' => $this->getClientSecret(),
|
||||
'grant_type' => 'refresh_token'
|
||||
])
|
||||
), true);
|
||||
|
||||
if (empty($this->tokens['refresh_token'])) {
|
||||
$this->tokens['refresh_token'] = $refreshToken;
|
||||
}
|
||||
|
||||
return $this->tokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUserID(string $accessToken): string
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
return $user['sub'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUserEmail(string $accessToken): string
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
return $user['email'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the OAuth email is verified
|
||||
*
|
||||
* @link https://auth0.com/docs/api/authentication?javascript#user-profile
|
||||
*
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmailVerified(string $accessToken): bool
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if ($user['email_verified'] ?? false) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUserName(string $accessToken): string
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
return $user['name'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getUser(string $accessToken): array
|
||||
{
|
||||
if (empty($this->user)) {
|
||||
$headers = ['Authorization: Bearer ' . \urlencode($accessToken)];
|
||||
$user = $this->request('GET', 'https://' . $this->getAuth0Domain() . '/userinfo', $headers);
|
||||
$this->user = \json_decode($user, true);
|
||||
}
|
||||
|
||||
return $this->user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the Client Secret from the JSON stored in appSecret
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getClientSecret(): string
|
||||
{
|
||||
$secret = $this->getAppSecret();
|
||||
|
||||
return $secret['clientSecret'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the Auth0 Domain from the JSON stored in appSecret
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getAuth0Domain(): string
|
||||
{
|
||||
$secret = $this->getAppSecret();
|
||||
|
||||
return $secret['auth0Domain'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode the JSON stored in appSecret
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getAppSecret(): array
|
||||
{
|
||||
try {
|
||||
$secret = \json_decode($this->appSecret, true, 512, JSON_THROW_ON_ERROR);
|
||||
} catch (\Throwable $th) {
|
||||
throw new \Exception('Invalid secret');
|
||||
}
|
||||
return $secret;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Auth\OAuth2;
|
||||
|
||||
use Appwrite\Auth\OAuth2;
|
||||
|
||||
class Autodesk extends OAuth2
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $user = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $tokens = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $scopes = [
|
||||
'user-profile:read',
|
||||
];
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getName(): string
|
||||
{
|
||||
return 'autodesk';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getLoginURL(): string
|
||||
{
|
||||
return 'https://developer.api.autodesk.com/authentication/v1/authorize?' . \http_build_query([
|
||||
'client_id' => $this->appID,
|
||||
'scope' => \implode(' ', $this->getScopes()),
|
||||
'state' => \json_encode($this->state),
|
||||
'redirect_uri' => $this->callback,
|
||||
'response_type' => 'code'
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $code
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getTokens(string $code): array
|
||||
{
|
||||
if (empty($this->tokens)) {
|
||||
$headers = ['Content-Type: application/x-www-form-urlencoded'];
|
||||
$response = $this->request(
|
||||
'POST',
|
||||
'https://developer.api.autodesk.com/authentication/v1/gettoken',
|
||||
$headers,
|
||||
\http_build_query([
|
||||
'client_id' => $this->appID,
|
||||
'redirect_uri' => $this->callback,
|
||||
'client_secret' => $this->appSecret,
|
||||
'code' => $code,
|
||||
'grant_type' => 'authorization_code'
|
||||
])
|
||||
);
|
||||
|
||||
$this->tokens = \json_decode($response, true);
|
||||
}
|
||||
|
||||
return $this->tokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $refreshToken
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function refreshTokens(string $refreshToken): array
|
||||
{
|
||||
$response = $this->request(
|
||||
'POST',
|
||||
'https://developer.api.autodesk.com/authentication/v1/refreshtoken',
|
||||
[],
|
||||
\http_build_query([
|
||||
'client_id' => $this->appID,
|
||||
'client_secret' => $this->appSecret,
|
||||
'grant_type' => 'refresh_token',
|
||||
'code' => $code,
|
||||
'redirect_uri' => $this->callback,
|
||||
])
|
||||
);
|
||||
|
||||
$output = [];
|
||||
\parse_str($response, $output);
|
||||
$this->tokens = $output;
|
||||
|
||||
if (empty($this->tokens['refresh_token'])) {
|
||||
$this->tokens['refresh_token'] = $refreshToken;
|
||||
}
|
||||
|
||||
return $this->tokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUserID(string $accessToken): string
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
return $user['userId'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUserEmail(string $accessToken): string
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
return $user['emailId'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the OAuth email is verified
|
||||
*
|
||||
* @link https://docs.github.com/en/rest/users/emails#list-email-addresses-for-the-authenticated-user
|
||||
*
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmailVerified(string $accessToken): bool
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if ($user['emailVerified'] ?? false) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUserName(string $accessToken): string
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
return $user['userName'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getUser(string $accessToken): array
|
||||
{
|
||||
if (empty($this->user)) {
|
||||
$headers = ['Authorization: Bearer ' . \urlencode($accessToken)];
|
||||
$user = $this->request('GET', 'https://developer.api.autodesk.com/userprofile/v1/users/@me', $headers);
|
||||
$this->user = \json_decode($user, true);
|
||||
}
|
||||
|
||||
return $this->user;
|
||||
}
|
||||
}
|
||||
@@ -12,17 +12,17 @@ class Bitbucket extends OAuth2
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $user = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $tokens = [];
|
||||
protected array $user = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $scopes = [];
|
||||
protected array $tokens = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $scopes = [];
|
||||
|
||||
/**
|
||||
* @return string
|
||||
@@ -37,12 +37,12 @@ class Bitbucket extends OAuth2
|
||||
*/
|
||||
public function getLoginURL(): string
|
||||
{
|
||||
return 'https://bitbucket.org/site/oauth2/authorize?'.\http_build_query([
|
||||
'response_type' => 'code',
|
||||
'client_id' => $this->appID,
|
||||
'scope' => \implode(' ', $this->getScopes()),
|
||||
'state' => \json_encode($this->state),
|
||||
]);
|
||||
return 'https://bitbucket.org/site/oauth2/authorize?' . \http_build_query([
|
||||
'response_type' => 'code',
|
||||
'client_id' => $this->appID,
|
||||
'scope' => \implode(' ', $this->getScopes()),
|
||||
'state' => \json_encode($this->state),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -52,7 +52,7 @@ class Bitbucket extends OAuth2
|
||||
*/
|
||||
protected function getTokens(string $code): array
|
||||
{
|
||||
if(empty($this->tokens)) {
|
||||
if (empty($this->tokens)) {
|
||||
// Required as per Bitbucket Spec.
|
||||
$headers = ['Content-Type: application/x-www-form-urlencoded'];
|
||||
$this->tokens = \json_decode($this->request(
|
||||
@@ -76,7 +76,7 @@ class Bitbucket extends OAuth2
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function refreshTokens(string $refreshToken):array
|
||||
public function refreshTokens(string $refreshToken): array
|
||||
{
|
||||
$headers = ['Content-Type: application/x-www-form-urlencoded'];
|
||||
$this->tokens = \json_decode($this->request(
|
||||
@@ -91,7 +91,7 @@ class Bitbucket extends OAuth2
|
||||
])
|
||||
), true);
|
||||
|
||||
if(empty($this->tokens['refresh_token'])) {
|
||||
if (empty($this->tokens['refresh_token'])) {
|
||||
$this->tokens['refresh_token'] = $refreshToken;
|
||||
}
|
||||
|
||||
@@ -107,11 +107,7 @@ class Bitbucket extends OAuth2
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if (isset($user['uuid'])) {
|
||||
return $user['uuid'];
|
||||
}
|
||||
|
||||
return '';
|
||||
return $user['uuid'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -123,11 +119,25 @@ class Bitbucket extends OAuth2
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if (isset($user['email'])) {
|
||||
return $user['email'];
|
||||
return $user['email'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the OAuth email is verified
|
||||
*
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmailVerified(string $accessToken): bool
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if ($user['is_confirmed'] ?? false) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return '';
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -139,11 +149,7 @@ class Bitbucket extends OAuth2
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if (isset($user['display_name'])) {
|
||||
return $user['display_name'];
|
||||
}
|
||||
|
||||
return '';
|
||||
return $user['display_name'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -154,11 +160,20 @@ class Bitbucket extends OAuth2
|
||||
protected function getUser(string $accessToken): array
|
||||
{
|
||||
if (empty($this->user)) {
|
||||
$user = $this->request('GET', 'https://api.bitbucket.org/2.0/user?access_token='.\urlencode($accessToken));
|
||||
$user = $this->request('GET', 'https://api.bitbucket.org/2.0/user?access_token=' . \urlencode($accessToken));
|
||||
$this->user = \json_decode($user, true);
|
||||
|
||||
$email = $this->request('GET', 'https://api.bitbucket.org/2.0/user/emails?access_token='.\urlencode($accessToken));
|
||||
$this->user['email'] = \json_decode($email, true)['values'][0]['email'];
|
||||
$emails = $this->request('GET', 'https://api.bitbucket.org/2.0/user/emails?access_token=' . \urlencode($accessToken));
|
||||
$emails = \json_decode($emails, true);
|
||||
if (isset($emails['values'])) {
|
||||
foreach ($emails['values'] as $email) {
|
||||
if ($email['is_confirmed']) {
|
||||
$this->user['email'] = $email['email'];
|
||||
$this->user['is_confirmed'] = $email['is_confirmed'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $this->user;
|
||||
}
|
||||
|
||||
@@ -3,43 +3,41 @@
|
||||
namespace Appwrite\Auth\OAuth2;
|
||||
|
||||
use Appwrite\Auth\OAuth2;
|
||||
use Utopia\Exception;
|
||||
|
||||
// Reference Material
|
||||
// https://dev.bitly.com/v4_documentation.html
|
||||
|
||||
class Bitly extends OAuth2
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private string $endpoint = 'https://bitly.com/oauth/';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $endpoint = 'https://bitly.com/oauth/';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $resourceEndpoint = 'https://api-ssl.bitly.com/';
|
||||
private string $resourceEndpoint = 'https://api-ssl.bitly.com/';
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $scopes = [];
|
||||
protected array $scopes = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $user = [];
|
||||
|
||||
protected array $user = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $tokens = [];
|
||||
protected array $tokens = [];
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getName():string
|
||||
public function getName(): string
|
||||
{
|
||||
return 'bitly';
|
||||
}
|
||||
@@ -47,9 +45,9 @@ class Bitly extends OAuth2
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getLoginURL():string
|
||||
public function getLoginURL(): string
|
||||
{
|
||||
return $this->endpoint . 'authorize?'.
|
||||
return $this->endpoint . 'authorize?' .
|
||||
\http_build_query([
|
||||
'client_id' => $this->appID,
|
||||
'redirect_uri' => $this->callback,
|
||||
@@ -64,7 +62,7 @@ class Bitly extends OAuth2
|
||||
*/
|
||||
protected function getTokens(string $code): array
|
||||
{
|
||||
if(empty($this->tokens)) {
|
||||
if (empty($this->tokens)) {
|
||||
$response = $this->request(
|
||||
'POST',
|
||||
$this->resourceEndpoint . 'oauth/access_token',
|
||||
@@ -91,7 +89,7 @@ class Bitly extends OAuth2
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function refreshTokens(string $refreshToken):array
|
||||
public function refreshTokens(string $refreshToken): array
|
||||
{
|
||||
$response = $this->request(
|
||||
'POST',
|
||||
@@ -109,7 +107,7 @@ class Bitly extends OAuth2
|
||||
\parse_str($response, $output);
|
||||
$this->tokens = $output;
|
||||
|
||||
if(empty($this->tokens['refresh_token'])) {
|
||||
if (empty($this->tokens['refresh_token'])) {
|
||||
$this->tokens['refresh_token'] = $refreshToken;
|
||||
}
|
||||
|
||||
@@ -117,51 +115,61 @@ class Bitly extends OAuth2
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $accessToken
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUserID(string $accessToken):string
|
||||
public function getUserID(string $accessToken): string
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if (isset($user['login'])) {
|
||||
return $user['login'];
|
||||
}
|
||||
|
||||
return '';
|
||||
return $user['login'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $accessToken
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUserEmail(string $accessToken):string
|
||||
public function getUserEmail(string $accessToken): string
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if (isset($user['emails'])) {
|
||||
return $user['emails'][0]['email'];
|
||||
foreach ($user['emails'] as $email) {
|
||||
if ($email['is_verified'] === true) {
|
||||
return $email['email'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $accessToken
|
||||
* Check if the OAuth email is verified
|
||||
*
|
||||
* @link https://dev.bitly.com/api-reference#getUser
|
||||
*
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmailVerified(string $accessToken): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUserName(string $accessToken):string
|
||||
public function getUserName(string $accessToken): string
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if (isset($user['name'])) {
|
||||
return $user['name'];
|
||||
}
|
||||
|
||||
return '';
|
||||
return $user['name'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -172,7 +180,7 @@ class Bitly extends OAuth2
|
||||
protected function getUser(string $accessToken)
|
||||
{
|
||||
$headers = [
|
||||
'Authorization: Bearer '. \urlencode($accessToken),
|
||||
'Authorization: Bearer ' . \urlencode($accessToken),
|
||||
"Accept: application/json"
|
||||
];
|
||||
|
||||
@@ -180,7 +188,6 @@ class Bitly extends OAuth2
|
||||
$this->user = \json_decode($this->request('GET', $this->resourceEndpoint . "v4/user", $headers), true);
|
||||
}
|
||||
|
||||
|
||||
return $this->user;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,27 +12,27 @@ class Box extends OAuth2
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $endpoint = 'https://account.box.com/api/oauth2/';
|
||||
private string $endpoint = 'https://account.box.com/api/oauth2/';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $resourceEndpoint = 'https://api.box.com/2.0/';
|
||||
private string $resourceEndpoint = 'https://api.box.com/2.0/';
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $user = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $tokens = [];
|
||||
protected array $user = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $scopes = [
|
||||
protected array $tokens = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $scopes = [
|
||||
'manage_app_users',
|
||||
];
|
||||
|
||||
@@ -49,7 +49,7 @@ class Box extends OAuth2
|
||||
*/
|
||||
public function getLoginURL(): string
|
||||
{
|
||||
$url = $this->endpoint . 'authorize?'.
|
||||
$url = $this->endpoint . 'authorize?' .
|
||||
\http_build_query([
|
||||
'response_type' => 'code',
|
||||
'client_id' => $this->appID,
|
||||
@@ -68,7 +68,7 @@ class Box extends OAuth2
|
||||
*/
|
||||
protected function getTokens(string $code): array
|
||||
{
|
||||
if(empty($this->tokens)) {
|
||||
if (empty($this->tokens)) {
|
||||
$headers = ['Content-Type: application/x-www-form-urlencoded'];
|
||||
$this->tokens = \json_decode($this->request(
|
||||
'POST',
|
||||
@@ -93,7 +93,7 @@ class Box extends OAuth2
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function refreshTokens(string $refreshToken):array
|
||||
public function refreshTokens(string $refreshToken): array
|
||||
{
|
||||
$headers = ['Content-Type: application/x-www-form-urlencoded'];
|
||||
$this->tokens = \json_decode($this->request(
|
||||
@@ -108,7 +108,7 @@ class Box extends OAuth2
|
||||
])
|
||||
), true);
|
||||
|
||||
if(empty($this->tokens['refresh_token'])) {
|
||||
if (empty($this->tokens['refresh_token'])) {
|
||||
$this->tokens['refresh_token'] = $refreshToken;
|
||||
}
|
||||
|
||||
@@ -124,11 +124,7 @@ class Box extends OAuth2
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if (isset($user['id'])) {
|
||||
return $user['id'];
|
||||
}
|
||||
|
||||
return '';
|
||||
return $user['id'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -140,11 +136,23 @@ class Box extends OAuth2
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if (isset($user['login'])) {
|
||||
return $user['login'];
|
||||
}
|
||||
return $user['login'] ?? '';
|
||||
}
|
||||
|
||||
return '';
|
||||
/**
|
||||
* Check if the OAuth email is verified
|
||||
*
|
||||
* If present, the email is verified. This was verfied through a manual Box sign up process
|
||||
*
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmailVerified(string $accessToken): bool
|
||||
{
|
||||
$email = $this->getUserEmail($accessToken);
|
||||
|
||||
return !empty($email);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -156,11 +164,7 @@ class Box extends OAuth2
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if (isset($user['name'])) {
|
||||
return $user['name'];
|
||||
}
|
||||
|
||||
return '';
|
||||
return $user['name'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -171,7 +175,7 @@ class Box extends OAuth2
|
||||
protected function getUser(string $accessToken): array
|
||||
{
|
||||
$header = [
|
||||
'Authorization: Bearer '.\urlencode($accessToken),
|
||||
'Authorization: Bearer ' . \urlencode($accessToken),
|
||||
];
|
||||
if (empty($this->user)) {
|
||||
$user = $this->request(
|
||||
|
||||
@@ -0,0 +1,213 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Auth\OAuth2;
|
||||
|
||||
use Appwrite\Auth\OAuth2;
|
||||
|
||||
// Reference Material
|
||||
// https://developers.dailymotion.com/api/#authentication
|
||||
|
||||
class Dailymotion extends OAuth2
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private string $endpoint = 'https://api.dailymotion.com';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private string $authEndpoint = 'https://www.dailymotion.com/oauth/authorize';
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $scopes = [
|
||||
'userinfo',
|
||||
'email'
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $fields = [
|
||||
'email',
|
||||
'id',
|
||||
'fullname',
|
||||
'verified'
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $user = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $tokens = [];
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getName(): string
|
||||
{
|
||||
return 'dailymotion';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getFields(): array
|
||||
{
|
||||
return $this->fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getLoginURL(): string
|
||||
{
|
||||
$url = $this->authEndpoint . '?' .
|
||||
\http_build_query([
|
||||
'response_type' => 'code',
|
||||
'client_id' => $this->appID,
|
||||
'state' => \json_encode($this->state),
|
||||
'redirect_uri' => $this->callback,
|
||||
'scope' => \implode(' ', $this->getScopes())
|
||||
]);
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $code
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getTokens(string $code): array
|
||||
{
|
||||
if (empty($this->tokens)) {
|
||||
$this->tokens = \json_decode($this->request(
|
||||
'POST',
|
||||
$this->endpoint . '/oauth/token',
|
||||
["Content-Type: application/x-www-form-urlencoded"],
|
||||
\http_build_query([
|
||||
'grant_type' => 'authorization_code',
|
||||
"client_id" => $this->appID,
|
||||
"client_secret" => $this->appSecret,
|
||||
"redirect_uri" => $this->callback,
|
||||
'code' => $code,
|
||||
'scope' => \implode(' ', $this->getScopes()),
|
||||
])
|
||||
), true);
|
||||
}
|
||||
return $this->tokens;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $refreshToken
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function refreshTokens(string $refreshToken): array
|
||||
{
|
||||
|
||||
$this->tokens = \json_decode($this->request(
|
||||
'POST',
|
||||
$this->endpoint . '/oauth/token',
|
||||
['Content-Type: application/x-www-form-urlencoded'],
|
||||
\http_build_query([
|
||||
'grant_type' => 'refresh_token',
|
||||
'refresh_token' => $refreshToken,
|
||||
'client_id' => $this->appID,
|
||||
'client_secret' => $this->appSecret,
|
||||
])
|
||||
), true);
|
||||
|
||||
if (empty($this->tokens['refresh_token'])) {
|
||||
$this->tokens['refresh_token'] = $refreshToken;
|
||||
}
|
||||
|
||||
|
||||
return $this->tokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUserID(string $accessToken): string
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
$userId = $user['id'] ?? '';
|
||||
|
||||
return $userId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUserEmail(string $accessToken): string
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
$userEmail = $user['email'] ?? '';
|
||||
|
||||
return $userEmail;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the OAuth email is verified
|
||||
*
|
||||
* @link https://developers.dailymotion.com/api/#user-fields
|
||||
*
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmailVerified(string $accessToken): bool
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
return $user['verified'] ?? false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUserName(string $accessToken): string
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
|
||||
$username = $user['fullname'] ?? '';
|
||||
|
||||
return $username;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getUser(string $accessToken): array
|
||||
{
|
||||
if (empty($this->user)) {
|
||||
$user = $this->request(
|
||||
'GET',
|
||||
$this->endpoint . '/user/me?fields=' . \implode(',', $this->getFields()),
|
||||
['Authorization: Bearer ' . \urlencode($accessToken)],
|
||||
);
|
||||
$this->user = \json_decode($user, true);
|
||||
}
|
||||
|
||||
return $this->user;
|
||||
}
|
||||
}
|
||||
@@ -12,22 +12,22 @@ class Discord extends OAuth2
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $endpoint = 'https://discordapp.com/api';
|
||||
private string $endpoint = 'https://discordapp.com/api';
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $user = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $tokens = [];
|
||||
protected array $user = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $scopes = [
|
||||
protected array $tokens = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $scopes = [
|
||||
'identify',
|
||||
'email'
|
||||
];
|
||||
@@ -45,7 +45,7 @@ class Discord extends OAuth2
|
||||
*/
|
||||
public function getLoginURL(): string
|
||||
{
|
||||
$url = $this->endpoint . '/oauth2/authorize?'.
|
||||
$url = $this->endpoint . '/oauth2/authorize?' .
|
||||
\http_build_query([
|
||||
'response_type' => 'code',
|
||||
'client_id' => $this->appID,
|
||||
@@ -64,7 +64,7 @@ class Discord extends OAuth2
|
||||
*/
|
||||
protected function getTokens(string $code): array
|
||||
{
|
||||
if(empty($this->tokens)) {
|
||||
if (empty($this->tokens)) {
|
||||
$this->tokens = \json_decode($this->request(
|
||||
'POST',
|
||||
$this->endpoint . '/oauth2/token',
|
||||
@@ -88,7 +88,7 @@ class Discord extends OAuth2
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function refreshTokens(string $refreshToken):array
|
||||
public function refreshTokens(string $refreshToken): array
|
||||
{
|
||||
$this->tokens = \json_decode($this->request(
|
||||
'POST',
|
||||
@@ -102,7 +102,7 @@ class Discord extends OAuth2
|
||||
])
|
||||
), true);
|
||||
|
||||
if(empty($this->tokens['refresh_token'])) {
|
||||
if (empty($this->tokens['refresh_token'])) {
|
||||
$this->tokens['refresh_token'] = $refreshToken;
|
||||
}
|
||||
|
||||
@@ -118,11 +118,7 @@ class Discord extends OAuth2
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if (isset($user['id'])) {
|
||||
return $user['id'];
|
||||
}
|
||||
|
||||
return '';
|
||||
return $user['id'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -134,11 +130,27 @@ class Discord extends OAuth2
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if (isset($user['email'])) {
|
||||
return $user['email'];
|
||||
return $user['email'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the OAuth email is verified
|
||||
*
|
||||
* @link https://discord.com/developers/docs/resources/user
|
||||
*
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmailVerified(string $accessToken): bool
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if ($user['verified'] ?? false) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return '';
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -150,11 +162,7 @@ class Discord extends OAuth2
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if (isset($user['username'])) {
|
||||
return $user['username'];
|
||||
}
|
||||
|
||||
return '';
|
||||
return $user['username'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -168,7 +176,7 @@ class Discord extends OAuth2
|
||||
$user = $this->request(
|
||||
'GET',
|
||||
$this->endpoint . '/users/@me',
|
||||
['Authorization: Bearer '.\urlencode($accessToken)]
|
||||
['Authorization: Bearer ' . \urlencode($accessToken)]
|
||||
);
|
||||
$this->user = \json_decode($user, true);
|
||||
}
|
||||
|
||||
@@ -13,17 +13,17 @@ class Dropbox extends OAuth2
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $user = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $tokens = [];
|
||||
protected array $user = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $scopes = [];
|
||||
protected array $tokens = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $scopes = [];
|
||||
|
||||
/**
|
||||
* @return string
|
||||
@@ -32,17 +32,17 @@ class Dropbox extends OAuth2
|
||||
{
|
||||
return 'dropbox';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getLoginURL(): string
|
||||
{
|
||||
return 'https://www.dropbox.com/oauth2/authorize?'.\http_build_query([
|
||||
'client_id' => $this->appID,
|
||||
'redirect_uri' => $this->callback,
|
||||
'state' => \json_encode($this->state),
|
||||
'response_type' => 'code'
|
||||
return 'https://www.dropbox.com/oauth2/authorize?' . \http_build_query([
|
||||
'client_id' => $this->appID,
|
||||
'redirect_uri' => $this->callback,
|
||||
'state' => \json_encode($this->state),
|
||||
'response_type' => 'code'
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ class Dropbox extends OAuth2
|
||||
*/
|
||||
protected function getTokens(string $code): array
|
||||
{
|
||||
if(empty($this->tokens)) {
|
||||
if (empty($this->tokens)) {
|
||||
$headers = ['Content-Type: application/x-www-form-urlencoded'];
|
||||
$this->tokens = \json_decode($this->request(
|
||||
'POST',
|
||||
@@ -77,7 +77,7 @@ class Dropbox extends OAuth2
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function refreshTokens(string $refreshToken):array
|
||||
public function refreshTokens(string $refreshToken): array
|
||||
{
|
||||
$headers = ['Content-Type: application/x-www-form-urlencoded'];
|
||||
$this->tokens = \json_decode($this->request(
|
||||
@@ -92,7 +92,7 @@ class Dropbox extends OAuth2
|
||||
])
|
||||
), true);
|
||||
|
||||
if(empty($this->tokens['refresh_token'])) {
|
||||
if (empty($this->tokens['refresh_token'])) {
|
||||
$this->tokens['refresh_token'] = $refreshToken;
|
||||
}
|
||||
|
||||
@@ -108,11 +108,7 @@ class Dropbox extends OAuth2
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if (isset($user['account_id'])) {
|
||||
return $user['account_id'];
|
||||
}
|
||||
|
||||
return '';
|
||||
return $user['account_id'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -124,11 +120,27 @@ class Dropbox extends OAuth2
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if (isset($user['email'])) {
|
||||
return $user['email'];
|
||||
return $user['email'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the OAuth email is verified
|
||||
*
|
||||
* @link https://www.dropbox.com/developers/documentation/http/documentation#users-get_current_account
|
||||
*
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmailVerified(string $accessToken): bool
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if ($user['email_verified'] ?? false) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return '';
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -140,11 +152,7 @@ class Dropbox extends OAuth2
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if (isset($user['name'])) {
|
||||
return $user['name']['display_name'];
|
||||
}
|
||||
|
||||
return '';
|
||||
return $user['name']['display_name'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -155,7 +163,7 @@ class Dropbox extends OAuth2
|
||||
protected function getUser(string $accessToken): array
|
||||
{
|
||||
if (empty($this->user)) {
|
||||
$headers = ['Authorization: Bearer '. \urlencode($accessToken)];
|
||||
$headers = ['Authorization: Bearer ' . \urlencode($accessToken)];
|
||||
$user = $this->request('POST', 'https://api.dropboxapi.com/2/users/get_current_account', $headers);
|
||||
$this->user = \json_decode($user, true);
|
||||
}
|
||||
|
||||
@@ -3,36 +3,35 @@
|
||||
namespace Appwrite\Auth\OAuth2;
|
||||
|
||||
use Appwrite\Auth\OAuth2;
|
||||
use Utopia\Exception;
|
||||
|
||||
class Facebook extends OAuth2
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $version = 'v2.8';
|
||||
protected string $version = 'v2.8';
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $user = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $tokens = [];
|
||||
protected array $user = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $scopes = [
|
||||
protected array $tokens = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $scopes = [
|
||||
'email'
|
||||
];
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getName():string
|
||||
public function getName(): string
|
||||
{
|
||||
return 'facebook';
|
||||
}
|
||||
@@ -40,10 +39,10 @@ class Facebook extends OAuth2
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getLoginURL():string
|
||||
public function getLoginURL(): string
|
||||
{
|
||||
return 'https://www.facebook.com/'.$this->version.'/dialog/oauth?'.\http_build_query([
|
||||
'client_id'=> $this->appID,
|
||||
return 'https://www.facebook.com/' . $this->version . '/dialog/oauth?' . \http_build_query([
|
||||
'client_id' => $this->appID,
|
||||
'redirect_uri' => $this->callback,
|
||||
'scope' => \implode(' ', $this->getScopes()),
|
||||
'state' => \json_encode($this->state)
|
||||
@@ -57,7 +56,7 @@ class Facebook extends OAuth2
|
||||
*/
|
||||
protected function getTokens(string $code): array
|
||||
{
|
||||
if(empty($this->tokens)) {
|
||||
if (empty($this->tokens)) {
|
||||
$this->tokens = \json_decode($this->request(
|
||||
'GET',
|
||||
'https://graph.facebook.com/' . $this->version . '/oauth/access_token?' . \http_build_query([
|
||||
@@ -77,7 +76,7 @@ class Facebook extends OAuth2
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function refreshTokens(string $refreshToken):array
|
||||
public function refreshTokens(string $refreshToken): array
|
||||
{
|
||||
$this->tokens = \json_decode($this->request(
|
||||
'GET',
|
||||
@@ -90,7 +89,7 @@ class Facebook extends OAuth2
|
||||
])
|
||||
), true);
|
||||
|
||||
if(empty($this->tokens['refresh_token'])) {
|
||||
if (empty($this->tokens['refresh_token'])) {
|
||||
$this->tokens['refresh_token'] = $refreshToken;
|
||||
}
|
||||
|
||||
@@ -102,15 +101,11 @@ class Facebook extends OAuth2
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUserID(string $accessToken):string
|
||||
public function getUserID(string $accessToken): string
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if (isset($user['id'])) {
|
||||
return $user['id'];
|
||||
}
|
||||
|
||||
return '';
|
||||
return $user['id'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -118,15 +113,27 @@ class Facebook extends OAuth2
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUserEmail(string $accessToken):string
|
||||
public function getUserEmail(string $accessToken): string
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if (isset($user['email'])) {
|
||||
return $user['email'];
|
||||
}
|
||||
return $user['email'] ?? '';
|
||||
}
|
||||
|
||||
return '';
|
||||
/**
|
||||
* Check if the OAuth email is verified
|
||||
*
|
||||
* If present, the email is verified. This was verfied through a manual Facebook sign up process
|
||||
*
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmailVerified(string $accessToken): bool
|
||||
{
|
||||
$email = $this->getUserEmail($accessToken);
|
||||
|
||||
return !empty($email);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -134,15 +141,11 @@ class Facebook extends OAuth2
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUserName(string $accessToken):string
|
||||
public function getUserName(string $accessToken): string
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if (isset($user['name'])) {
|
||||
return $user['name'];
|
||||
}
|
||||
|
||||
return '';
|
||||
return $user['name'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -150,10 +153,10 @@ class Facebook extends OAuth2
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getUser(string $accessToken):array
|
||||
protected function getUser(string $accessToken): array
|
||||
{
|
||||
if (empty($this->user)) {
|
||||
$user = $this->request('GET', 'https://graph.facebook.com/'.$this->version.'/me?fields=email,name&access_token='.\urlencode($accessToken));
|
||||
$user = $this->request('GET', 'https://graph.facebook.com/' . $this->version . '/me?fields=email,name&access_token=' . \urlencode($accessToken));
|
||||
|
||||
$this->user = \json_decode($user, true);
|
||||
}
|
||||
|
||||
@@ -3,31 +3,30 @@
|
||||
namespace Appwrite\Auth\OAuth2;
|
||||
|
||||
use Appwrite\Auth\OAuth2;
|
||||
use Utopia\Exception;
|
||||
|
||||
class Github extends OAuth2
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $user = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $tokens = [];
|
||||
protected array $user = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $scopes = [
|
||||
protected array $tokens = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $scopes = [
|
||||
'user:email',
|
||||
];
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getName():string
|
||||
public function getName(): string
|
||||
{
|
||||
return 'github';
|
||||
}
|
||||
@@ -35,9 +34,9 @@ class Github extends OAuth2
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getLoginURL():string
|
||||
public function getLoginURL(): string
|
||||
{
|
||||
return 'https://github.com/login/oauth/authorize?'. \http_build_query([
|
||||
return 'https://github.com/login/oauth/authorize?' . \http_build_query([
|
||||
'client_id' => $this->appID,
|
||||
'redirect_uri' => $this->callback,
|
||||
'scope' => \implode(' ', $this->getScopes()),
|
||||
@@ -52,7 +51,7 @@ class Github extends OAuth2
|
||||
*/
|
||||
protected function getTokens(string $code): array
|
||||
{
|
||||
if(empty($this->tokens)) {
|
||||
if (empty($this->tokens)) {
|
||||
$response = $this->request(
|
||||
'POST',
|
||||
'https://github.com/login/oauth/access_token',
|
||||
@@ -78,7 +77,7 @@ class Github extends OAuth2
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function refreshTokens(string $refreshToken):array
|
||||
public function refreshTokens(string $refreshToken): array
|
||||
{
|
||||
$response = $this->request(
|
||||
'POST',
|
||||
@@ -96,7 +95,7 @@ class Github extends OAuth2
|
||||
\parse_str($response, $output);
|
||||
$this->tokens = $output;
|
||||
|
||||
if(empty($this->tokens['refresh_token'])) {
|
||||
if (empty($this->tokens['refresh_token'])) {
|
||||
$this->tokens['refresh_token'] = $refreshToken;
|
||||
}
|
||||
|
||||
@@ -104,53 +103,59 @@ class Github extends OAuth2
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $accessToken
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUserID(string $accessToken):string
|
||||
public function getUserID(string $accessToken): string
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if (isset($user['id'])) {
|
||||
return $user['id'];
|
||||
}
|
||||
|
||||
return '';
|
||||
return $user['id'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $accessToken
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUserEmail(string $accessToken):string
|
||||
{
|
||||
$emails = \json_decode($this->request('GET', 'https://api.github.com/user/emails', ['Authorization: token '.\urlencode($accessToken)]), true);
|
||||
|
||||
foreach ($emails as $email) {
|
||||
if ($email['primary'] && $email['verified']) {
|
||||
return $email['email'];
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $accessToken
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUserName(string $accessToken):string
|
||||
public function getUserEmail(string $accessToken): string
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if (isset($user['name'])) {
|
||||
return $user['name'];
|
||||
return $user['email'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the OAuth email is verified
|
||||
*
|
||||
* @link https://docs.github.com/en/rest/users/emails#list-email-addresses-for-the-authenticated-user
|
||||
*
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmailVerified(string $accessToken): bool
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if ($user['verified'] ?? false) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return '';
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUserName(string $accessToken): string
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
return $user['name'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -161,7 +166,18 @@ class Github extends OAuth2
|
||||
protected function getUser(string $accessToken)
|
||||
{
|
||||
if (empty($this->user)) {
|
||||
$this->user = \json_decode($this->request('GET', 'https://api.github.com/user', ['Authorization: token '.\urlencode($accessToken)]), true);
|
||||
$this->user = \json_decode($this->request('GET', 'https://api.github.com/user', ['Authorization: token ' . \urlencode($accessToken)]), true);
|
||||
|
||||
$emails = $this->request('GET', 'https://api.github.com/user/emails', ['Authorization: token ' . \urlencode($accessToken)]);
|
||||
|
||||
$emails = \json_decode($emails, true);
|
||||
foreach ($emails as $email) {
|
||||
if (isset($email['verified']) && $email['verified'] === true) {
|
||||
$this->user['email'] = $email['email'];
|
||||
$this->user['verified'] = $email['verified'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->user;
|
||||
|
||||
@@ -12,17 +12,17 @@ class Gitlab extends OAuth2
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $user = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $tokens = [];
|
||||
protected array $user = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $scopes = [
|
||||
protected array $tokens = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $scopes = [
|
||||
'read_user'
|
||||
];
|
||||
|
||||
@@ -39,7 +39,7 @@ class Gitlab extends OAuth2
|
||||
*/
|
||||
public function getLoginURL(): string
|
||||
{
|
||||
return 'https://gitlab.com/oauth/authorize?'.\http_build_query([
|
||||
return $this->getEndpoint() . '/oauth/authorize?' . \http_build_query([
|
||||
'client_id' => $this->appID,
|
||||
'redirect_uri' => $this->callback,
|
||||
'scope' => \implode(' ', $this->getScopes()),
|
||||
@@ -55,13 +55,13 @@ class Gitlab extends OAuth2
|
||||
*/
|
||||
protected function getTokens(string $code): array
|
||||
{
|
||||
if(empty($this->tokens)) {
|
||||
if (empty($this->tokens)) {
|
||||
$this->tokens = \json_decode($this->request(
|
||||
'POST',
|
||||
'https://gitlab.com/oauth/token?' . \http_build_query([
|
||||
$this->getEndpoint() . '/oauth/token?' . \http_build_query([
|
||||
'code' => $code,
|
||||
'client_id' => $this->appID,
|
||||
'client_secret' => $this->appSecret,
|
||||
'client_secret' => $this->getAppSecret()['clientSecret'],
|
||||
'redirect_uri' => $this->callback,
|
||||
'grant_type' => 'authorization_code'
|
||||
])
|
||||
@@ -76,19 +76,19 @@ class Gitlab extends OAuth2
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function refreshTokens(string $refreshToken):array
|
||||
public function refreshTokens(string $refreshToken): array
|
||||
{
|
||||
$this->tokens = \json_decode($this->request(
|
||||
'POST',
|
||||
'https://gitlab.com/oauth/token?' . \http_build_query([
|
||||
$this->getEndpoint() . '/oauth/token?' . \http_build_query([
|
||||
'refresh_token' => $refreshToken,
|
||||
'client_id' => $this->appID,
|
||||
'client_secret' => $this->appSecret,
|
||||
'client_secret' => $this->getAppSecret()['clientSecret'],
|
||||
'grant_type' => 'refresh_token'
|
||||
])
|
||||
), true);
|
||||
|
||||
if(empty($this->tokens['refresh_token'])) {
|
||||
if (empty($this->tokens['refresh_token'])) {
|
||||
$this->tokens['refresh_token'] = $refreshToken;
|
||||
}
|
||||
|
||||
@@ -120,11 +120,27 @@ class Gitlab extends OAuth2
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if (isset($user['email'])) {
|
||||
return $user['email'];
|
||||
return $user['email'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the OAuth email is verified
|
||||
*
|
||||
* @link https://docs.gitlab.com/ee/api/users.html#list-current-user-for-normal-users
|
||||
*
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmailVerified(string $accessToken): bool
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if ($user['confirmed_at'] ?? false) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return '';
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -136,11 +152,7 @@ class Gitlab extends OAuth2
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if (isset($user['name'])) {
|
||||
return $user['name'];
|
||||
}
|
||||
|
||||
return '';
|
||||
return $user['name'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -151,10 +163,39 @@ class Gitlab extends OAuth2
|
||||
protected function getUser(string $accessToken): array
|
||||
{
|
||||
if (empty($this->user)) {
|
||||
$user = $this->request('GET', 'https://gitlab.com/api/v4/user?access_token='.\urlencode($accessToken));
|
||||
$user = $this->request('GET', $this->getEndpoint() . '/api/v4/user?access_token=' . \urlencode($accessToken));
|
||||
$this->user = \json_decode($user, true);
|
||||
}
|
||||
|
||||
return $this->user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode the JSON stored in appSecret
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getAppSecret(): array
|
||||
{
|
||||
try {
|
||||
$secret = \json_decode($this->appSecret, true, 512, JSON_THROW_ON_ERROR);
|
||||
} catch (\Throwable $th) {
|
||||
throw new \Exception('Invalid secret');
|
||||
}
|
||||
return $secret;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Extracts the Tenant Id from the JSON stored in appSecret. Defaults to 'common' as a fallback
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getEndpoint(): string
|
||||
{
|
||||
$defaultEndpoint = 'https://gitlab.com';
|
||||
$secret = $this->getAppSecret();
|
||||
$endpoint = $secret['endpoint'] ?? $defaultEndpoint;
|
||||
return empty($endpoint) ? $defaultEndpoint : $endpoint;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,12 +14,12 @@ class Google extends OAuth2
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $version = 'v4';
|
||||
protected string $version = 'v4';
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $scopes = [
|
||||
protected array $scopes = [
|
||||
'https://www.googleapis.com/auth/userinfo.email',
|
||||
'https://www.googleapis.com/auth/userinfo.profile',
|
||||
'openid'
|
||||
@@ -28,12 +28,12 @@ class Google extends OAuth2
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $user = [];
|
||||
|
||||
protected array $user = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $tokens = [];
|
||||
protected array $tokens = [];
|
||||
|
||||
/**
|
||||
* @return string
|
||||
@@ -48,7 +48,7 @@ class Google extends OAuth2
|
||||
*/
|
||||
public function getLoginURL(): string
|
||||
{
|
||||
return 'https://accounts.google.com/o/oauth2/v2/auth?'. \http_build_query([
|
||||
return 'https://accounts.google.com/o/oauth2/v2/auth?' . \http_build_query([
|
||||
'client_id' => $this->appID,
|
||||
'redirect_uri' => $this->callback,
|
||||
'scope' => \implode(' ', $this->getScopes()),
|
||||
@@ -64,7 +64,7 @@ class Google extends OAuth2
|
||||
*/
|
||||
protected function getTokens(string $code): array
|
||||
{
|
||||
if(empty($this->tokens)) {
|
||||
if (empty($this->tokens)) {
|
||||
$this->tokens = \json_decode($this->request(
|
||||
'POST',
|
||||
'https://oauth2.googleapis.com/token?' . \http_build_query([
|
||||
@@ -86,7 +86,7 @@ class Google extends OAuth2
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function refreshTokens(string $refreshToken):array
|
||||
public function refreshTokens(string $refreshToken): array
|
||||
{
|
||||
$this->tokens = \json_decode($this->request(
|
||||
'POST',
|
||||
@@ -98,7 +98,7 @@ class Google extends OAuth2
|
||||
])
|
||||
), true);
|
||||
|
||||
if(empty($this->tokens['refresh_token'])) {
|
||||
if (empty($this->tokens['refresh_token'])) {
|
||||
$this->tokens['refresh_token'] = $refreshToken;
|
||||
}
|
||||
|
||||
@@ -114,11 +114,7 @@ class Google extends OAuth2
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if (isset($user['id'])) {
|
||||
return $user['id'];
|
||||
}
|
||||
|
||||
return '';
|
||||
return $user['sub'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -130,11 +126,27 @@ class Google extends OAuth2
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if (isset($user['email'])) {
|
||||
return $user['email'];
|
||||
return $user['email'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the OAuth email is verified
|
||||
*
|
||||
* @link https://www.oauth.com/oauth2-servers/signing-in-with-google/verifying-the-user-info/
|
||||
*
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmailVerified(string $accessToken): bool
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if ($user['email_verified'] ?? false) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return '';
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -146,11 +158,7 @@ class Google extends OAuth2
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if (isset($user['name'])) {
|
||||
return $user['name'];
|
||||
}
|
||||
|
||||
return '';
|
||||
return $user['name'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -161,7 +169,7 @@ class Google extends OAuth2
|
||||
protected function getUser(string $accessToken): array
|
||||
{
|
||||
if (empty($this->user)) {
|
||||
$user = $this->request('GET', 'https://www.googleapis.com/oauth2/v2/userinfo?access_token='.\urlencode($accessToken));
|
||||
$user = $this->request('GET', 'https://www.googleapis.com/oauth2/v3/userinfo?access_token=' . \urlencode($accessToken));
|
||||
$this->user = \json_decode($user, true);
|
||||
}
|
||||
|
||||
|
||||
@@ -9,17 +9,17 @@ class Linkedin extends OAuth2
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $user = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $tokens = [];
|
||||
protected array $user = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $scopes = [
|
||||
protected array $tokens = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $scopes = [
|
||||
'r_liteprofile',
|
||||
'r_emailaddress',
|
||||
];
|
||||
@@ -40,7 +40,7 @@ class Linkedin extends OAuth2
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getName():string
|
||||
public function getName(): string
|
||||
{
|
||||
return 'linkedin';
|
||||
}
|
||||
@@ -48,15 +48,15 @@ class Linkedin extends OAuth2
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getLoginURL():string
|
||||
public function getLoginURL(): string
|
||||
{
|
||||
return 'https://www.linkedin.com/oauth/v2/authorization?'.\http_build_query([
|
||||
'response_type' => 'code',
|
||||
'client_id' => $this->appID,
|
||||
'redirect_uri' => $this->callback,
|
||||
'scope' => \implode(' ', $this->getScopes()),
|
||||
'state' => \json_encode($this->state),
|
||||
]);
|
||||
return 'https://www.linkedin.com/oauth/v2/authorization?' . \http_build_query([
|
||||
'response_type' => 'code',
|
||||
'client_id' => $this->appID,
|
||||
'redirect_uri' => $this->callback,
|
||||
'scope' => \implode(' ', $this->getScopes()),
|
||||
'state' => \json_encode($this->state),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -66,7 +66,7 @@ class Linkedin extends OAuth2
|
||||
*/
|
||||
protected function getTokens(string $code): array
|
||||
{
|
||||
if(empty($this->tokens)) {
|
||||
if (empty($this->tokens)) {
|
||||
$this->tokens = \json_decode($this->request(
|
||||
'POST',
|
||||
'https://www.linkedin.com/oauth/v2/accessToken',
|
||||
@@ -89,7 +89,7 @@ class Linkedin extends OAuth2
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function refreshTokens(string $refreshToken):array
|
||||
public function refreshTokens(string $refreshToken): array
|
||||
{
|
||||
$this->tokens = \json_decode($this->request(
|
||||
'POST',
|
||||
@@ -104,7 +104,7 @@ class Linkedin extends OAuth2
|
||||
])
|
||||
), true);
|
||||
|
||||
if(empty($this->tokens['refresh_token'])) {
|
||||
if (empty($this->tokens['refresh_token'])) {
|
||||
$this->tokens['refresh_token'] = $refreshToken;
|
||||
}
|
||||
|
||||
@@ -112,48 +112,51 @@ class Linkedin extends OAuth2
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $accessToken
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUserID(string $accessToken):string
|
||||
public function getUserID(string $accessToken): string
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if (isset($user['id'])) {
|
||||
return $user['id'];
|
||||
}
|
||||
|
||||
return '';
|
||||
return $user['id'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $accessToken
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUserEmail(string $accessToken):string
|
||||
public function getUserEmail(string $accessToken): string
|
||||
{
|
||||
$email = \json_decode($this->request('GET', 'https://api.linkedin.com/v2/emailAddress?q=members&projection=(elements*(handle~))', ['Authorization: Bearer '.\urlencode($accessToken)]), true);
|
||||
$email = \json_decode($this->request('GET', 'https://api.linkedin.com/v2/emailAddress?q=members&projection=(elements*(handle~))', ['Authorization: Bearer ' . \urlencode($accessToken)]), true);
|
||||
|
||||
if (
|
||||
isset($email['elements']) &&
|
||||
isset($email['elements'][0]) &&
|
||||
isset($email['elements'][0]['handle~']) &&
|
||||
isset($email['elements'][0]['handle~']['emailAddress'])
|
||||
) {
|
||||
return $email['elements'][0]['handle~']['emailAddress'];
|
||||
}
|
||||
|
||||
return '';
|
||||
return $email['elements'][0]['handle~']['emailAddress'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $accessToken
|
||||
* Check if the OAuth email is verified
|
||||
*
|
||||
* If present, the email is verified. This was verfied through a manual Linkedin sign up process
|
||||
*
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmailVerified(string $accessToken): bool
|
||||
{
|
||||
$email = $this->getUserEmail($accessToken);
|
||||
|
||||
return !empty($email);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUserName(string $accessToken):string
|
||||
public function getUserName(string $accessToken): string
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
$name = '';
|
||||
@@ -163,7 +166,7 @@ class Linkedin extends OAuth2
|
||||
}
|
||||
|
||||
if (isset($user['localizedLastName'])) {
|
||||
$name = (empty($name)) ? $user['localizedLastName'] : $name.' '.$user['localizedLastName'];
|
||||
$name = (empty($name)) ? $user['localizedLastName'] : $name . ' ' . $user['localizedLastName'];
|
||||
}
|
||||
|
||||
return $name;
|
||||
@@ -177,7 +180,7 @@ class Linkedin extends OAuth2
|
||||
protected function getUser(string $accessToken)
|
||||
{
|
||||
if (empty($this->user)) {
|
||||
$this->user = \json_decode($this->request('GET', 'https://api.linkedin.com/v2/me', ['Authorization: Bearer '.\urlencode($accessToken)]), true);
|
||||
$this->user = \json_decode($this->request('GET', 'https://api.linkedin.com/v2/me', ['Authorization: Bearer ' . \urlencode($accessToken)]), true);
|
||||
}
|
||||
|
||||
return $this->user;
|
||||
|
||||
@@ -13,17 +13,17 @@ class Microsoft extends OAuth2
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $user = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $tokens = [];
|
||||
protected array $user = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $scopes = [
|
||||
protected array $tokens = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $scopes = [
|
||||
'offline_access',
|
||||
'user.read'
|
||||
];
|
||||
@@ -35,17 +35,17 @@ class Microsoft extends OAuth2
|
||||
{
|
||||
return 'microsoft';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getLoginURL(): string
|
||||
{
|
||||
return 'https://login.microsoftonline.com/'.$this->getTenantId().'/oauth2/v2.0/authorize?'.\http_build_query([
|
||||
return 'https://login.microsoftonline.com/' . $this->getTenantID() . '/oauth2/v2.0/authorize?' . \http_build_query([
|
||||
'client_id' => $this->appID,
|
||||
'redirect_uri' => $this->callback,
|
||||
'state'=> \json_encode($this->state),
|
||||
'scope'=> \implode(' ', $this->getScopes()),
|
||||
'state' => \json_encode($this->state),
|
||||
'scope' => \implode(' ', $this->getScopes()),
|
||||
'response_type' => 'code',
|
||||
'response_mode' => 'query'
|
||||
]);
|
||||
@@ -58,11 +58,11 @@ class Microsoft extends OAuth2
|
||||
*/
|
||||
protected function getTokens(string $code): array
|
||||
{
|
||||
if(empty($this->tokens)) {
|
||||
if (empty($this->tokens)) {
|
||||
$headers = ['Content-Type: application/x-www-form-urlencoded'];
|
||||
$this->tokens = \json_decode($this->request(
|
||||
'POST',
|
||||
'https://login.microsoftonline.com/' . $this->getTenantId() . '/oauth2/v2.0/token',
|
||||
'https://login.microsoftonline.com/' . $this->getTenantID() . '/oauth2/v2.0/token',
|
||||
$headers,
|
||||
\http_build_query([
|
||||
'code' => $code,
|
||||
@@ -83,12 +83,12 @@ class Microsoft extends OAuth2
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function refreshTokens(string $refreshToken):array
|
||||
public function refreshTokens(string $refreshToken): array
|
||||
{
|
||||
$headers = ['Content-Type: application/x-www-form-urlencoded'];
|
||||
$this->tokens = \json_decode($this->request(
|
||||
'POST',
|
||||
'https://login.microsoftonline.com/' . $this->getTenantId() . '/oauth2/v2.0/token',
|
||||
'https://login.microsoftonline.com/' . $this->getTenantID() . '/oauth2/v2.0/token',
|
||||
$headers,
|
||||
\http_build_query([
|
||||
'refresh_token' => $refreshToken,
|
||||
@@ -98,7 +98,7 @@ class Microsoft extends OAuth2
|
||||
])
|
||||
), true);
|
||||
|
||||
if(empty($this->tokens['refresh_token'])) {
|
||||
if (empty($this->tokens['refresh_token'])) {
|
||||
$this->tokens['refresh_token'] = $refreshToken;
|
||||
}
|
||||
|
||||
@@ -114,11 +114,7 @@ class Microsoft extends OAuth2
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if (isset($user['id'])) {
|
||||
return $user['id'];
|
||||
}
|
||||
|
||||
return '';
|
||||
return $user['id'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -130,11 +126,23 @@ class Microsoft extends OAuth2
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if (isset($user['userPrincipalName'])) {
|
||||
return $user['userPrincipalName'];
|
||||
}
|
||||
return $user['userPrincipalName'] ?? '';
|
||||
}
|
||||
|
||||
return '';
|
||||
/**
|
||||
* Check if the OAuth email is verified
|
||||
*
|
||||
* If present, the email is verified. This was verfied through a manual Microsoft sign up process
|
||||
*
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmailVerified(string $accessToken): bool
|
||||
{
|
||||
$email = $this->getUserEmail($accessToken);
|
||||
|
||||
return !empty($email);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -146,11 +154,7 @@ class Microsoft extends OAuth2
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if (isset($user['displayName'])) {
|
||||
return $user['displayName'];
|
||||
}
|
||||
|
||||
return '';
|
||||
return $user['displayName'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -161,7 +165,7 @@ class Microsoft extends OAuth2
|
||||
protected function getUser(string $accessToken): array
|
||||
{
|
||||
if (empty($this->user)) {
|
||||
$headers = ['Authorization: Bearer '. \urlencode($accessToken)];
|
||||
$headers = ['Authorization: Bearer ' . \urlencode($accessToken)];
|
||||
$user = $this->request('GET', 'https://graph.microsoft.com/v1.0/me', $headers);
|
||||
$this->user = \json_decode($user, true);
|
||||
}
|
||||
@@ -169,38 +173,42 @@ class Microsoft extends OAuth2
|
||||
return $this->user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the Client Secret from the JSON stored in appSecret
|
||||
* @return string
|
||||
*/
|
||||
protected function getClientSecret(): string
|
||||
{
|
||||
$secret = $this->decodeJson();
|
||||
|
||||
return (isset($secret['clientSecret'])) ? $secret['clientSecret'] : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode the JSON stored in appSecret
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function decodeJson(): array
|
||||
{
|
||||
protected function getAppSecret(): array
|
||||
{
|
||||
try {
|
||||
$secret = \json_decode($this->appSecret, true);
|
||||
$secret = \json_decode($this->appSecret, true, 512, JSON_THROW_ON_ERROR);
|
||||
} catch (\Throwable $th) {
|
||||
throw new Exception('Invalid secret');
|
||||
throw new \Exception('Invalid secret');
|
||||
}
|
||||
return $secret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the Tenant Id from the JSON stored in appSecret. Defaults to 'common' as a fallback
|
||||
* Extracts the Client Secret from the JSON stored in appSecret
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getTenantId(): string
|
||||
protected function getClientSecret(): string
|
||||
{
|
||||
$secret = $this->decodeJson();
|
||||
return (isset($secret['tenantId'])) ? $secret['tenantId'] : 'common';
|
||||
$secret = $this->getAppSecret();
|
||||
|
||||
return $secret['clientSecret'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the Tenant Id from the JSON stored in appSecret. Defaults to 'common' as a fallback
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getTenantID(): string
|
||||
{
|
||||
$secret = $this->getAppSecret();
|
||||
|
||||
return $secret['tenantID'] ?? 'common';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,29 +10,29 @@ class Mock extends OAuth2
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $version = 'v1';
|
||||
protected string $version = 'v1';
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $scopes = [
|
||||
protected array $scopes = [
|
||||
'email'
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $user = [];
|
||||
|
||||
protected array $user = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $tokens = [];
|
||||
protected array $tokens = [];
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getName():string
|
||||
public function getName(): string
|
||||
{
|
||||
return 'mock';
|
||||
}
|
||||
@@ -40,9 +40,9 @@ class Mock extends OAuth2
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getLoginURL():string
|
||||
public function getLoginURL(): string
|
||||
{
|
||||
return 'http://localhost/'.$this->version.'/mock/tests/general/oauth2?'. \http_build_query([
|
||||
return 'http://localhost/' . $this->version . '/mock/tests/general/oauth2?' . \http_build_query([
|
||||
'client_id' => $this->appID,
|
||||
'redirect_uri' => $this->callback,
|
||||
'scope' => \implode(' ', $this->getScopes()),
|
||||
@@ -57,16 +57,16 @@ class Mock extends OAuth2
|
||||
*/
|
||||
protected function getTokens(string $code): array
|
||||
{
|
||||
if(empty($this->tokens)) {
|
||||
if (empty($this->tokens)) {
|
||||
$this->tokens = \json_decode($this->request(
|
||||
'GET',
|
||||
'http://localhost/' . $this->version . '/mock/tests/general/oauth2/token?' .
|
||||
\http_build_query([
|
||||
'client_id' => $this->appID,
|
||||
'redirect_uri' => $this->callback,
|
||||
'client_secret' => $this->appSecret,
|
||||
'code' => $code
|
||||
])
|
||||
\http_build_query([
|
||||
'client_id' => $this->appID,
|
||||
'redirect_uri' => $this->callback,
|
||||
'client_secret' => $this->appSecret,
|
||||
'code' => $code
|
||||
])
|
||||
), true);
|
||||
}
|
||||
|
||||
@@ -78,20 +78,20 @@ class Mock extends OAuth2
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function refreshTokens(string $refreshToken):array
|
||||
public function refreshTokens(string $refreshToken): array
|
||||
{
|
||||
$this->tokens = \json_decode($this->request(
|
||||
'GET',
|
||||
'http://localhost/' . $this->version . '/mock/tests/general/oauth2/token?' .
|
||||
\http_build_query([
|
||||
'client_id' => $this->appID,
|
||||
'client_secret' => $this->appSecret,
|
||||
'refresh_token' => $refreshToken,
|
||||
'grant_type' => 'refresh_token'
|
||||
])
|
||||
\http_build_query([
|
||||
'client_id' => $this->appID,
|
||||
'client_secret' => $this->appSecret,
|
||||
'refresh_token' => $refreshToken,
|
||||
'grant_type' => 'refresh_token'
|
||||
])
|
||||
), true);
|
||||
|
||||
if(empty($this->tokens['refresh_token'])) {
|
||||
if (empty($this->tokens['refresh_token'])) {
|
||||
$this->tokens['refresh_token'] = $refreshToken;
|
||||
}
|
||||
|
||||
@@ -103,15 +103,11 @@ class Mock extends OAuth2
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUserID(string $accessToken):string
|
||||
public function getUserID(string $accessToken): string
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if (isset($user['id'])) {
|
||||
return $user['id'];
|
||||
}
|
||||
|
||||
return '';
|
||||
return $user['id'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -119,15 +115,23 @@ class Mock extends OAuth2
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUserEmail(string $accessToken):string
|
||||
public function getUserEmail(string $accessToken): string
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if (isset($user['email'])) {
|
||||
return $user['email'];
|
||||
}
|
||||
return $user['email'] ?? '';
|
||||
}
|
||||
|
||||
return '';
|
||||
/**
|
||||
* Check if the OAuth email is verified
|
||||
*
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmailVerified(string $accessToken): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -135,15 +139,11 @@ class Mock extends OAuth2
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUserName(string $accessToken):string
|
||||
public function getUserName(string $accessToken): string
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if (isset($user['name'])) {
|
||||
return $user['name'];
|
||||
}
|
||||
|
||||
return '';
|
||||
return $user['name'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -151,10 +151,10 @@ class Mock extends OAuth2
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getUser(string $accessToken):array
|
||||
protected function getUser(string $accessToken): array
|
||||
{
|
||||
if (empty($this->user)) {
|
||||
$user = $this->request('GET', 'http://localhost/'.$this->version.'/mock/tests/general/oauth2/user?token='.\urlencode($accessToken));
|
||||
$user = $this->request('GET', 'http://localhost/' . $this->version . '/mock/tests/general/oauth2/user?token=' . \urlencode($accessToken));
|
||||
|
||||
$this->user = \json_decode($user, true);
|
||||
}
|
||||
|
||||
@@ -9,32 +9,32 @@ class Notion extends OAuth2
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $endpoint = 'https://api.notion.com/v1';
|
||||
private string $endpoint = 'https://api.notion.com/v1';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $version = '2021-08-16';
|
||||
private string $version = '2021-08-16';
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $user = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $tokens = [];
|
||||
protected array $user = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $scopes = [];
|
||||
protected array $tokens = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $scopes = [];
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getName():string
|
||||
public function getName(): string
|
||||
{
|
||||
return 'notion';
|
||||
}
|
||||
@@ -42,9 +42,9 @@ class Notion extends OAuth2
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getLoginURL():string
|
||||
public function getLoginURL(): string
|
||||
{
|
||||
return $this->endpoint . '/oauth/authorize?'. \http_build_query([
|
||||
return $this->endpoint . '/oauth/authorize?' . \http_build_query([
|
||||
'client_id' => $this->appID,
|
||||
'redirect_uri' => $this->callback,
|
||||
'response_type' => 'code',
|
||||
@@ -60,7 +60,7 @@ class Notion extends OAuth2
|
||||
*/
|
||||
protected function getTokens(string $code): array
|
||||
{
|
||||
if(empty($this->tokens)) {
|
||||
if (empty($this->tokens)) {
|
||||
$headers = ['Authorization: Basic ' . \base64_encode($this->appID . ':' . $this->appSecret)];
|
||||
$this->tokens = \json_decode($this->request(
|
||||
'POST',
|
||||
@@ -82,7 +82,7 @@ class Notion extends OAuth2
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function refreshTokens(string $refreshToken):array
|
||||
public function refreshTokens(string $refreshToken): array
|
||||
{
|
||||
$headers = ['Authorization: Basic ' . \base64_encode($this->appID . ':' . $this->appSecret)];
|
||||
$this->tokens = \json_decode($this->request(
|
||||
@@ -95,7 +95,7 @@ class Notion extends OAuth2
|
||||
])
|
||||
), true);
|
||||
|
||||
if(empty($this->tokens['refresh_token'])) {
|
||||
if (empty($this->tokens['refresh_token'])) {
|
||||
$this->tokens['refresh_token'] = $refreshToken;
|
||||
}
|
||||
|
||||
@@ -103,51 +103,55 @@ class Notion extends OAuth2
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $accessToken
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUserID(string $accessToken):string
|
||||
public function getUserID(string $accessToken): string
|
||||
{
|
||||
$response = $this->getUser($accessToken);
|
||||
|
||||
if (isset($response['bot']['owner']['user']['id'])) {
|
||||
return $response['bot']['owner']['user']['id'];
|
||||
}
|
||||
|
||||
return '';
|
||||
return $response['bot']['owner']['user']['id'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $accessToken
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUserEmail(string $accessToken):string
|
||||
public function getUserEmail(string $accessToken): string
|
||||
{
|
||||
$response = $this->getUser($accessToken);
|
||||
|
||||
if(isset($response['bot']['owner']['user']['person']['email'])){
|
||||
return $response['bot']['owner']['user']['person']['email'];
|
||||
}
|
||||
|
||||
return '';
|
||||
return $response['bot']['owner']['user']['person']['email'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $accessToken
|
||||
* Check if the OAuth email is verified
|
||||
*
|
||||
* If present, the email is verified. This was verfied through a manual Notion sign up process
|
||||
*
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmailVerified(string $accessToken): bool
|
||||
{
|
||||
$email = $this->getUserEmail($accessToken);
|
||||
|
||||
return !empty($email);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUserName(string $accessToken):string
|
||||
public function getUserName(string $accessToken): string
|
||||
{
|
||||
$response = $this->getUser($accessToken);
|
||||
|
||||
if (isset($response['bot']['owner']['user']['name'])) {
|
||||
return $response['bot']['owner']['user']['name'];
|
||||
}
|
||||
|
||||
return '';
|
||||
return $response['bot']['owner']['user']['name'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -155,11 +159,11 @@ class Notion extends OAuth2
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getUser(string $accessToken)
|
||||
protected function getUser(string $accessToken): array
|
||||
{
|
||||
$headers = [
|
||||
'Notion-Version: ' . $this->version,
|
||||
'Authorization: Bearer '.\urlencode($accessToken)
|
||||
'Authorization: Bearer ' . \urlencode($accessToken)
|
||||
];
|
||||
|
||||
if (empty($this->user)) {
|
||||
|
||||
@@ -0,0 +1,231 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Auth\OAuth2;
|
||||
|
||||
use Appwrite\Auth\OAuth2;
|
||||
|
||||
// Reference Material
|
||||
// https://developer.okta.com/docs/guides/sign-into-web-app-redirect/php/main/
|
||||
|
||||
class Okta extends OAuth2
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $scopes = [
|
||||
'openid',
|
||||
'profile',
|
||||
'email',
|
||||
'offline_access'
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $user = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $tokens = [];
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getName(): string
|
||||
{
|
||||
return 'okta';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getLoginURL(): string
|
||||
{
|
||||
return 'https://' . $this->getOktaDomain() . '/oauth2/' . $this->getAuthorizationServerId() . '/v1/authorize?' . \http_build_query([
|
||||
'client_id' => $this->appID,
|
||||
'redirect_uri' => $this->callback,
|
||||
'state' => \json_encode($this->state),
|
||||
'scope' => \implode(' ', $this->getScopes()),
|
||||
'response_type' => 'code'
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $code
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getTokens(string $code): array
|
||||
{
|
||||
if (empty($this->tokens)) {
|
||||
$headers = ['Content-Type: application/x-www-form-urlencoded'];
|
||||
$this->tokens = \json_decode($this->request(
|
||||
'POST',
|
||||
'https://' . $this->getOktaDomain() . '/oauth2/' . $this->getAuthorizationServerId() . '/v1/token',
|
||||
$headers,
|
||||
\http_build_query([
|
||||
'code' => $code,
|
||||
'client_id' => $this->appID,
|
||||
'client_secret' => $this->getClientSecret(),
|
||||
'redirect_uri' => $this->callback,
|
||||
'scope' => \implode(' ', $this->getScopes()),
|
||||
'grant_type' => 'authorization_code'
|
||||
])
|
||||
), true);
|
||||
}
|
||||
|
||||
return $this->tokens;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $refreshToken
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function refreshTokens(string $refreshToken): array
|
||||
{
|
||||
$headers = ['Content-Type: application/x-www-form-urlencoded'];
|
||||
$this->tokens = \json_decode($this->request(
|
||||
'POST',
|
||||
'https://' . $this->getOktaDomain() . '/oauth2/' . $this->getAuthorizationServerId() . '/v1/token',
|
||||
$headers,
|
||||
\http_build_query([
|
||||
'refresh_token' => $refreshToken,
|
||||
'client_id' => $this->appID,
|
||||
'client_secret' => $this->getClientSecret(),
|
||||
'grant_type' => 'refresh_token'
|
||||
])
|
||||
), true);
|
||||
|
||||
if (empty($this->tokens['refresh_token'])) {
|
||||
$this->tokens['refresh_token'] = $refreshToken;
|
||||
}
|
||||
|
||||
return $this->tokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUserID(string $accessToken): string
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
return $user['sub'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUserEmail(string $accessToken): string
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
return $user['email'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the OAuth email is verified
|
||||
*
|
||||
* @link https://developer.okta.com/docs/reference/api/oidc/#userinfo
|
||||
*
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmailVerified(string $accessToken): bool
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if ($user['email_verified'] ?? false) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUserName(string $accessToken): string
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
return $user['name'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getUser(string $accessToken): array
|
||||
{
|
||||
if (empty($this->user)) {
|
||||
$headers = ['Authorization: Bearer ' . \urlencode($accessToken)];
|
||||
$user = $this->request('GET', 'https://' . $this->getOktaDomain() . '/oauth2/' . $this->getAuthorizationServerId() . '/v1/userinfo', $headers);
|
||||
$this->user = \json_decode($user, true);
|
||||
}
|
||||
|
||||
return $this->user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the Client Secret from the JSON stored in appSecret
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getClientSecret(): string
|
||||
{
|
||||
$secret = $this->getAppSecret();
|
||||
|
||||
return $secret['clientSecret'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the Okta Domain from the JSON stored in appSecret
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getOktaDomain(): string
|
||||
{
|
||||
$secret = $this->getAppSecret();
|
||||
|
||||
return $secret['oktaDomain'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the Okta Authorization Server ID from the JSON stored in appSecret
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getAuthorizationServerId(): string
|
||||
{
|
||||
$secret = $this->getAppSecret();
|
||||
|
||||
return $secret['authorizationServerId'] ?? 'default';
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode the JSON stored in appSecret
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getAppSecret(): array
|
||||
{
|
||||
try {
|
||||
$secret = \json_decode($this->appSecret, true, 512, JSON_THROW_ON_ERROR);
|
||||
} catch (\Throwable $th) {
|
||||
throw new \Exception('Invalid secret');
|
||||
}
|
||||
return $secret;
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,7 @@ class Paypal extends OAuth2
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $endpoint = [
|
||||
private array $endpoint = [
|
||||
'sandbox' => 'https://www.sandbox.paypal.com/',
|
||||
'live' => 'https://www.paypal.com/',
|
||||
];
|
||||
@@ -20,7 +20,7 @@ class Paypal extends OAuth2
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $resourceEndpoint = [
|
||||
private array $resourceEndpoint = [
|
||||
'sandbox' => 'https://api.sandbox.paypal.com/v1/',
|
||||
'live' => 'https://api.paypal.com/v1/',
|
||||
];
|
||||
@@ -28,22 +28,22 @@ class Paypal extends OAuth2
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $environment = 'live';
|
||||
protected string $environment = 'live';
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $user = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $tokens = [];
|
||||
protected array $user = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $scopes = [
|
||||
protected array $tokens = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $scopes = [
|
||||
'openid',
|
||||
'profile',
|
||||
'email'
|
||||
@@ -62,7 +62,7 @@ class Paypal extends OAuth2
|
||||
*/
|
||||
public function getLoginURL(): string
|
||||
{
|
||||
$url = $this->endpoint[$this->environment] . 'connect/?'.
|
||||
$url = $this->endpoint[$this->environment] . 'connect/?' .
|
||||
\http_build_query([
|
||||
'flowEntry' => 'static',
|
||||
'response_type' => 'code',
|
||||
@@ -83,7 +83,7 @@ class Paypal extends OAuth2
|
||||
*/
|
||||
protected function getTokens(string $code): array
|
||||
{
|
||||
if(empty($this->tokens)) {
|
||||
if (empty($this->tokens)) {
|
||||
$this->tokens = \json_decode($this->request(
|
||||
'POST',
|
||||
$this->resourceEndpoint[$this->environment] . 'oauth2/token',
|
||||
@@ -103,7 +103,7 @@ class Paypal extends OAuth2
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function refreshTokens(string $refreshToken):array
|
||||
public function refreshTokens(string $refreshToken): array
|
||||
{
|
||||
$this->tokens = \json_decode($this->request(
|
||||
'POST',
|
||||
@@ -115,7 +115,7 @@ class Paypal extends OAuth2
|
||||
])
|
||||
), true);
|
||||
|
||||
if(empty($this->tokens['refresh_token'])) {
|
||||
if (empty($this->tokens['refresh_token'])) {
|
||||
$this->tokens['refresh_token'] = $refreshToken;
|
||||
}
|
||||
|
||||
@@ -131,11 +131,7 @@ class Paypal extends OAuth2
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if (isset($user['payer_id'])) {
|
||||
return $user['payer_id'];
|
||||
}
|
||||
|
||||
return '';
|
||||
return $user['payer_id'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -148,12 +144,38 @@ class Paypal extends OAuth2
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if (isset($user['emails'])) {
|
||||
return $user['emails'][0]['value'];
|
||||
$email = array_filter($user['emails'], function ($email) {
|
||||
return $email['primary'] === true;
|
||||
});
|
||||
|
||||
if (!empty($email)) {
|
||||
return $email[0]['value'];
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the OAuth email is verified
|
||||
*
|
||||
* @link https://developer.paypal.com/docs/api/identity/v1/#userinfo_get
|
||||
*
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmailVerified(string $accessToken): bool
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if ($user['verified_account'] ?? false) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $accessToken
|
||||
*
|
||||
@@ -163,11 +185,7 @@ class Paypal extends OAuth2
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if (isset($user['name'])) {
|
||||
return $user['name'];
|
||||
}
|
||||
|
||||
return '';
|
||||
return $user['name'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -179,7 +197,7 @@ class Paypal extends OAuth2
|
||||
{
|
||||
$header = [
|
||||
'Content-Type: application/json',
|
||||
'Authorization: Bearer '.\urlencode($accessToken),
|
||||
'Authorization: Bearer ' . \urlencode($accessToken),
|
||||
];
|
||||
if (empty($this->user)) {
|
||||
$user = $this->request(
|
||||
|
||||
@@ -6,7 +6,7 @@ use Appwrite\Auth\OAuth2\Paypal;
|
||||
|
||||
class PaypalSandbox extends Paypal
|
||||
{
|
||||
protected $environment = 'sandbox';
|
||||
protected string $environment = 'sandbox';
|
||||
|
||||
/**
|
||||
* @return string
|
||||
|
||||
@@ -14,17 +14,17 @@ class Salesforce extends OAuth2
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $user = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $tokens = [];
|
||||
protected array $user = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $scopes = [
|
||||
protected array $tokens = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $scopes = [
|
||||
"openid"
|
||||
];
|
||||
|
||||
@@ -37,7 +37,7 @@ class Salesforce extends OAuth2
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $state
|
||||
* @param string $state
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
@@ -52,13 +52,13 @@ class Salesforce extends OAuth2
|
||||
*/
|
||||
public function getLoginURL(): string
|
||||
{
|
||||
return 'https://login.salesforce.com/services/oauth2/authorize?'.\http_build_query([
|
||||
'response_type' => 'code',
|
||||
'client_id' => $this->appID,
|
||||
'redirect_uri'=> $this->callback,
|
||||
'scope'=> \implode(' ', $this->getScopes()),
|
||||
'state' => \json_encode($this->state)
|
||||
]);
|
||||
return 'https://login.salesforce.com/services/oauth2/authorize?' . \http_build_query([
|
||||
'response_type' => 'code',
|
||||
'client_id' => $this->appID,
|
||||
'redirect_uri' => $this->callback,
|
||||
'scope' => \implode(' ', $this->getScopes()),
|
||||
'state' => \json_encode($this->state)
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -68,7 +68,7 @@ class Salesforce extends OAuth2
|
||||
*/
|
||||
protected function getTokens(string $code): array
|
||||
{
|
||||
if(empty($this->tokens)) {
|
||||
if (empty($this->tokens)) {
|
||||
$headers = [
|
||||
'Authorization: Basic ' . \base64_encode($this->appID . ':' . $this->appSecret),
|
||||
'Content-Type: application/x-www-form-urlencoded',
|
||||
@@ -93,7 +93,7 @@ class Salesforce extends OAuth2
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function refreshTokens(string $refreshToken):array
|
||||
public function refreshTokens(string $refreshToken): array
|
||||
{
|
||||
$headers = [
|
||||
'Authorization: Basic ' . \base64_encode($this->appID . ':' . $this->appSecret),
|
||||
@@ -109,7 +109,7 @@ class Salesforce extends OAuth2
|
||||
])
|
||||
), true);
|
||||
|
||||
if(empty($this->tokens['refresh_token'])) {
|
||||
if (empty($this->tokens['refresh_token'])) {
|
||||
$this->tokens['refresh_token'] = $refreshToken;
|
||||
}
|
||||
|
||||
@@ -125,11 +125,7 @@ class Salesforce extends OAuth2
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if (isset($user['user_id'])) {
|
||||
return $user['user_id'];
|
||||
}
|
||||
|
||||
return '';
|
||||
return $user['user_id'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -141,11 +137,27 @@ class Salesforce extends OAuth2
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if (isset($user['email'])) {
|
||||
return $user['email'];
|
||||
return $user['email'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the OAuth email is verified
|
||||
*
|
||||
* @link https://help.salesforce.com/s/articleView?id=sf.remoteaccess_using_userinfo_endpoint.htm&type=5
|
||||
*
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmailVerified(string $accessToken): bool
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if ($user['email_verified'] ?? false) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return '';
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -157,11 +169,7 @@ class Salesforce extends OAuth2
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if (isset($user['name'])) {
|
||||
return $user['name'];
|
||||
}
|
||||
|
||||
return '';
|
||||
return $user['name'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -172,7 +180,7 @@ class Salesforce extends OAuth2
|
||||
protected function getUser(string $accessToken): array
|
||||
{
|
||||
if (empty($this->user)) {
|
||||
$user = $this->request('GET', 'https://login.salesforce.com/services/oauth2/userinfo?access_token='.\urlencode($accessToken));
|
||||
$user = $this->request('GET', 'https://login.salesforce.com/services/oauth2/userinfo?access_token=' . \urlencode($accessToken));
|
||||
$this->user = \json_decode($user, true);
|
||||
}
|
||||
return $this->user;
|
||||
|
||||
@@ -3,24 +3,23 @@
|
||||
namespace Appwrite\Auth\OAuth2;
|
||||
|
||||
use Appwrite\Auth\OAuth2;
|
||||
use Utopia\Exception;
|
||||
|
||||
class Slack extends OAuth2
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $user = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $tokens = [];
|
||||
protected array $user = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $scopes = [
|
||||
protected array $tokens = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $scopes = [
|
||||
'identity.avatar',
|
||||
'identity.basic',
|
||||
'identity.email',
|
||||
@@ -30,7 +29,7 @@ class Slack extends OAuth2
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getName():string
|
||||
public function getName(): string
|
||||
{
|
||||
return 'slack';
|
||||
}
|
||||
@@ -38,11 +37,11 @@ class Slack extends OAuth2
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getLoginURL():string
|
||||
public function getLoginURL(): string
|
||||
{
|
||||
// https://api.slack.com/docs/oauth#step_1_-_sending_users_to_authorize_and_or_install
|
||||
return 'https://slack.com/oauth/authorize?'.\http_build_query([
|
||||
'client_id'=> $this->appID,
|
||||
return 'https://slack.com/oauth/authorize?' . \http_build_query([
|
||||
'client_id' => $this->appID,
|
||||
'scope' => \implode(' ', $this->getScopes()),
|
||||
'redirect_uri' => $this->callback,
|
||||
'state' => \json_encode($this->state)
|
||||
@@ -56,7 +55,7 @@ class Slack extends OAuth2
|
||||
*/
|
||||
protected function getTokens(string $code): array
|
||||
{
|
||||
if(empty($this->tokens)) {
|
||||
if (empty($this->tokens)) {
|
||||
// https://api.slack.com/docs/oauth#step_3_-_exchanging_a_verification_code_for_an_access_token
|
||||
$this->tokens = \json_decode($this->request(
|
||||
'GET',
|
||||
@@ -77,7 +76,7 @@ class Slack extends OAuth2
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function refreshTokens(string $refreshToken):array
|
||||
public function refreshTokens(string $refreshToken): array
|
||||
{
|
||||
$this->tokens = \json_decode($this->request(
|
||||
'GET',
|
||||
@@ -89,7 +88,7 @@ class Slack extends OAuth2
|
||||
])
|
||||
), true);
|
||||
|
||||
if(empty($this->tokens['refresh_token'])) {
|
||||
if (empty($this->tokens['refresh_token'])) {
|
||||
$this->tokens['refresh_token'] = $refreshToken;
|
||||
}
|
||||
|
||||
@@ -101,15 +100,11 @@ class Slack extends OAuth2
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUserID(string $accessToken):string
|
||||
public function getUserID(string $accessToken): string
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if (isset($user['user']['id'])) {
|
||||
return $user['user']['id'];
|
||||
}
|
||||
|
||||
return '';
|
||||
return $user['user']['id'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -117,15 +112,29 @@ class Slack extends OAuth2
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUserEmail(string $accessToken):string
|
||||
public function getUserEmail(string $accessToken): string
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if (isset($user['user']['email'])) {
|
||||
return $user['user']['email'];
|
||||
}
|
||||
return $user['user']['email'] ?? '';
|
||||
}
|
||||
|
||||
return '';
|
||||
/**
|
||||
* Check if the OAuth email is verified
|
||||
*
|
||||
* If present, the email is verified. This was verfied through a manual Slack sign up process
|
||||
*
|
||||
* @link https://slack.com/help/articles/207262907-Change-your-email-address
|
||||
*
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmailVerified(string $accessToken): bool
|
||||
{
|
||||
$email = $this->getUserEmail($accessToken);
|
||||
|
||||
return !empty($email);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -133,29 +142,26 @@ class Slack extends OAuth2
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUserName(string $accessToken):string
|
||||
public function getUserName(string $accessToken): string
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if (isset($user['user']['name'])) {
|
||||
return $user['user']['name'];
|
||||
}
|
||||
|
||||
return '';
|
||||
return $user['user']['name'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @link https://api.slack.com/methods/users.identity
|
||||
*
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getUser(string $accessToken):array
|
||||
protected function getUser(string $accessToken): array
|
||||
{
|
||||
if (empty($this->user)) {
|
||||
// https://api.slack.com/methods/users.identity
|
||||
$user = $this->request(
|
||||
'GET',
|
||||
'https://slack.com/api/users.identity?token='.\urlencode($accessToken)
|
||||
'https://slack.com/api/users.identity?token=' . \urlencode($accessToken)
|
||||
);
|
||||
|
||||
$this->user = \json_decode($user, true);
|
||||
|
||||
@@ -9,38 +9,37 @@ use Appwrite\Auth\OAuth2;
|
||||
|
||||
class Spotify extends OAuth2
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private string $endpoint = 'https://accounts.spotify.com/';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $endpoint = 'https://accounts.spotify.com/';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $resourceEndpoint = 'https://api.spotify.com/v1/';
|
||||
private string $resourceEndpoint = 'https://api.spotify.com/v1/';
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $scopes = [
|
||||
protected array $scopes = [
|
||||
'user-read-email',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $user = [];
|
||||
|
||||
protected array $user = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $tokens = [];
|
||||
protected array $tokens = [];
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getName():string
|
||||
public function getName(): string
|
||||
{
|
||||
return 'spotify';
|
||||
}
|
||||
@@ -48,9 +47,9 @@ class Spotify extends OAuth2
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getLoginURL():string
|
||||
public function getLoginURL(): string
|
||||
{
|
||||
return $this->endpoint . 'authorize?'.
|
||||
return $this->endpoint . 'authorize?' .
|
||||
\http_build_query([
|
||||
'response_type' => 'code',
|
||||
'client_id' => $this->appID,
|
||||
@@ -67,7 +66,7 @@ class Spotify extends OAuth2
|
||||
*/
|
||||
protected function getTokens(string $code): array
|
||||
{
|
||||
if(empty($this->tokens)) {
|
||||
if (empty($this->tokens)) {
|
||||
$headers = ['Authorization: Basic ' . \base64_encode($this->appID . ':' . $this->appSecret)];
|
||||
$this->tokens = \json_decode($this->request(
|
||||
'POST',
|
||||
@@ -89,7 +88,7 @@ class Spotify extends OAuth2
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function refreshTokens(string $refreshToken):array
|
||||
public function refreshTokens(string $refreshToken): array
|
||||
{
|
||||
$headers = ['Authorization: Basic ' . \base64_encode($this->appID . ':' . $this->appSecret)];
|
||||
$this->tokens = \json_decode($this->request(
|
||||
@@ -102,7 +101,7 @@ class Spotify extends OAuth2
|
||||
])
|
||||
), true);
|
||||
|
||||
if(empty($this->tokens['refresh_token'])) {
|
||||
if (empty($this->tokens['refresh_token'])) {
|
||||
$this->tokens['refresh_token'] = $refreshToken;
|
||||
}
|
||||
|
||||
@@ -110,51 +109,55 @@ class Spotify extends OAuth2
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $accessToken
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUserID(string $accessToken):string
|
||||
public function getUserID(string $accessToken): string
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if (isset($user['id'])) {
|
||||
return $user['id'];
|
||||
}
|
||||
|
||||
return '';
|
||||
return $user['id'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $accessToken
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUserEmail(string $accessToken):string
|
||||
public function getUserEmail(string $accessToken): string
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if (isset($user['email'])) {
|
||||
return $user['email'];
|
||||
}
|
||||
|
||||
return '';
|
||||
return $user['email'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $accessToken
|
||||
* Check if the OAuth email is verified
|
||||
*
|
||||
* Spotify does not assure that the email is verified
|
||||
*
|
||||
* @link https://developer.spotify.com/documentation/web-api/reference/#/operations/get-current-users-profile
|
||||
*
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmailVerified(string $accessToken): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUserName(string $accessToken):string
|
||||
public function getUserName(string $accessToken): string
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if (isset($user['display_name'])) {
|
||||
return $user['display_name'];
|
||||
}
|
||||
|
||||
return '';
|
||||
return $user['display_name'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -167,8 +170,8 @@ class Spotify extends OAuth2
|
||||
if (empty($this->user)) {
|
||||
$this->user = \json_decode($this->request(
|
||||
'GET',
|
||||
$this->resourceEndpoint . "me",
|
||||
['Authorization: Bearer '.\urlencode($accessToken)]
|
||||
$this->resourceEndpoint . 'me',
|
||||
['Authorization: Bearer ' . \urlencode($accessToken)]
|
||||
), true);
|
||||
}
|
||||
|
||||
|
||||
@@ -10,38 +10,37 @@ class Stripe extends OAuth2
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $user = [];
|
||||
|
||||
protected array $user = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $tokens = [];
|
||||
protected array $tokens = [];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $stripeAccountId = '';
|
||||
protected string $stripeAccountId = '';
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $scopes = [
|
||||
protected array $scopes = [
|
||||
'read_write',
|
||||
];
|
||||
|
||||
/**
|
||||
* @return string
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
|
||||
protected $grantType = [
|
||||
'authorize' => 'authorization_code',
|
||||
'refresh' => 'refresh_token',
|
||||
protected array $grantType = [
|
||||
'authorize' => 'authorization_code',
|
||||
'refresh' => 'refresh_token',
|
||||
];
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getName():string
|
||||
public function getName(): string
|
||||
{
|
||||
return 'stripe';
|
||||
}
|
||||
@@ -49,9 +48,9 @@ class Stripe extends OAuth2
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getLoginURL():string
|
||||
public function getLoginURL(): string
|
||||
{
|
||||
return 'https://connect.stripe.com/oauth/authorize?'. \http_build_query([
|
||||
return 'https://connect.stripe.com/oauth/authorize?' . \http_build_query([
|
||||
'response_type' => 'code', // The only option at the moment is "code."
|
||||
'client_id' => $this->appID,
|
||||
'redirect_uri' => $this->callback,
|
||||
@@ -67,7 +66,7 @@ class Stripe extends OAuth2
|
||||
*/
|
||||
protected function getTokens(string $code): array
|
||||
{
|
||||
if(empty($this->tokens)) {
|
||||
if (empty($this->tokens)) {
|
||||
$this->tokens = \json_decode($this->request(
|
||||
'POST',
|
||||
'https://connect.stripe.com/oauth/token',
|
||||
@@ -89,7 +88,7 @@ class Stripe extends OAuth2
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function refreshTokens(string $refreshToken):array
|
||||
public function refreshTokens(string $refreshToken): array
|
||||
{
|
||||
$this->tokens = \json_decode($this->request(
|
||||
'POST',
|
||||
@@ -101,7 +100,7 @@ class Stripe extends OAuth2
|
||||
])
|
||||
), true);
|
||||
|
||||
if(empty($this->tokens['refresh_token'])) {
|
||||
if (empty($this->tokens['refresh_token'])) {
|
||||
$this->tokens['refresh_token'] = $refreshToken;
|
||||
}
|
||||
|
||||
@@ -110,51 +109,59 @@ class Stripe extends OAuth2
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $accessToken
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUserID(string $accessToken):string
|
||||
public function getUserID(string $accessToken): string
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if (isset($user['id'])) {
|
||||
return $user['id'];
|
||||
}
|
||||
|
||||
return '';
|
||||
return $user['id'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $accessToken
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUserEmail(string $accessToken):string
|
||||
public function getUserEmail(string $accessToken): string
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if(empty($user)) {
|
||||
return '';
|
||||
|
||||
if (empty($user)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $user['email'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $accessToken
|
||||
* Check if the OAuth email is verified
|
||||
*
|
||||
* If present, the email is verified. This was verfied through a manual Stripe sign up process
|
||||
*
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmailVerified(string $accessToken): bool
|
||||
{
|
||||
$email = $this->getUserEmail($accessToken);
|
||||
|
||||
return !empty($email);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUserName(string $accessToken):string
|
||||
public function getUserName(string $accessToken): string
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if (isset($user['name'])) {
|
||||
return $user['name'];
|
||||
}
|
||||
|
||||
return '';
|
||||
return $user['name'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -166,15 +173,13 @@ class Stripe extends OAuth2
|
||||
{
|
||||
if (empty($this->user) && !empty($this->stripeAccountId)) {
|
||||
$this->user = \json_decode(
|
||||
$this->request(
|
||||
'GET',
|
||||
'https://api.stripe.com/v1/accounts/' . $this->stripeAccountId,
|
||||
['Authorization: Bearer '.\urlencode($accessToken)]
|
||||
),
|
||||
true
|
||||
$this->request(
|
||||
'GET',
|
||||
'https://api.stripe.com/v1/accounts/' . $this->stripeAccountId,
|
||||
['Authorization: Bearer ' . \urlencode($accessToken)]
|
||||
),
|
||||
true
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
|
||||
return $this->user;
|
||||
|
||||
@@ -9,38 +9,40 @@ use Appwrite\Auth\OAuth2;
|
||||
|
||||
class Tradeshift extends OAuth2
|
||||
{
|
||||
const TRADESHIFT_SANDBOX_API_DOMAIN = 'api-sandbox.tradeshift.com';
|
||||
const TRADESHIFT_API_DOMAIN = 'api.tradeshift.com';
|
||||
public const TRADESHIFT_SANDBOX_API_DOMAIN = 'api-sandbox.tradeshift.com';
|
||||
public const TRADESHIFT_API_DOMAIN = 'api.tradeshift.com';
|
||||
|
||||
private $apiDomain = [
|
||||
private array $apiDomain = [
|
||||
'sandbox' => self::TRADESHIFT_SANDBOX_API_DOMAIN,
|
||||
'live' => self::TRADESHIFT_API_DOMAIN,
|
||||
];
|
||||
|
||||
private $endpoint = [
|
||||
private array $endpoint = [
|
||||
'sandbox' => 'https://' . self::TRADESHIFT_SANDBOX_API_DOMAIN . '/tradeshift/',
|
||||
'live' => 'https://' . self::TRADESHIFT_API_DOMAIN . '/tradeshift/',
|
||||
];
|
||||
|
||||
private $resourceEndpoint = [
|
||||
private array $resourceEndpoint = [
|
||||
'sandbox' => 'https://' . self::TRADESHIFT_SANDBOX_API_DOMAIN . '/tradeshift/rest/external/',
|
||||
'live' => 'https://' . self::TRADESHIFT_API_DOMAIN . '/tradeshift/rest/external/',
|
||||
];
|
||||
|
||||
protected $environment = 'live';
|
||||
protected string $environment = 'live';
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $user = [];
|
||||
|
||||
protected array $user = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $tokens = [];
|
||||
protected array $tokens = [];
|
||||
|
||||
|
||||
protected $scopes = [
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $scopes = [
|
||||
'openid',
|
||||
'offline',
|
||||
];
|
||||
@@ -78,7 +80,7 @@ class Tradeshift extends OAuth2
|
||||
*/
|
||||
protected function getTokens(string $code): array
|
||||
{
|
||||
if(empty($this->tokens)) {
|
||||
if (empty($this->tokens)) {
|
||||
$this->tokens = \json_decode($this->request(
|
||||
'POST',
|
||||
$this->endpoint[$this->environment] . 'auth/token',
|
||||
@@ -98,7 +100,7 @@ class Tradeshift extends OAuth2
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function refreshTokens(string $refreshToken):array
|
||||
public function refreshTokens(string $refreshToken): array
|
||||
{
|
||||
$this->tokens = \json_decode($this->request(
|
||||
'POST',
|
||||
@@ -109,8 +111,8 @@ class Tradeshift extends OAuth2
|
||||
'refresh_token' => $refreshToken,
|
||||
])
|
||||
), true);
|
||||
|
||||
if(empty($this->tokens['refresh_token'])) {
|
||||
|
||||
if (empty($this->tokens['refresh_token'])) {
|
||||
$this->tokens['refresh_token'] = $refreshToken;
|
||||
}
|
||||
|
||||
@@ -141,6 +143,22 @@ class Tradeshift extends OAuth2
|
||||
return $user['Username'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the OAuth email is verified
|
||||
*
|
||||
* If present, the email is verified. This was verfied through a manual Tradeshift sign up process
|
||||
*
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmailVerified(string $accessToken): bool
|
||||
{
|
||||
$email = $this->getUser($accessToken);
|
||||
|
||||
return !empty($email);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $accessToken
|
||||
*
|
||||
|
||||
@@ -6,7 +6,7 @@ use Appwrite\Auth\OAuth2\Tradeshift;
|
||||
|
||||
class TradeshiftBox extends Tradeshift
|
||||
{
|
||||
protected $environment = 'sandbox';
|
||||
protected string $environment = 'sandbox';
|
||||
|
||||
/**
|
||||
* @return string
|
||||
|
||||
@@ -9,38 +9,37 @@ use Appwrite\Auth\OAuth2;
|
||||
|
||||
class Twitch extends OAuth2
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private string $endpoint = 'https://id.twitch.tv/oauth2/';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $endpoint = 'https://id.twitch.tv/oauth2/';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $resourceEndpoint = 'https://api.twitch.tv/helix/users';
|
||||
private string $resourceEndpoint = 'https://api.twitch.tv/helix/users';
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $scopes = [
|
||||
protected array $scopes = [
|
||||
'user:read:email',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $user = [];
|
||||
|
||||
protected array $user = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $tokens = [];
|
||||
protected array $tokens = [];
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getName():string
|
||||
public function getName(): string
|
||||
{
|
||||
return 'twitch';
|
||||
}
|
||||
@@ -48,9 +47,9 @@ class Twitch extends OAuth2
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getLoginURL():string
|
||||
public function getLoginURL(): string
|
||||
{
|
||||
return $this->endpoint . 'authorize?'.
|
||||
return $this->endpoint . 'authorize?' .
|
||||
\http_build_query([
|
||||
'response_type' => 'code',
|
||||
'client_id' => $this->appID,
|
||||
@@ -68,7 +67,7 @@ class Twitch extends OAuth2
|
||||
*/
|
||||
protected function getTokens(string $code): array
|
||||
{
|
||||
if(empty($this->tokens)) {
|
||||
if (empty($this->tokens)) {
|
||||
$this->tokens = \json_decode($this->request(
|
||||
'POST',
|
||||
$this->endpoint . 'token?' . \http_build_query([
|
||||
@@ -89,7 +88,7 @@ class Twitch extends OAuth2
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function refreshTokens(string $refreshToken):array
|
||||
public function refreshTokens(string $refreshToken): array
|
||||
{
|
||||
$this->tokens = \json_decode($this->request(
|
||||
'POST',
|
||||
@@ -101,7 +100,7 @@ class Twitch extends OAuth2
|
||||
])
|
||||
), true);
|
||||
|
||||
if(empty($this->tokens['refresh_token'])) {
|
||||
if (empty($this->tokens['refresh_token'])) {
|
||||
$this->tokens['refresh_token'] = $refreshToken;
|
||||
}
|
||||
|
||||
@@ -109,51 +108,57 @@ class Twitch extends OAuth2
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $accessToken
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUserID(string $accessToken):string
|
||||
public function getUserID(string $accessToken): string
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if (isset($user['id'])) {
|
||||
return $user['id'];
|
||||
}
|
||||
|
||||
return '';
|
||||
return $user['id'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $accessToken
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUserEmail(string $accessToken):string
|
||||
public function getUserEmail(string $accessToken): string
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if (isset($user['email'])) {
|
||||
return $user['email'];
|
||||
}
|
||||
|
||||
return '';
|
||||
return $user['email'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $accessToken
|
||||
* Check if the OAuth email is verified
|
||||
*
|
||||
* If present, the email is verified
|
||||
*
|
||||
* @link https://dev.twitch.tv/docs/api/reference#get-users
|
||||
*
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmailVerified(string $accessToken): bool
|
||||
{
|
||||
$email = $this->getUserEmail($accessToken);
|
||||
|
||||
return !empty($email);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUserName(string $accessToken):string
|
||||
public function getUserName(string $accessToken): string
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if (isset($user['display_name'])) {
|
||||
return $user['display_name'];
|
||||
}
|
||||
|
||||
return '';
|
||||
return $user['display_name'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -168,8 +173,8 @@ class Twitch extends OAuth2
|
||||
'GET',
|
||||
$this->resourceEndpoint,
|
||||
[
|
||||
'Authorization: Bearer '.\urlencode($accessToken),
|
||||
'Client-Id: '. \urlencode($this->appID)
|
||||
'Authorization: Bearer ' . \urlencode($accessToken),
|
||||
'Client-Id: ' . \urlencode($this->appID)
|
||||
]
|
||||
), true);
|
||||
|
||||
|
||||
@@ -1,191 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Auth\OAuth2;
|
||||
|
||||
use Appwrite\Auth\OAuth2;
|
||||
use Utopia\Exception;
|
||||
|
||||
// Reference Material
|
||||
// https://vk.com/dev/first_guide
|
||||
// https://vk.com/dev/auth_sites
|
||||
// https://vk.com/dev/api_requests
|
||||
// https://plugins.miniorange.com/guide-to-configure-vkontakte-as-oauth-server
|
||||
|
||||
class Vk extends OAuth2
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $user = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $tokens = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $scopes = [
|
||||
'openid',
|
||||
'email'
|
||||
];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $version = '5.101';
|
||||
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getName(): string
|
||||
{
|
||||
return 'vk';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getLoginURL(): string
|
||||
{
|
||||
return 'https://oauth.vk.com/authorize?' . \http_build_query([
|
||||
'client_id' => $this->appID,
|
||||
'redirect_uri' => $this->callback,
|
||||
'response_type' => 'code',
|
||||
'state' => \json_encode($this->state),
|
||||
'v' => $this->version,
|
||||
'scope' => \implode(' ', $this->getScopes())
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $code
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getTokens(string $code): array
|
||||
{
|
||||
if(empty($this->tokens)) {
|
||||
$headers = ['Content-Type: application/x-www-form-urlencoded;charset=UTF-8'];
|
||||
$this->tokens = \json_decode($this->request(
|
||||
'POST',
|
||||
'https://oauth.vk.com/access_token?',
|
||||
$headers,
|
||||
\http_build_query([
|
||||
'code' => $code,
|
||||
'client_id' => $this->appID,
|
||||
'client_secret' => $this->appSecret,
|
||||
'redirect_uri' => $this->callback
|
||||
])
|
||||
), true);
|
||||
|
||||
$this->user['email'] = $this->tokens['email'];
|
||||
$this->user['user_id'] = $this->tokens['user_id'];
|
||||
}
|
||||
|
||||
return $this->tokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $refreshToken
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function refreshTokens(string $refreshToken):array
|
||||
{
|
||||
$headers = ['Content-Type: application/x-www-form-urlencoded;charset=UTF-8'];
|
||||
$this->tokens = \json_decode($this->request(
|
||||
'POST',
|
||||
'https://oauth.vk.com/access_token?',
|
||||
$headers,
|
||||
\http_build_query([
|
||||
'refresh_token' => $refreshToken,
|
||||
'client_id' => $this->appID,
|
||||
'client_secret' => $this->appSecret,
|
||||
'grant_type' => 'refresh_token'
|
||||
])
|
||||
), true);
|
||||
|
||||
if(empty($this->tokens['refresh_token'])) {
|
||||
$this->tokens['refresh_token'] = $refreshToken;
|
||||
}
|
||||
|
||||
$this->user['email'] = $this->tokens['email'];
|
||||
$this->user['user_id'] = $this->tokens['user_id'];
|
||||
|
||||
return $this->tokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUserID(string $accessToken): string
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if (isset($user['user_id'])) {
|
||||
return $user['user_id'];
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUserEmail(string $accessToken): string
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if (isset($user['email'])) {
|
||||
return $user['email'];
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUserName(string $accessToken): string
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if (isset($user['name'])) {
|
||||
return $user['name'];
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getUser(string $accessToken): array
|
||||
{
|
||||
if (empty($this->user['name'])) {
|
||||
$user = $this->request(
|
||||
'GET',
|
||||
'https://api.vk.com/method/users.get?'. \http_build_query([
|
||||
'v' => $this->version,
|
||||
'fields' => 'id,name,email,first_name,last_name',
|
||||
'access_token' => $accessToken
|
||||
])
|
||||
);
|
||||
|
||||
$user = \json_decode($user, true);
|
||||
$this->user['name'] = $user['response'][0]['first_name'] ." ".$user['response'][0]['last_name'];
|
||||
}
|
||||
return $this->user;
|
||||
}
|
||||
}
|
||||
@@ -12,24 +12,24 @@ class WordPress extends OAuth2
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $user = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $tokens = [];
|
||||
protected array $user = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $scopes = [
|
||||
protected array $tokens = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $scopes = [
|
||||
'auth',
|
||||
];
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getName():string
|
||||
public function getName(): string
|
||||
{
|
||||
return 'wordpress';
|
||||
}
|
||||
@@ -37,9 +37,9 @@ class WordPress extends OAuth2
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getLoginURL():string
|
||||
public function getLoginURL(): string
|
||||
{
|
||||
return 'https://public-api.wordpress.com/oauth2/authorize?'. \http_build_query([
|
||||
return 'https://public-api.wordpress.com/oauth2/authorize?' . \http_build_query([
|
||||
'client_id' => $this->appID,
|
||||
'redirect_uri' => $this->callback,
|
||||
'response_type' => 'code',
|
||||
@@ -55,7 +55,7 @@ class WordPress extends OAuth2
|
||||
*/
|
||||
protected function getTokens(string $code): array
|
||||
{
|
||||
if(empty($this->tokens)) {
|
||||
if (empty($this->tokens)) {
|
||||
$this->tokens = \json_decode($this->request(
|
||||
'POST',
|
||||
'https://public-api.wordpress.com/oauth2/token',
|
||||
@@ -78,7 +78,7 @@ class WordPress extends OAuth2
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function refreshTokens(string $refreshToken):array
|
||||
public function refreshTokens(string $refreshToken): array
|
||||
{
|
||||
$this->tokens = \json_decode($this->request(
|
||||
'POST',
|
||||
@@ -92,7 +92,7 @@ class WordPress extends OAuth2
|
||||
])
|
||||
), true);
|
||||
|
||||
if(empty($this->tokens['refresh_token'])) {
|
||||
if (empty($this->tokens['refresh_token'])) {
|
||||
$this->tokens['refresh_token'] = $refreshToken;
|
||||
}
|
||||
|
||||
@@ -100,51 +100,63 @@ class WordPress extends OAuth2
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $accessToken
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUserID(string $accessToken):string
|
||||
public function getUserID(string $accessToken): string
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if (isset($user['ID'])) {
|
||||
return $user['ID'];
|
||||
return $user['ID'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUserEmail(string $accessToken): string
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if ($user['verified']) {
|
||||
return $user['email'] ?? '';
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $accessToken
|
||||
* Check if the OAuth email is verified
|
||||
*
|
||||
* @return string
|
||||
* @link https://developer.wordpress.com/docs/api/1.1/get/me/
|
||||
*
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getUserEmail(string $accessToken):string
|
||||
public function isEmailVerified(string $accessToken): bool
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if (isset($user['email']) && $user['verified']) {
|
||||
return $user['email'];
|
||||
if ($user['email_verified'] ?? false) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return '';
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $accessToken
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUserName(string $accessToken):string
|
||||
public function getUserName(string $accessToken): string
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if (isset($user['username'])) {
|
||||
return $user['username'];
|
||||
}
|
||||
|
||||
return '';
|
||||
return $user['username'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -155,7 +167,7 @@ class WordPress extends OAuth2
|
||||
protected function getUser(string $accessToken)
|
||||
{
|
||||
if (empty($this->user)) {
|
||||
$this->user = \json_decode($this->request('GET', 'https://public-api.wordpress.com/rest/v1/me', ['Authorization: Bearer '.$accessToken]), true);
|
||||
$this->user = \json_decode($this->request('GET', 'https://public-api.wordpress.com/rest/v1/me', ['Authorization: Bearer ' . $accessToken]), true);
|
||||
}
|
||||
|
||||
return $this->user;
|
||||
|
||||
@@ -9,21 +9,20 @@ use Appwrite\Auth\OAuth2;
|
||||
|
||||
class Yahoo extends OAuth2
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private string $endpoint = 'https://api.login.yahoo.com/oauth2/';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $endpoint = 'https://api.login.yahoo.com/oauth2/';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $resourceEndpoint = 'https://api.login.yahoo.com/openid/v1/userinfo';
|
||||
private string $resourceEndpoint = 'https://api.login.yahoo.com/openid/v1/userinfo';
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $scopes = [
|
||||
protected array $scopes = [
|
||||
'sdct-r',
|
||||
'sdpp-w',
|
||||
];
|
||||
@@ -31,17 +30,17 @@ class Yahoo extends OAuth2
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $user = [];
|
||||
|
||||
protected array $user = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $tokens = [];
|
||||
protected array $tokens = [];
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getName():string
|
||||
public function getName(): string
|
||||
{
|
||||
return 'yahoo';
|
||||
}
|
||||
@@ -60,9 +59,9 @@ class Yahoo extends OAuth2
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getLoginURL():string
|
||||
public function getLoginURL(): string
|
||||
{
|
||||
return $this->endpoint . 'request_auth?'.
|
||||
return $this->endpoint . 'request_auth?' .
|
||||
\http_build_query([
|
||||
'response_type' => 'code',
|
||||
'client_id' => $this->appID,
|
||||
@@ -79,7 +78,7 @@ class Yahoo extends OAuth2
|
||||
*/
|
||||
protected function getTokens(string $code): array
|
||||
{
|
||||
if(empty($this->tokens)) {
|
||||
if (empty($this->tokens)) {
|
||||
$headers = [
|
||||
'Authorization: Basic ' . \base64_encode($this->appID . ':' . $this->appSecret),
|
||||
'Content-Type: application/x-www-form-urlencoded',
|
||||
@@ -105,7 +104,7 @@ class Yahoo extends OAuth2
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function refreshTokens(string $refreshToken):array
|
||||
public function refreshTokens(string $refreshToken): array
|
||||
{
|
||||
$headers = [
|
||||
'Authorization: Basic ' . \base64_encode($this->appID . ':' . $this->appSecret),
|
||||
@@ -122,7 +121,7 @@ class Yahoo extends OAuth2
|
||||
])
|
||||
), true);
|
||||
|
||||
if(empty($this->tokens['refresh_token'])) {
|
||||
if (empty($this->tokens['refresh_token'])) {
|
||||
$this->tokens['refresh_token'] = $refreshToken;
|
||||
}
|
||||
|
||||
@@ -130,51 +129,55 @@ class Yahoo extends OAuth2
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $accessToken
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUserID(string $accessToken):string
|
||||
public function getUserID(string $accessToken): string
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if (isset($user['sub'])) {
|
||||
return $user['sub'];
|
||||
}
|
||||
|
||||
return '';
|
||||
return $user['sub'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $accessToken
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUserEmail(string $accessToken):string
|
||||
public function getUserEmail(string $accessToken): string
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if (isset($user['email'])) {
|
||||
return $user['email'];
|
||||
}
|
||||
|
||||
return '';
|
||||
return $user['email'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $accessToken
|
||||
* Check if the OAuth email is verified
|
||||
*
|
||||
* If present, the email is verified. This was verfied through a manual Yahoo sign up process
|
||||
*
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmailVerified(string $accessToken): bool
|
||||
{
|
||||
$email = $this->getUserEmail($accessToken);
|
||||
|
||||
return !empty($email);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUserName(string $accessToken):string
|
||||
public function getUserName(string $accessToken): string
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if (isset($user['name'])) {
|
||||
return $user['name'];
|
||||
}
|
||||
|
||||
return '';
|
||||
return $user['name'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -188,7 +191,7 @@ class Yahoo extends OAuth2
|
||||
$this->user = \json_decode($this->request(
|
||||
'GET',
|
||||
$this->resourceEndpoint,
|
||||
['Authorization: Bearer '.\urlencode($accessToken)]
|
||||
['Authorization: Bearer ' . \urlencode($accessToken)]
|
||||
), true);
|
||||
}
|
||||
|
||||
|
||||
@@ -12,17 +12,17 @@ class Yammer extends OAuth2
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $endpoint = 'https://www.yammer.com/oauth2/';
|
||||
private string $endpoint = 'https://www.yammer.com/oauth2/';
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $user = [];
|
||||
|
||||
protected array $user = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $tokens = [];
|
||||
protected array $tokens = [];
|
||||
|
||||
/**
|
||||
* @return string
|
||||
@@ -37,13 +37,13 @@ class Yammer extends OAuth2
|
||||
*/
|
||||
public function getLoginURL(): string
|
||||
{
|
||||
return $this->endpoint . 'oauth2/authorize?'.
|
||||
\http_build_query([
|
||||
'client_id' => $this->appID,
|
||||
'response_type' => 'code',
|
||||
'redirect_uri' => $this->callback,
|
||||
'state' => \json_encode($this->state)
|
||||
]);
|
||||
return $this->endpoint . 'oauth2/authorize?' .
|
||||
\http_build_query([
|
||||
'client_id' => $this->appID,
|
||||
'response_type' => 'code',
|
||||
'redirect_uri' => $this->callback,
|
||||
'state' => \json_encode($this->state)
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -53,7 +53,7 @@ class Yammer extends OAuth2
|
||||
*/
|
||||
protected function getTokens(string $code): array
|
||||
{
|
||||
if(empty($this->tokens)) {
|
||||
if (empty($this->tokens)) {
|
||||
$headers = ['Content-Type: application/x-www-form-urlencoded'];
|
||||
$this->tokens = \json_decode($this->request(
|
||||
'POST',
|
||||
@@ -76,7 +76,7 @@ class Yammer extends OAuth2
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function refreshTokens(string $refreshToken):array
|
||||
public function refreshTokens(string $refreshToken): array
|
||||
{
|
||||
$headers = ['Content-Type: application/x-www-form-urlencoded'];
|
||||
$this->tokens = \json_decode($this->request(
|
||||
@@ -91,7 +91,7 @@ class Yammer extends OAuth2
|
||||
])
|
||||
), true);
|
||||
|
||||
if(empty($this->tokens['refresh_token'])) {
|
||||
if (empty($this->tokens['refresh_token'])) {
|
||||
$this->tokens['refresh_token'] = $refreshToken;
|
||||
}
|
||||
|
||||
@@ -107,11 +107,7 @@ class Yammer extends OAuth2
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if (isset($user['id'])) {
|
||||
return $user['id'];
|
||||
}
|
||||
|
||||
return '';
|
||||
return $user['id'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -123,11 +119,23 @@ class Yammer extends OAuth2
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if (isset($user['email'])) {
|
||||
return $user['email'];
|
||||
}
|
||||
|
||||
return '';
|
||||
return $user['email'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the OAuth email is verified
|
||||
*
|
||||
* If present, the email is verified. This was verfied through a manual Yammer sign up process
|
||||
*
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmailVerified(string $accessToken): bool
|
||||
{
|
||||
$email = $this->getUserEmail($accessToken);
|
||||
|
||||
return !empty($email);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -139,11 +147,7 @@ class Yammer extends OAuth2
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if (isset($user['full_name'])) {
|
||||
return $user['full_name'];
|
||||
}
|
||||
|
||||
return '';
|
||||
return $user['full_name'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -154,7 +158,7 @@ class Yammer extends OAuth2
|
||||
protected function getUser(string $accessToken): array
|
||||
{
|
||||
if (empty($this->user)) {
|
||||
$headers = ['Authorization: Bearer '. \urlencode($accessToken)];
|
||||
$headers = ['Authorization: Bearer ' . \urlencode($accessToken)];
|
||||
$user = $this->request('GET', 'https://www.yammer.com/api/v1/users/current.json', $headers);
|
||||
$this->user = \json_decode($user, true);
|
||||
}
|
||||
|
||||
@@ -14,17 +14,17 @@ class Yandex extends OAuth2
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $user = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $tokens = [];
|
||||
protected array $user = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $scopes = [];
|
||||
protected array $tokens = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $scopes = [];
|
||||
|
||||
/**
|
||||
* @return string
|
||||
@@ -35,7 +35,7 @@ class Yandex extends OAuth2
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $state
|
||||
* @param string $state
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
@@ -50,12 +50,12 @@ class Yandex extends OAuth2
|
||||
*/
|
||||
public function getLoginURL(): string
|
||||
{
|
||||
return 'https://oauth.yandex.com/authorize?'.\http_build_query([
|
||||
'response_type' => 'code',
|
||||
'client_id' => $this->appID,
|
||||
'scope'=> \implode(' ', $this->getScopes()),
|
||||
'state' => \json_encode($this->state)
|
||||
]);
|
||||
return 'https://oauth.yandex.com/authorize?' . \http_build_query([
|
||||
'response_type' => 'code',
|
||||
'client_id' => $this->appID,
|
||||
'scope' => \implode(' ', $this->getScopes()),
|
||||
'state' => \json_encode($this->state)
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -65,7 +65,7 @@ class Yandex extends OAuth2
|
||||
*/
|
||||
protected function getTokens(string $code): array
|
||||
{
|
||||
if(empty($this->tokens)) {
|
||||
if (empty($this->tokens)) {
|
||||
$headers = [
|
||||
'Authorization: Basic ' . \base64_encode($this->appID . ':' . $this->appSecret),
|
||||
'Content-Type: application/x-www-form-urlencoded',
|
||||
@@ -89,7 +89,7 @@ class Yandex extends OAuth2
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function refreshTokens(string $refreshToken):array
|
||||
public function refreshTokens(string $refreshToken): array
|
||||
{
|
||||
$headers = [
|
||||
'Authorization: Basic ' . \base64_encode($this->appID . ':' . $this->appSecret),
|
||||
@@ -105,7 +105,7 @@ class Yandex extends OAuth2
|
||||
])
|
||||
), true);
|
||||
|
||||
if(empty($this->tokens['refresh_token'])) {
|
||||
if (empty($this->tokens['refresh_token'])) {
|
||||
$this->tokens['refresh_token'] = $refreshToken;
|
||||
}
|
||||
|
||||
@@ -121,11 +121,7 @@ class Yandex extends OAuth2
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if (isset($user['id'])) {
|
||||
return $user['id'];
|
||||
}
|
||||
|
||||
return '';
|
||||
return $user['id'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -137,11 +133,19 @@ class Yandex extends OAuth2
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if (isset($user['default_email'])) {
|
||||
return $user['default_email'];
|
||||
}
|
||||
return $user['default_email'] ?? '';
|
||||
}
|
||||
|
||||
return '';
|
||||
/**
|
||||
* Check if the OAuth email is verified
|
||||
*
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmailVerified(string $accessToken): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -153,11 +157,7 @@ class Yandex extends OAuth2
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if (isset($user['display_name'])) {
|
||||
return $user['display_name'];
|
||||
}
|
||||
|
||||
return '';
|
||||
return $user['display_name'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -168,7 +168,7 @@ class Yandex extends OAuth2
|
||||
protected function getUser(string $accessToken): array
|
||||
{
|
||||
if (empty($this->user)) {
|
||||
$user = $this->request('GET', 'https://login.yandex.ru/info?'.\http_build_query([
|
||||
$user = $this->request('GET', 'https://login.yandex.ru/info?' . \http_build_query([
|
||||
'format' => 'json',
|
||||
'oauth_token' => $accessToken
|
||||
]));
|
||||
|
||||
@@ -0,0 +1,180 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Auth\OAuth2;
|
||||
|
||||
use Appwrite\Auth\OAuth2;
|
||||
|
||||
class Zoom extends OAuth2
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private string $endpoint = 'https://zoom.us';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private string $version = '2022-03-26';
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $user = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $tokens = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $scopes = [
|
||||
'user_info:read'
|
||||
];
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getName(): string
|
||||
{
|
||||
return 'zoom';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getLoginURL(): string
|
||||
{
|
||||
return $this->endpoint . '/oauth/authorize?' . \http_build_query([
|
||||
'client_id' => $this->appID,
|
||||
'redirect_uri' => $this->callback,
|
||||
'response_type' => 'code',
|
||||
'scope' => \implode(' ', $this->getScopes()),
|
||||
'state' => \json_encode($this->state),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $code
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getTokens(string $code): array
|
||||
{
|
||||
if (empty($this->tokens)) {
|
||||
$headers = ['Authorization: Basic ' . \base64_encode($this->appID . ':' . $this->appSecret), 'Content-Type: application/x-www-form-urlencoded'];
|
||||
$this->tokens = \json_decode($this->request(
|
||||
'POST',
|
||||
$this->endpoint . '/oauth/token',
|
||||
$headers,
|
||||
\http_build_query([
|
||||
'grant_type' => 'authorization_code',
|
||||
'redirect_uri' => $this->callback,
|
||||
'code' => $code
|
||||
])
|
||||
), true);
|
||||
}
|
||||
|
||||
return $this->tokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $refreshToken
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function refreshTokens(string $refreshToken): array
|
||||
{
|
||||
$headers = ['Authorization: Basic ' . \base64_encode($this->appID . ':' . $this->appSecret), 'Content-Type: application/x-www-form-urlencoded'];
|
||||
$this->tokens = \json_decode($this->request(
|
||||
'POST',
|
||||
$this->endpoint . '/oauth/token',
|
||||
$headers,
|
||||
\http_build_query([
|
||||
'grant_type' => 'refresh_token',
|
||||
'refresh_token' => $refreshToken,
|
||||
])
|
||||
), true);
|
||||
|
||||
if (empty($this->tokens['refresh_token'])) {
|
||||
$this->tokens['refresh_token'] = $refreshToken;
|
||||
}
|
||||
|
||||
return $this->tokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUserID(string $accessToken): string
|
||||
{
|
||||
$response = $this->getUser($accessToken);
|
||||
|
||||
return $response['id'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUserEmail(string $accessToken): string
|
||||
{
|
||||
$response = $this->getUser($accessToken);
|
||||
|
||||
return $response['email'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the OAuth email is verified
|
||||
*
|
||||
* @link https://marketplace.zoom.us/docs/api-reference/zoom-api/methods/#operation/user
|
||||
*
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmailVerified(string $accessToken): bool
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if (($user['verified'] ?? false) === 1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUserName(string $accessToken): string
|
||||
{
|
||||
$response = $this->getUser($accessToken);
|
||||
|
||||
return ($response['first_name'] ?? '') . ' ' . ($response['last_name'] ?? '');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getUser(string $accessToken)
|
||||
{
|
||||
$headers = [
|
||||
'Authorization: Bearer ' . \urlencode($accessToken)
|
||||
];
|
||||
|
||||
if (empty($this->user)) {
|
||||
$this->user = \json_decode($this->request('GET', 'https://api.zoom.us/v2/users/me', $headers), true);
|
||||
}
|
||||
|
||||
return $this->user;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Auth;
|
||||
|
||||
use Appwrite\Extend\Exception;
|
||||
|
||||
abstract class Phone
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected string $user;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected string $secret;
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
*/
|
||||
public function __construct(string $user, string $secret)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->secret = $secret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send Message to phone.
|
||||
* @param string $from
|
||||
* @param string $to
|
||||
* @param string $message
|
||||
* @return void
|
||||
*/
|
||||
abstract public function send(string $from, string $to, string $message): void;
|
||||
|
||||
/**
|
||||
* @param string $method
|
||||
* @param string $url
|
||||
* @param array $headers
|
||||
* @param string $payload
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function request(string $method, string $url, array $headers = [], ?string $payload = null, ?string $userpwd = null): string
|
||||
{
|
||||
$ch = \curl_init($url);
|
||||
|
||||
\curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
|
||||
\curl_setopt($ch, CURLOPT_HEADER, 0);
|
||||
\curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
||||
\curl_setopt($ch, CURLOPT_USERAGENT, 'Appwrite Phone Authentication');
|
||||
|
||||
if (!is_null($payload)) {
|
||||
\curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
|
||||
}
|
||||
|
||||
if (!is_null($userpwd)) {
|
||||
\curl_setopt($ch, CURLOPT_USERPWD, $userpwd);
|
||||
}
|
||||
|
||||
$headers[] = 'Content-length: ' . \strlen($payload);
|
||||
|
||||
\curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
|
||||
|
||||
$response = (string) \curl_exec($ch);
|
||||
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
|
||||
\curl_close($ch);
|
||||
|
||||
if ($code >= 400) {
|
||||
throw new Exception($response);
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate 6 random digits for phone verification.
|
||||
*
|
||||
* @param int $digits
|
||||
* @return string
|
||||
*/
|
||||
public function generateSecretDigits(int $digits = 6): string
|
||||
{
|
||||
return substr(str_shuffle("0123456789"), 0, $digits);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Auth\Phone;
|
||||
|
||||
use Appwrite\Auth\Phone;
|
||||
|
||||
class Mock extends Phone
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public static string $defaultDigits = '123456';
|
||||
|
||||
/**
|
||||
* @param string $from
|
||||
* @param string $to
|
||||
* @param string $message
|
||||
* @return void
|
||||
*/
|
||||
public function send(string $from, string $to, string $message): void
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $digits
|
||||
* @return string
|
||||
*/
|
||||
public function generateSecretDigits(int $digits = 6): string
|
||||
{
|
||||
return self::$defaultDigits;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Auth\Phone;
|
||||
|
||||
use Appwrite\Auth\Phone;
|
||||
|
||||
// Reference Material
|
||||
// https://docs.msg91.com/p/tf9GTextN/e/Irz7-x1PK/MSG91
|
||||
|
||||
class Msg91 extends Phone
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private string $endpoint = 'https://api.msg91.com/api/v5/flow/';
|
||||
|
||||
/**
|
||||
* For Flow based sending SMS sender ID should not be set in flow
|
||||
* In environment _APP_PHONE_PROVIDER format is 'phone://[senderID]:[authKey]@msg91'.
|
||||
* _APP_PHONE_FROM value is flow ID created in Msg91
|
||||
* Eg. _APP_PHONE_PROVIDER = phone://DINESH:5e1e93cad6fc054d8e759a5b@msg91
|
||||
* _APP_PHONE_FROM = 3968636f704b303135323339
|
||||
* @param string $from-> utilized from for flow id
|
||||
* @param string $to
|
||||
* @param string $message
|
||||
* @return void
|
||||
*/
|
||||
public function send(string $from, string $to, string $message): void
|
||||
{
|
||||
$to = ltrim($to, '+');
|
||||
$this->request(
|
||||
method: 'POST',
|
||||
url: $this->endpoint,
|
||||
payload: json_encode([
|
||||
'sender' => $this->user,
|
||||
'otp' => $message,
|
||||
'flow_id' => $from,
|
||||
'mobiles' => $to
|
||||
]),
|
||||
headers: [
|
||||
"content-type: application/JSON",
|
||||
"authkey: {$this->secret}",
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Auth\Phone;
|
||||
|
||||
use Appwrite\Auth\Phone;
|
||||
|
||||
// Reference Material
|
||||
// https://developer.telesign.com/enterprise/docs/sms-api-send-an-sms
|
||||
|
||||
class Telesign extends Phone
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private string $endpoint = 'https://rest-api.telesign.com/v1/messaging';
|
||||
|
||||
/**
|
||||
* @param string $from
|
||||
* @param string $to
|
||||
* @param string $message
|
||||
* @return void
|
||||
* @throws \Appwrite\Extend\Exception
|
||||
*/
|
||||
public function send(string $from, string $to, string $message): void
|
||||
{
|
||||
$to = ltrim($to, '+');
|
||||
|
||||
$this->request(
|
||||
method: 'POST',
|
||||
url: $this->endpoint,
|
||||
payload: \http_build_query([
|
||||
'message' => $message,
|
||||
'message_type' => 'otp',
|
||||
'phone_number' => $to
|
||||
]),
|
||||
userpwd: "{$this->user}:{$this->secret}"
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Auth\Phone;
|
||||
|
||||
use Appwrite\Auth\Phone;
|
||||
|
||||
// Reference Material
|
||||
// https://www.textmagic.com/docs/api/start/
|
||||
|
||||
class TextMagic extends Phone
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private string $endpoint = 'https://rest.textmagic.com/api/v2';
|
||||
|
||||
/**
|
||||
* @param string $from
|
||||
* @param string $to
|
||||
* @param string $message
|
||||
* @return void
|
||||
*/
|
||||
public function send(string $from, string $to, string $message): void
|
||||
{
|
||||
$to = ltrim($to, '+');
|
||||
$from = ltrim($from, '+');
|
||||
|
||||
$this->request(
|
||||
method: 'POST',
|
||||
url: $this->endpoint . '/messages',
|
||||
payload: \http_build_query([
|
||||
'text' => $message,
|
||||
'from' => $from,
|
||||
'phones' => $to
|
||||
]),
|
||||
headers: [
|
||||
"X-TM-Username: {$this->user}",
|
||||
"X-TM-Key: {$this->secret}",
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Auth\Phone;
|
||||
|
||||
use Appwrite\Auth\Phone;
|
||||
|
||||
// Reference Material
|
||||
// https://www.twilio.com/docs/sms/api
|
||||
|
||||
class Twilio extends Phone
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private string $endpoint = 'https://api.twilio.com/2010-04-01';
|
||||
|
||||
/**
|
||||
* @param string $from
|
||||
* @param string $to
|
||||
* @param string $message
|
||||
* @return void
|
||||
*/
|
||||
public function send(string $from, string $to, string $message): void
|
||||
{
|
||||
$this->request(
|
||||
method: 'POST',
|
||||
url: "{$this->endpoint}/Accounts/{$this->user}/Messages.json",
|
||||
payload: \http_build_query([
|
||||
'Body' => $message,
|
||||
'From' => $from,
|
||||
'To' => $to
|
||||
]),
|
||||
userpwd: "{$this->user}:{$this->secret}"
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Auth\Phone;
|
||||
|
||||
use Appwrite\Auth\Phone;
|
||||
|
||||
// Reference Material
|
||||
// https://developer.vonage.com/api/sms
|
||||
|
||||
class Vonage extends Phone
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private string $endpoint = 'https://rest.nexmo.com/sms/json';
|
||||
|
||||
/**
|
||||
* @param string $from
|
||||
* @param string $to
|
||||
* @param string $message
|
||||
* @return void
|
||||
*/
|
||||
public function send(string $from, string $to, string $message): void
|
||||
{
|
||||
$to = ltrim($to, '+');
|
||||
$headers = ['Content-Type: application/x-www-form-urlencoded'];
|
||||
|
||||
$this->request(
|
||||
method: 'POST',
|
||||
url: $this->endpoint,
|
||||
headers: $headers,
|
||||
payload: \http_build_query([
|
||||
'text' => $message,
|
||||
'from' => $from,
|
||||
'to' => $to,
|
||||
'api_key' => $this->user,
|
||||
'api_secret' => $this->secret
|
||||
])
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -34,7 +34,7 @@ class Password extends Validator
|
||||
{
|
||||
if (!\is_string($value)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (\strlen($value) < 8) {
|
||||
return false;
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Auth\Validator;
|
||||
|
||||
use Utopia\Validator;
|
||||
|
||||
/**
|
||||
* Phone.
|
||||
*
|
||||
* Validates a number for the E.164 format.
|
||||
*/
|
||||
class Phone extends Validator
|
||||
{
|
||||
/**
|
||||
* Get Description.
|
||||
*
|
||||
* Returns validator description
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDescription(): string
|
||||
{
|
||||
return "Phone number must start with a '+' can have a maximum of fifteen digits.";
|
||||
}
|
||||
|
||||
/**
|
||||
* Is valid.
|
||||
*
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid($value): bool
|
||||
{
|
||||
return is_string($value) && !!\preg_match('/^\+[1-9]\d{1,14}$/', $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is array
|
||||
*
|
||||
* Function will return true if object is array.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isArray(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Type
|
||||
*
|
||||
* Returns validator type.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getType(): string
|
||||
{
|
||||
return self::TYPE_STRING;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\DSN;
|
||||
|
||||
class DSN
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected string $scheme;
|
||||
|
||||
/**
|
||||
* @var ?string
|
||||
*/
|
||||
protected ?string $user;
|
||||
|
||||
/**
|
||||
* @var ?string
|
||||
*/
|
||||
protected ?string $password;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected string $host;
|
||||
|
||||
/**
|
||||
* @var ?string
|
||||
*/
|
||||
protected ?string $port;
|
||||
|
||||
/**
|
||||
* @var ?string
|
||||
*/
|
||||
protected ?string $database;
|
||||
|
||||
/**
|
||||
* @var ?string
|
||||
*/
|
||||
protected ?string $query;
|
||||
|
||||
/**
|
||||
* Construct
|
||||
*
|
||||
* Construct a new DSN object
|
||||
*
|
||||
* @param string $dsn
|
||||
*/
|
||||
public function __construct(string $dsn)
|
||||
{
|
||||
$parts = parse_url($dsn);
|
||||
|
||||
if (!$parts) {
|
||||
throw new \InvalidArgumentException("Unable to parse DSN: $dsn");
|
||||
}
|
||||
|
||||
$this->scheme = $parts['scheme'] ?? null;
|
||||
$this->user = $parts['user'] ?? null;
|
||||
$this->password = $parts['pass'] ?? null;
|
||||
$this->host = $parts['host'] ?? null;
|
||||
$this->port = $parts['port'] ?? null;
|
||||
$this->database = $parts['path'] ?? null;
|
||||
$this->query = $parts['query'] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the scheme.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getScheme(): string
|
||||
{
|
||||
return $this->scheme;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the user.
|
||||
*
|
||||
* @return ?string
|
||||
*/
|
||||
public function getUser(): ?string
|
||||
{
|
||||
return $this->user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the password.
|
||||
*
|
||||
* @return ?string
|
||||
*/
|
||||
public function getPassword(): ?string
|
||||
{
|
||||
return $this->password;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the host
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getHost(): string
|
||||
{
|
||||
return $this->host;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the port
|
||||
*
|
||||
* @return ?string
|
||||
*/
|
||||
public function getPort(): ?string
|
||||
{
|
||||
return $this->port;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the database
|
||||
*
|
||||
* @return ?string
|
||||
*/
|
||||
public function getDatabase(): ?string
|
||||
{
|
||||
return ltrim($this->database, '/');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the query string
|
||||
*
|
||||
* @return ?string
|
||||
*/
|
||||
public function getQuery(): ?string
|
||||
{
|
||||
return $this->query;
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,7 @@ class Compose
|
||||
|
||||
$this->compose['services'] = (isset($this->compose['services']) && is_array($this->compose['services']))
|
||||
? $this->compose['services'] : [];
|
||||
|
||||
|
||||
foreach ($this->compose['services'] as $key => &$service) {
|
||||
$service = new Service($service);
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ class Service
|
||||
public function __construct(array $service)
|
||||
{
|
||||
$this->service = $service;
|
||||
|
||||
|
||||
$ports = (isset($this->service['ports']) && is_array($this->service['ports'])) ? $this->service['ports'] : [];
|
||||
$this->service['ports'] = [];
|
||||
|
||||
@@ -54,7 +54,7 @@ class Service
|
||||
public function getImageVersion(): string
|
||||
{
|
||||
$image = $this->getImage();
|
||||
return substr($image, ((int)strpos($image, ':'))+1);
|
||||
return substr($image, ((int)strpos($image, ':')) + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -70,7 +70,7 @@ class Env
|
||||
$output = '';
|
||||
|
||||
foreach ($this->vars as $key => $value) {
|
||||
$output .= $key.'='.$value."\n";
|
||||
$output .= $key . '=' . $value . "\n";
|
||||
}
|
||||
|
||||
return $output;
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Event;
|
||||
|
||||
use Resque;
|
||||
|
||||
class Audit extends Event
|
||||
{
|
||||
protected string $resource = '';
|
||||
protected string $mode = '';
|
||||
protected string $userAgent = '';
|
||||
protected string $ip = '';
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct(Event::AUDITS_QUEUE_NAME, Event::AUDITS_CLASS_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set resource for this audit event.
|
||||
*
|
||||
* @param string $resource
|
||||
* @return self
|
||||
*/
|
||||
public function setResource(string $resource): self
|
||||
{
|
||||
$this->resource = $resource;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the set audit resource.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getResource(): string
|
||||
{
|
||||
return $this->resource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set mode for this audit event
|
||||
*
|
||||
* @param string $mode
|
||||
* @return self
|
||||
*/
|
||||
public function setMode(string $mode): self
|
||||
{
|
||||
$this->mode = $mode;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the set audit mode.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getMode(): string
|
||||
{
|
||||
return $this->mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set user agent for this audit event.
|
||||
*
|
||||
* @param string $userAgent
|
||||
* @return self
|
||||
*/
|
||||
public function setUserAgent(string $userAgent): self
|
||||
{
|
||||
$this->userAgent = $userAgent;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the set audit user agent.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUserAgent(): string
|
||||
{
|
||||
return $this->userAgent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set IP for this audit event.
|
||||
*
|
||||
* @param string $ip
|
||||
* @return self
|
||||
*/
|
||||
public function setIP(string $ip): self
|
||||
{
|
||||
$this->ip = $ip;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the set audit IP.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getIP(): string
|
||||
{
|
||||
return $this->ip;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the event and sends it to the audit worker.
|
||||
*
|
||||
* @return string|bool
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function trigger(): string|bool
|
||||
{
|
||||
return Resque::enqueue($this->queue, $this->class, [
|
||||
'project' => $this->project,
|
||||
'user' => $this->user,
|
||||
'payload' => $this->payload,
|
||||
'resource' => $this->resource,
|
||||
'mode' => $this->mode,
|
||||
'ip' => $this->ip,
|
||||
'userAgent' => $this->userAgent,
|
||||
'events' => Event::generateEvents($this->getEvent(), $this->getParams())
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Event;
|
||||
|
||||
use Resque;
|
||||
use Utopia\Database\Document;
|
||||
|
||||
class Build extends Event
|
||||
{
|
||||
protected string $type = '';
|
||||
protected ?Document $resource = null;
|
||||
protected ?Document $deployment = null;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct(Event::BUILDS_QUEUE_NAME, Event::BUILDS_CLASS_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets resource document for the build event.
|
||||
*
|
||||
* @param Document $resource
|
||||
* @return self
|
||||
*/
|
||||
public function setResource(Document $resource): self
|
||||
{
|
||||
$this->resource = $resource;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns set resource document for the build event.
|
||||
*
|
||||
* @return null|Document
|
||||
*/
|
||||
public function getResource(): ?Document
|
||||
{
|
||||
return $this->resource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets deployment for the build event.
|
||||
*
|
||||
* @param Document $deployment
|
||||
* @return self
|
||||
*/
|
||||
public function setDeployment(Document $deployment): self
|
||||
{
|
||||
$this->deployment = $deployment;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns set deployment for the build event.
|
||||
*
|
||||
* @return null|Document
|
||||
*/
|
||||
public function getDeployment(): ?Document
|
||||
{
|
||||
return $this->deployment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets type for the build event.
|
||||
*
|
||||
* @param string $type Can be `BUILD_TYPE_DEPLOYMENT` or `BUILD_TYPE_RETRY`.
|
||||
* @return self
|
||||
*/
|
||||
public function setType(string $type): self
|
||||
{
|
||||
$this->type = $type;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns set type for the function event.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getType(): string
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the function event and sends it to the functions worker.
|
||||
*
|
||||
* @return string|bool
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function trigger(): string|bool
|
||||
{
|
||||
return Resque::enqueue($this->queue, $this->class, [
|
||||
'project' => $this->project,
|
||||
'resource' => $this->resource,
|
||||
'deployment' => $this->deployment,
|
||||
'type' => $this->type
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Event;
|
||||
|
||||
use Resque;
|
||||
use Utopia\Database\Document;
|
||||
|
||||
class Certificate extends Event
|
||||
{
|
||||
protected bool $skipRenewCheck = false;
|
||||
protected ?Document $domain = null;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct(Event::CERTIFICATES_QUEUE_NAME, Event::CERTIFICATES_CLASS_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set domain for this certificates event.
|
||||
*
|
||||
* @param Document $domain
|
||||
* @return self
|
||||
*/
|
||||
public function setDomain(Document $domain): self
|
||||
{
|
||||
$this->domain = $domain;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the set domain for this certificate event.
|
||||
*
|
||||
* @return null|Document
|
||||
*/
|
||||
public function getDomain(): ?Document
|
||||
{
|
||||
return $this->domain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set if the certificate needs to be validated.
|
||||
*
|
||||
* @param bool $skipRenewCheck
|
||||
* @return self
|
||||
*/
|
||||
public function setSkipRenewCheck(bool $skipRenewCheck): self
|
||||
{
|
||||
$this->skipRenewCheck = $skipRenewCheck;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return if the certificate needs be validated.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getSkipRenewCheck(): bool
|
||||
{
|
||||
return $this->skipRenewCheck;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the event and sends it to the certificates worker.
|
||||
*
|
||||
* @return string|bool
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function trigger(): string|bool
|
||||
{
|
||||
return Resque::enqueue($this->queue, $this->class, [
|
||||
'project' => $this->project,
|
||||
'domain' => $this->domain,
|
||||
'skipRenewCheck' => $this->skipRenewCheck
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Event;
|
||||
|
||||
use Resque;
|
||||
use Utopia\Database\Document;
|
||||
|
||||
class Database extends Event
|
||||
{
|
||||
protected string $type = '';
|
||||
protected ?Document $database = null;
|
||||
protected ?Document $collection = null;
|
||||
protected ?Document $document = null;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct(Event::DATABASE_QUEUE_NAME, Event::DATABASE_CLASS_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the type for this database event (use the constants starting with DATABASE_TYPE_*).
|
||||
*
|
||||
* @param string $type
|
||||
* @return self
|
||||
*/
|
||||
public function setType(string $type): self
|
||||
{
|
||||
$this->type = $type;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the set type for the database event.
|
||||
* @return string
|
||||
*/
|
||||
public function getType(): string
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the database for this event
|
||||
*
|
||||
* @param Document $database
|
||||
* @return self
|
||||
*/
|
||||
public function setDatabase(Document $database): self
|
||||
{
|
||||
$this->database = $database;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the collection for this database event.
|
||||
*
|
||||
* @param Document $collection
|
||||
* @return self
|
||||
*/
|
||||
public function setCollection(Document $collection): self
|
||||
{
|
||||
$this->collection = $collection;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns set collection for this event.
|
||||
*
|
||||
* @return null|Document
|
||||
*/
|
||||
public function getCollection(): ?Document
|
||||
{
|
||||
return $this->collection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the document for this database event.
|
||||
*
|
||||
* @param Document $document
|
||||
* @return self
|
||||
*/
|
||||
public function setDocument(Document $document): self
|
||||
{
|
||||
$this->document = $document;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns set document for this database event.
|
||||
* @return null|Document
|
||||
*/
|
||||
public function getDocument(): ?Document
|
||||
{
|
||||
return $this->document;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the event and send it to the database worker.
|
||||
*
|
||||
* @return string|bool
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function trigger(): string|bool
|
||||
{
|
||||
return Resque::enqueue($this->queue, $this->class, [
|
||||
'project' => $this->project,
|
||||
'user' => $this->user,
|
||||
'type' => $this->type,
|
||||
'collection' => $this->collection,
|
||||
'document' => $this->document,
|
||||
'database' => $this->database,
|
||||
'events' => Event::generateEvents($this->getEvent(), $this->getParams())
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Event;
|
||||
|
||||
use Resque;
|
||||
use Utopia\Database\Document;
|
||||
|
||||
class Delete extends Event
|
||||
{
|
||||
protected string $type = '';
|
||||
protected ?int $timestamp = null;
|
||||
protected ?int $timestamp1d = null;
|
||||
protected ?int $timestamp30m = null;
|
||||
protected ?Document $document = null;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct(Event::DELETE_QUEUE_NAME, Event::DELETE_CLASS_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the type for the delete event (use the constants starting with DELETE_TYPE_*).
|
||||
*
|
||||
* @param string $type
|
||||
* @return self
|
||||
*/
|
||||
public function setType(string $type): self
|
||||
{
|
||||
$this->type = $type;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the set type for the delete event.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getType(): string
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set timestamp.
|
||||
*
|
||||
* @param int $timestamp
|
||||
* @return self
|
||||
*/
|
||||
public function setTimestamp(int $timestamp): self
|
||||
{
|
||||
$this->timestamp = $timestamp;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set timestamp for 1 day interval.
|
||||
*
|
||||
* @param int $timestamp
|
||||
* @return self
|
||||
*/
|
||||
public function setTimestamp1d(int $timestamp): self
|
||||
{
|
||||
$this->timestamp1d = $timestamp;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets timestamp for 30m interval.
|
||||
*
|
||||
* @param int $timestamp
|
||||
* @return self
|
||||
*/
|
||||
public function setTimestamp30m(int $timestamp): self
|
||||
{
|
||||
$this->timestamp30m = $timestamp;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the document for the delete event.
|
||||
*
|
||||
* @param Document $document
|
||||
* @return self
|
||||
*/
|
||||
public function setDocument(Document $document): self
|
||||
{
|
||||
$this->document = $document;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the set document for the delete event.
|
||||
*
|
||||
* @return null|Document
|
||||
*/
|
||||
public function getDocument(): ?Document
|
||||
{
|
||||
return $this->document;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes this event and sends it to the deletes worker.
|
||||
*
|
||||
* @return string|bool
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function trigger(): string|bool
|
||||
{
|
||||
return Resque::enqueue($this->queue, $this->class, [
|
||||
'project' => $this->project,
|
||||
'type' => $this->type,
|
||||
'document' => $this->document,
|
||||
'timestamp' => $this->timestamp,
|
||||
'timestamp1d' => $this->timestamp1d,
|
||||
'timestamp30m' => $this->timestamp30m
|
||||
]);
|
||||
}
|
||||
}
|
||||
+385
-54
@@ -2,58 +2,52 @@
|
||||
|
||||
namespace Appwrite\Event;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use Resque;
|
||||
use Utopia\Database\Document;
|
||||
|
||||
class Event
|
||||
{
|
||||
public const DATABASE_QUEUE_NAME = 'v1-database';
|
||||
public const DATABASE_CLASS_NAME = 'DatabaseV1';
|
||||
|
||||
const DATABASE_QUEUE_NAME= 'v1-database';
|
||||
const DATABASE_CLASS_NAME = 'DatabaseV1';
|
||||
public const DELETE_QUEUE_NAME = 'v1-deletes';
|
||||
public const DELETE_CLASS_NAME = 'DeletesV1';
|
||||
|
||||
const DELETE_QUEUE_NAME = 'v1-deletes';
|
||||
const DELETE_CLASS_NAME = 'DeletesV1';
|
||||
public const AUDITS_QUEUE_NAME = 'v1-audits';
|
||||
public const AUDITS_CLASS_NAME = 'AuditsV1';
|
||||
|
||||
const AUDITS_QUEUE_NAME = 'v1-audits';
|
||||
const AUDITS_CLASS_NAME = 'AuditsV1';
|
||||
public const MAILS_QUEUE_NAME = 'v1-mails';
|
||||
public const MAILS_CLASS_NAME = 'MailsV1';
|
||||
|
||||
const USAGE_QUEUE_NAME = 'v1-usage';
|
||||
const USAGE_CLASS_NAME = 'UsageV1';
|
||||
public const FUNCTIONS_QUEUE_NAME = 'v1-functions';
|
||||
public const FUNCTIONS_CLASS_NAME = 'FunctionsV1';
|
||||
|
||||
const MAILS_QUEUE_NAME = 'v1-mails';
|
||||
const MAILS_CLASS_NAME = 'MailsV1';
|
||||
public const WEBHOOK_QUEUE_NAME = 'v1-webhooks';
|
||||
public const WEBHOOK_CLASS_NAME = 'WebhooksV1';
|
||||
|
||||
const FUNCTIONS_QUEUE_NAME = 'v1-functions';
|
||||
const FUNCTIONS_CLASS_NAME = 'FunctionsV1';
|
||||
public const CERTIFICATES_QUEUE_NAME = 'v1-certificates';
|
||||
public const CERTIFICATES_CLASS_NAME = 'CertificatesV1';
|
||||
|
||||
const WEBHOOK_QUEUE_NAME = 'v1-webhooks';
|
||||
const WEBHOOK_CLASS_NAME = 'WebhooksV1';
|
||||
public const BUILDS_QUEUE_NAME = 'v1-builds';
|
||||
public const BUILDS_CLASS_NAME = 'BuildsV1';
|
||||
|
||||
const CERTIFICATES_QUEUE_NAME = 'v1-certificates';
|
||||
const CERTIFICATES_CLASS_NAME = 'CertificatesV1';
|
||||
public const MESSAGING_QUEUE_NAME = 'v1-messaging';
|
||||
public const MESSAGING_CLASS_NAME = 'MessagingV1';
|
||||
|
||||
const BUILDS_QUEUE_NAME = 'v1-builds';
|
||||
const BUILDS_CLASS_NAME = 'BuildsV1';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $queue = '';
|
||||
protected string $queue = '';
|
||||
protected string $class = '';
|
||||
protected string $event = '';
|
||||
protected array $params = [];
|
||||
protected array $payload = [];
|
||||
protected array $context = [];
|
||||
protected ?Document $project = null;
|
||||
protected ?Document $user = null;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $class = '';
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $params = [];
|
||||
|
||||
/**
|
||||
* Event constructor.
|
||||
*
|
||||
* @param string $queue
|
||||
* @param string $class
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(string $queue, string $class)
|
||||
{
|
||||
@@ -62,48 +56,175 @@ class Event
|
||||
}
|
||||
|
||||
/**
|
||||
* Set queue used for this event.
|
||||
*
|
||||
* @param string $queue
|
||||
* return $this
|
||||
* @return Event
|
||||
*/
|
||||
public function setQueue(string $queue): self
|
||||
{
|
||||
$this->queue = $queue;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get queue used for this event.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getQueue()
|
||||
public function getQueue(): string
|
||||
{
|
||||
return $this->queue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $class
|
||||
* return $this
|
||||
* Set event name used for this event.
|
||||
* @param string $event
|
||||
* @return Event
|
||||
*/
|
||||
public function setClass(string $class): self
|
||||
public function setEvent(string $event): self
|
||||
{
|
||||
$this->class = $class;
|
||||
$this->event = $event;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get event name used for this event.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getClass()
|
||||
public function getEvent(): string
|
||||
{
|
||||
return $this->event;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set project for this event.
|
||||
*
|
||||
* @param Document $project
|
||||
* @return self
|
||||
*/
|
||||
public function setProject(Document $project): self
|
||||
{
|
||||
$this->project = $project;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get project for this event.
|
||||
*
|
||||
* @return Document
|
||||
*/
|
||||
public function getProject(): Document
|
||||
{
|
||||
return $this->project;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set user for this event.
|
||||
*
|
||||
* @param Document $user
|
||||
* @return self
|
||||
*/
|
||||
public function setUser(Document $user): self
|
||||
{
|
||||
$this->user = $user;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get project for this event.
|
||||
*
|
||||
* @return Document
|
||||
*/
|
||||
public function getUser(): Document
|
||||
{
|
||||
return $this->user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set payload for this event.
|
||||
*
|
||||
* @param array $payload
|
||||
* @return self
|
||||
*/
|
||||
public function setPayload(array $payload): self
|
||||
{
|
||||
$this->payload = $payload;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get payload for this event.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getPayload(): array
|
||||
{
|
||||
return $this->payload;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set context for this event.
|
||||
*
|
||||
* @param string $key
|
||||
* @param Document $context
|
||||
* @return self
|
||||
*/
|
||||
public function setContext(string $key, Document $context): self
|
||||
{
|
||||
$this->context[$key] = $context;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get context for this event.
|
||||
*
|
||||
* @param string $key
|
||||
*
|
||||
* @return null|Document
|
||||
*/
|
||||
public function getContext(string $key): ?Document
|
||||
{
|
||||
return $this->context[$key] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set class used for this event.
|
||||
* @param string $class
|
||||
* @return self
|
||||
*/
|
||||
public function setClass(string $class): self
|
||||
{
|
||||
$this->class = $class;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get class used for this event.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getClass(): string
|
||||
{
|
||||
return $this->class;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
* Set param of event.
|
||||
*
|
||||
* @return $this
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
* @return self
|
||||
*/
|
||||
public function setParam(string $key, $value): self
|
||||
public function setParam(string $key, mixed $value): self
|
||||
{
|
||||
$this->params[$key] = $value;
|
||||
|
||||
@@ -111,29 +232,239 @@ class Event
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
* Get param of event.
|
||||
*
|
||||
* @return mixed|null
|
||||
* @param string $key
|
||||
* @return mixed
|
||||
*/
|
||||
public function getParam(string $key)
|
||||
public function getParam(string $key): mixed
|
||||
{
|
||||
return (isset($this->params[$key])) ? $this->params[$key] : null;
|
||||
return $this->params[$key] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all params of the event.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getParams(): array
|
||||
{
|
||||
return $this->params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute Event.
|
||||
*
|
||||
* @return string|bool
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function trigger(): void
|
||||
public function trigger(): string|bool
|
||||
{
|
||||
Resque::enqueue($this->queue, $this->class, $this->params);
|
||||
|
||||
$this->reset();
|
||||
return Resque::enqueue($this->queue, $this->class, [
|
||||
'project' => $this->project,
|
||||
'user' => $this->user,
|
||||
'payload' => $this->payload,
|
||||
'context' => $this->context,
|
||||
'events' => Event::generateEvents($this->getEvent(), $this->getParams())
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets event.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function reset(): self
|
||||
{
|
||||
$this->params = [];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses event pattern and returns the parts in their respective section.
|
||||
*
|
||||
* @param string $pattern
|
||||
* @return array
|
||||
*/
|
||||
public static function parseEventPattern(string $pattern): array
|
||||
{
|
||||
$parts = \explode('.', $pattern);
|
||||
$count = \count($parts);
|
||||
|
||||
/**
|
||||
* Identify all sections of the pattern.
|
||||
*/
|
||||
$type = $parts[0] ?? false;
|
||||
$resource = $parts[1] ?? false;
|
||||
$hasSubResource = $count > 3 && \str_starts_with($parts[3], '[');
|
||||
$hasSubSubResource = $count > 5 && \str_starts_with($parts[5], '[') && $hasSubResource;
|
||||
|
||||
if ($hasSubResource) {
|
||||
$subType = $parts[2];
|
||||
$subResource = $parts[3];
|
||||
}
|
||||
|
||||
if ($hasSubSubResource) {
|
||||
$subSubType = $parts[4];
|
||||
$subSubResource = $parts[5];
|
||||
if ($count == 8) {
|
||||
$attribute = $parts[7];
|
||||
}
|
||||
}
|
||||
|
||||
if ($hasSubResource && !$hasSubSubResource) {
|
||||
if ($count === 6) {
|
||||
$attribute = $parts[5];
|
||||
}
|
||||
}
|
||||
|
||||
if (!$hasSubResource) {
|
||||
if ($count === 4) {
|
||||
$attribute = $parts[3];
|
||||
}
|
||||
}
|
||||
|
||||
$subType ??= false;
|
||||
$subResource ??= false;
|
||||
$subSubType ??= false;
|
||||
$subSubResource ??= false;
|
||||
$attribute ??= false;
|
||||
$action = match (true) {
|
||||
!$hasSubResource && $count > 2 => $parts[2],
|
||||
$hasSubSubResource => $parts[6] ?? false,
|
||||
$hasSubResource && $count > 4 => $parts[4],
|
||||
default => false
|
||||
};
|
||||
|
||||
|
||||
|
||||
return [
|
||||
'type' => $type,
|
||||
'resource' => $resource,
|
||||
'subType' => $subType,
|
||||
'subResource' => $subResource,
|
||||
'subSubType' => $subSubType,
|
||||
'subSubResource' => $subSubResource,
|
||||
'action' => $action,
|
||||
'attribute' => $attribute,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates all possible events from a pattern.
|
||||
*
|
||||
* @param string $pattern
|
||||
* @param array $params
|
||||
* @return array
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public static function generateEvents(string $pattern, array $params = []): array
|
||||
{
|
||||
// $params = \array_filter($params, fn($param) => !\is_array($param));
|
||||
$paramKeys = \array_keys($params);
|
||||
$paramValues = \array_values($params);
|
||||
|
||||
$patterns = [];
|
||||
|
||||
$parsed = self::parseEventPattern($pattern);
|
||||
$type = $parsed['type'];
|
||||
$resource = $parsed['resource'];
|
||||
$subType = $parsed['subType'];
|
||||
$subResource = $parsed['subResource'];
|
||||
$subSubType = $parsed['subSubType'];
|
||||
$subSubResource = $parsed['subSubResource'];
|
||||
$action = $parsed['action'];
|
||||
$attribute = $parsed['attribute'];
|
||||
|
||||
if ($resource && !\in_array(\trim($resource, "\[\]"), $paramKeys)) {
|
||||
throw new InvalidArgumentException("{$resource} is missing from the params.");
|
||||
}
|
||||
|
||||
if ($subResource && !\in_array(\trim($subResource, "\[\]"), $paramKeys)) {
|
||||
throw new InvalidArgumentException("{$subResource} is missing from the params.");
|
||||
}
|
||||
|
||||
if ($subSubResource && !\in_array(\trim($subSubResource, "\[\]"), $paramKeys)) {
|
||||
throw new InvalidArgumentException("{$subSubResource} is missing from the params.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Create all possible patterns including placeholders.
|
||||
*/
|
||||
if ($action) {
|
||||
if ($subSubResource) {
|
||||
if ($attribute) {
|
||||
$patterns[] = \implode('.', [$type, $resource, $subType, $subResource, $subSubType, $subSubResource, $action, $attribute]);
|
||||
}
|
||||
$patterns[] = \implode('.', [$type, $resource, $subType, $subResource, $subSubType, $subSubResource, $action]);
|
||||
$patterns[] = \implode('.', [$type, $resource, $subType, $subResource, $subSubType, $subSubResource]);
|
||||
} elseif ($subResource) {
|
||||
if ($attribute) {
|
||||
$patterns[] = \implode('.', [$type, $resource, $subType, $subResource, $action, $attribute]);
|
||||
}
|
||||
$patterns[] = \implode('.', [$type, $resource, $subType, $subResource, $action]);
|
||||
$patterns[] = \implode('.', [$type, $resource, $subType, $subResource]);
|
||||
} else {
|
||||
$patterns[] = \implode('.', [$type, $resource, $action]);
|
||||
}
|
||||
if ($attribute) {
|
||||
$patterns[] = \implode('.', [$type, $resource, $action, $attribute]);
|
||||
}
|
||||
}
|
||||
if ($subSubResource) {
|
||||
$patterns[] = \implode('.', [$type, $resource, $subType, $subResource, $subSubType, $subSubResource]);
|
||||
}
|
||||
if ($subResource) {
|
||||
$patterns[] = \implode('.', [$type, $resource, $subType, $subResource]);
|
||||
}
|
||||
$patterns[] = \implode('.', [$type, $resource]);
|
||||
|
||||
/**
|
||||
* Removes all duplicates.
|
||||
*/
|
||||
$patterns = \array_unique($patterns);
|
||||
|
||||
/**
|
||||
* Set all possible values of the patterns and replace placeholders.
|
||||
*/
|
||||
$events = [];
|
||||
foreach ($patterns as $eventPattern) {
|
||||
$events[] = \str_replace($paramKeys, $paramValues, $eventPattern);
|
||||
$events[] = \str_replace($paramKeys, '*', $eventPattern);
|
||||
foreach ($paramKeys as $key) {
|
||||
foreach ($paramKeys as $current) {
|
||||
if ($subSubResource) {
|
||||
foreach ($paramKeys as $subCurrent) {
|
||||
if ($subCurrent === $current || $subCurrent === $key) {
|
||||
continue;
|
||||
}
|
||||
$filtered1 = \array_filter($paramKeys, fn(string $k) => $k === $subCurrent);
|
||||
$events[] = \str_replace($paramKeys, $paramValues, \str_replace($filtered1, '*', $eventPattern));
|
||||
$filtered2 = \array_filter($paramKeys, fn(string $k) => $k === $current);
|
||||
$events[] = \str_replace($paramKeys, $paramValues, \str_replace($filtered2, '*', \str_replace($filtered1, '*', $eventPattern)));
|
||||
$events[] = \str_replace($paramKeys, $paramValues, \str_replace($filtered2, '*', $eventPattern));
|
||||
}
|
||||
} else {
|
||||
if ($current === $key) {
|
||||
continue;
|
||||
}
|
||||
$filtered = \array_filter($paramKeys, fn(string $k) => $k === $current);
|
||||
$events[] = \str_replace($paramKeys, $paramValues, \str_replace($filtered, '*', $eventPattern));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove [] from the events.
|
||||
*/
|
||||
$events = \array_map(fn (string $event) => \str_replace(['[', ']'], '', $event), $events);
|
||||
$events = \array_unique($events);
|
||||
|
||||
/**
|
||||
* Force a non-assoc array.
|
||||
*/
|
||||
return \array_values($events);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,178 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Event;
|
||||
|
||||
use DateTime;
|
||||
use Resque;
|
||||
use ResqueScheduler;
|
||||
use Utopia\Database\Document;
|
||||
|
||||
class Func extends Event
|
||||
{
|
||||
protected string $jwt = '';
|
||||
protected string $type = '';
|
||||
protected string $data = '';
|
||||
protected ?Document $function = null;
|
||||
protected ?Document $execution = null;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct(Event::FUNCTIONS_QUEUE_NAME, Event::FUNCTIONS_CLASS_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets function document for the function event.
|
||||
*
|
||||
* @param Document $function
|
||||
* @return self
|
||||
*/
|
||||
public function setFunction(Document $function): self
|
||||
{
|
||||
$this->function = $function;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns set function document for the function event.
|
||||
*
|
||||
* @return null|Document
|
||||
*/
|
||||
public function getFunction(): ?Document
|
||||
{
|
||||
return $this->function;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets execution for the function event.
|
||||
*
|
||||
* @param Document $execution
|
||||
* @return self
|
||||
*/
|
||||
public function setExecution(Document $execution): self
|
||||
{
|
||||
$this->execution = $execution;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns set execution for the function event.
|
||||
*
|
||||
* @return null|Document
|
||||
*/
|
||||
public function getExecution(): ?Document
|
||||
{
|
||||
return $this->execution;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets type for the function event.
|
||||
*
|
||||
* @param string $type Can be `schedule`, `event` or `http`.
|
||||
* @return self
|
||||
*/
|
||||
public function setType(string $type): self
|
||||
{
|
||||
$this->type = $type;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns set type for the function event.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getType(): string
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets custom data for the function event.
|
||||
*
|
||||
* @param string $data
|
||||
* @return self
|
||||
*/
|
||||
public function setData(string $data): self
|
||||
{
|
||||
$this->data = $data;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns set custom data for the function event.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getData(): string
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets JWT for the function event.
|
||||
*
|
||||
* @param string $jwt
|
||||
* @return self
|
||||
*/
|
||||
public function setJWT(string $jwt): self
|
||||
{
|
||||
$this->jwt = $jwt;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns set JWT for the function event.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getJWT(): string
|
||||
{
|
||||
return $this->jwt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the function event and sends it to the functions worker.
|
||||
*
|
||||
* @return string|bool
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function trigger(): string|bool
|
||||
{
|
||||
return Resque::enqueue($this->queue, $this->class, [
|
||||
'project' => $this->project,
|
||||
'user' => $this->user,
|
||||
'function' => $this->function,
|
||||
'execution' => $this->execution,
|
||||
'type' => $this->type,
|
||||
'jwt' => $this->jwt,
|
||||
'payload' => $this->payload,
|
||||
'data' => $this->data
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules the function event and schedules it in the functions worker queue.
|
||||
*
|
||||
* @param \DateTime|int $at
|
||||
* @return void
|
||||
* @throws \Resque_Exception
|
||||
* @throws \ResqueScheduler_InvalidTimestampException
|
||||
*/
|
||||
public function schedule(DateTime|int $at): void
|
||||
{
|
||||
ResqueScheduler::enqueueAt($at, $this->queue, $this->class, [
|
||||
'project' => $this->project,
|
||||
'user' => $this->user,
|
||||
'function' => $this->function,
|
||||
'execution' => $this->execution,
|
||||
'type' => $this->type,
|
||||
'payload' => $this->payload,
|
||||
'data' => $this->data
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Event;
|
||||
|
||||
use Resque;
|
||||
use Utopia\Database\Document;
|
||||
|
||||
class Mail extends Event
|
||||
{
|
||||
protected string $recipient = '';
|
||||
protected string $url = '';
|
||||
protected string $type = '';
|
||||
protected string $name = '';
|
||||
protected string $locale = '';
|
||||
protected ?Document $team = null;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct(Event::MAILS_QUEUE_NAME, Event::MAILS_CLASS_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets team for the mail event.
|
||||
*
|
||||
* @param Document $team
|
||||
* @return self
|
||||
*/
|
||||
public function setTeam(Document $team): self
|
||||
{
|
||||
$this->team = $team;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns set team for the mail event.
|
||||
*
|
||||
* @return null|Document
|
||||
*/
|
||||
public function getTeam(): ?Document
|
||||
{
|
||||
return $this->team;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets recipient for the mail event.
|
||||
*
|
||||
* @param string $recipient
|
||||
* @return self
|
||||
*/
|
||||
public function setRecipient(string $recipient): self
|
||||
{
|
||||
$this->recipient = $recipient;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns set recipient for mail event.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getRecipient(): string
|
||||
{
|
||||
return $this->recipient;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets url for the mail event.
|
||||
*
|
||||
* @param string $url
|
||||
* @return self
|
||||
*/
|
||||
public function setUrl(string $url): self
|
||||
{
|
||||
$this->url = $url;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns set url for the mail event.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getURL(): string
|
||||
{
|
||||
return $this->url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets type for the mail event (use the constants starting with MAIL_TYPE_*).
|
||||
*
|
||||
* @param string $type
|
||||
* @return self
|
||||
*/
|
||||
public function setType(string $type): self
|
||||
{
|
||||
$this->type = $type;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns set type for the mail event.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getType(): string
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets name for the mail event.
|
||||
*
|
||||
* @param string $name
|
||||
* @return self
|
||||
*/
|
||||
public function setName(string $name): self
|
||||
{
|
||||
$this->name = $name;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns set name for the mail event.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets locale for the mail event.
|
||||
*
|
||||
* @param string $locale
|
||||
* @return self
|
||||
*/
|
||||
public function setLocale(string $locale): self
|
||||
{
|
||||
$this->locale = $locale;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns set locale for the mail event.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getLocale(): string
|
||||
{
|
||||
return $this->locale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the event and sends it to the mails worker.
|
||||
*
|
||||
* @return string|bool
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function trigger(): string|bool
|
||||
{
|
||||
return Resque::enqueue($this->queue, $this->class, [
|
||||
'project' => $this->project,
|
||||
'user' => $this->user,
|
||||
'payload' => $this->payload,
|
||||
'recipient' => $this->recipient,
|
||||
'url' => $this->url,
|
||||
'locale' => $this->locale,
|
||||
'type' => $this->type,
|
||||
'name' => $this->name,
|
||||
'team' => $this->team,
|
||||
'events' => Event::generateEvents($this->getEvent(), $this->getParams())
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Event;
|
||||
|
||||
use Resque;
|
||||
|
||||
class Phone extends Event
|
||||
{
|
||||
protected string $recipient = '';
|
||||
protected string $message = '';
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct(Event::MESSAGING_QUEUE_NAME, Event::MESSAGING_CLASS_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets recipient for the messaging event.
|
||||
*
|
||||
* @param string $recipient
|
||||
* @return self
|
||||
*/
|
||||
public function setRecipient(string $recipient): self
|
||||
{
|
||||
$this->recipient = $recipient;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns set recipient for this messaging event.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getRecipient(): string
|
||||
{
|
||||
return $this->recipient;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets url for the messaging event.
|
||||
*
|
||||
* @param string $message
|
||||
* @return self
|
||||
*/
|
||||
public function setMessage(string $message): self
|
||||
{
|
||||
$this->message = $message;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns set url for the messaging event.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getMessage(): string
|
||||
{
|
||||
return $this->message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the event and sends it to the messaging worker.
|
||||
*
|
||||
* @return string|bool
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function trigger(): string|bool
|
||||
{
|
||||
return Resque::enqueue($this->queue, $this->class, [
|
||||
'project' => $this->project,
|
||||
'user' => $this->user,
|
||||
'payload' => $this->payload,
|
||||
'recipient' => $this->recipient,
|
||||
'message' => $this->message,
|
||||
'events' => Event::generateEvents($this->getEvent(), $this->getParams())
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Event\Validator;
|
||||
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Validator;
|
||||
|
||||
class Event extends Validator
|
||||
{
|
||||
/**
|
||||
* Get Description.
|
||||
*
|
||||
* Returns validator description
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Event is not valid.';
|
||||
}
|
||||
|
||||
/**
|
||||
* Is valid.
|
||||
*
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid($value): bool
|
||||
{
|
||||
$events = Config::getParam('events', []);
|
||||
$parts = \explode('.', $value);
|
||||
$count = \count($parts);
|
||||
|
||||
if ($count < 2 || $count > 7) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Identify all sections of the pattern.
|
||||
*/
|
||||
$type = $parts[0] ?? false;
|
||||
$resource = $parts[1] ?? false;
|
||||
$hasSubResource = $count > 3 && ($events[$type]['$resource'] ?? false) && ($events[$type][$parts[2]]['$resource'] ?? false);
|
||||
$hasSubSubResource = $count > 5 && $hasSubResource && ($events[$type][$parts[2]][$parts[4]]['$resource'] ?? false);
|
||||
|
||||
if (!$type || !$resource) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($hasSubResource) {
|
||||
$subType = $parts[2];
|
||||
$subResource = $parts[3];
|
||||
}
|
||||
|
||||
if ($hasSubSubResource) {
|
||||
$subSubType = $parts[4];
|
||||
$subSubResource = $parts[5];
|
||||
if ($count === 8) {
|
||||
$attribute = $parts[7];
|
||||
}
|
||||
}
|
||||
|
||||
if ($hasSubResource && !$hasSubSubResource) {
|
||||
if ($count === 6) {
|
||||
$attribute = $parts[5];
|
||||
}
|
||||
}
|
||||
|
||||
if (!$hasSubResource) {
|
||||
if ($count === 4) {
|
||||
$attribute = $parts[3];
|
||||
}
|
||||
}
|
||||
|
||||
$subSubType ??= false;
|
||||
$subSubResource ??= false;
|
||||
$subType ??= false;
|
||||
$subResource ??= false;
|
||||
$attribute ??= false;
|
||||
|
||||
$action = match (true) {
|
||||
!$hasSubResource && $count > 2 => $parts[2],
|
||||
$hasSubSubResource => $parts[6] ?? false,
|
||||
$hasSubResource && $count > 4 => $parts[4],
|
||||
default => false
|
||||
};
|
||||
|
||||
if (!\array_key_exists($type, $events)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($subType) {
|
||||
if ($action && !\array_key_exists($action, $events[$type][$subType])) {
|
||||
return false;
|
||||
}
|
||||
if (!($subResource) || !\array_key_exists($subType, $events[$type])) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if ($action && !\array_key_exists($action, $events[$type])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ($attribute) {
|
||||
if (($subType)) {
|
||||
if (!\array_key_exists($attribute, $events[$type][$subType][$action])) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!\array_key_exists($attribute, $events[$type][$action])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is array
|
||||
*
|
||||
* Function will return true if object is array.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isArray(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Type
|
||||
*
|
||||
* Returns validator type.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getType(): string
|
||||
{
|
||||
return self::TYPE_STRING;
|
||||
}
|
||||
}
|
||||
+110
-101
@@ -6,10 +6,10 @@ class Exception extends \Exception
|
||||
{
|
||||
/**
|
||||
* Error Codes
|
||||
*
|
||||
* Naming the error types based on the following convention
|
||||
*
|
||||
* Naming the error types based on the following convention
|
||||
* <ENTITY>_<ERROR_TYPE>
|
||||
*
|
||||
*
|
||||
* Appwrite has the follwing entities:
|
||||
* - General
|
||||
* - Users
|
||||
@@ -32,133 +32,143 @@ class Exception extends \Exception
|
||||
*/
|
||||
|
||||
/** General */
|
||||
const GENERAL_UNKNOWN = 'general_unknown';
|
||||
const GENERAL_MOCK = 'general_mock';
|
||||
const GENERAL_ACCESS_FORBIDDEN = 'general_access_forbidden';
|
||||
const GENERAL_UNKNOWN_ORIGIN = 'general_unknown_origin';
|
||||
const GENERAL_SERVICE_DISABLED = 'general_service_disabled';
|
||||
const GENERAL_UNAUTHORIZED_SCOPE = 'general_unauthorized_scope';
|
||||
const GENERAL_RATE_LIMIT_EXCEEDED = 'general_rate_limit_exceeded';
|
||||
const GENERAL_SMTP_DISABLED = 'general_smtp_disabled';
|
||||
const GENERAL_ARGUMENT_INVALID = 'general_argument_invalid';
|
||||
const GENERAL_QUERY_LIMIT_EXCEEDED = 'general_query_limit_exceeded';
|
||||
const GENERAL_QUERY_INVALID = 'general_query_invalid';
|
||||
const GENERAL_ROUTE_NOT_FOUND = 'general_route_not_found';
|
||||
const GENERAL_CURSOR_NOT_FOUND = 'general_cursor_not_found';
|
||||
const GENERAL_SERVER_ERROR = 'general_server_error';
|
||||
public const GENERAL_UNKNOWN = 'general_unknown';
|
||||
public const GENERAL_MOCK = 'general_mock';
|
||||
public const GENERAL_ACCESS_FORBIDDEN = 'general_access_forbidden';
|
||||
public const GENERAL_UNKNOWN_ORIGIN = 'general_unknown_origin';
|
||||
public const GENERAL_SERVICE_DISABLED = 'general_service_disabled';
|
||||
public const GENERAL_UNAUTHORIZED_SCOPE = 'general_unauthorized_scope';
|
||||
public const GENERAL_RATE_LIMIT_EXCEEDED = 'general_rate_limit_exceeded';
|
||||
public const GENERAL_SMTP_DISABLED = 'general_smtp_disabled';
|
||||
public const GENERAL_PHONE_DISABLED = 'general_phone_disabled';
|
||||
public const GENERAL_ARGUMENT_INVALID = 'general_argument_invalid';
|
||||
public const GENERAL_QUERY_LIMIT_EXCEEDED = 'general_query_limit_exceeded';
|
||||
public const GENERAL_QUERY_INVALID = 'general_query_invalid';
|
||||
public const GENERAL_ROUTE_NOT_FOUND = 'general_route_not_found';
|
||||
public const GENERAL_CURSOR_NOT_FOUND = 'general_cursor_not_found';
|
||||
public const GENERAL_SERVER_ERROR = 'general_server_error';
|
||||
public const GENERAL_PROTOCOL_UNSUPPORTED = 'general_protocol_unsupported';
|
||||
|
||||
/** Users */
|
||||
const USER_COUNT_EXCEEDED = 'user_count_exceeded';
|
||||
const USER_JWT_INVALID = 'user_jwt_invalid';
|
||||
const USER_ALREADY_EXISTS = 'user_already_exists';
|
||||
const USER_BLOCKED = 'user_blocked';
|
||||
const USER_INVALID_TOKEN = 'user_invalid_token';
|
||||
const USER_PASSWORD_RESET_REQUIRED = 'user_password_reset_required';
|
||||
const USER_EMAIL_NOT_WHITELISTED = 'user_email_not_whitelisted';
|
||||
const USER_IP_NOT_WHITELISTED = 'user_ip_not_whitelisted';
|
||||
const USER_INVALID_CREDENTIALS = 'user_invalid_credentials';
|
||||
const USER_ANONYMOUS_CONSOLE_PROHIBITED = 'user_anonymous_console_prohibited';
|
||||
const USER_SESSION_ALREADY_EXISTS = 'user_session_already_exists';
|
||||
const USER_NOT_FOUND = 'user_not_found';
|
||||
const USER_EMAIL_ALREADY_EXISTS = 'user_email_already_exists';
|
||||
const USER_PASSWORD_MISMATCH = 'user_password_mismatch';
|
||||
const USER_SESSION_NOT_FOUND = 'user_session_not_found';
|
||||
const USER_UNAUTHORIZED = 'user_unauthorized';
|
||||
const USER_AUTH_METHOD_UNSUPPORTED = 'user_auth_method_unsupported';
|
||||
public const USER_COUNT_EXCEEDED = 'user_count_exceeded';
|
||||
public const USER_JWT_INVALID = 'user_jwt_invalid';
|
||||
public const USER_ALREADY_EXISTS = 'user_already_exists';
|
||||
public const USER_BLOCKED = 'user_blocked';
|
||||
public const USER_INVALID_TOKEN = 'user_invalid_token';
|
||||
public const USER_PASSWORD_RESET_REQUIRED = 'user_password_reset_required';
|
||||
public const USER_EMAIL_NOT_WHITELISTED = 'user_email_not_whitelisted';
|
||||
public const USER_IP_NOT_WHITELISTED = 'user_ip_not_whitelisted';
|
||||
public const USER_INVALID_CREDENTIALS = 'user_invalid_credentials';
|
||||
public const USER_ANONYMOUS_CONSOLE_PROHIBITED = 'user_anonymous_console_prohibited';
|
||||
public const USER_SESSION_ALREADY_EXISTS = 'user_session_already_exists';
|
||||
public const USER_NOT_FOUND = 'user_not_found';
|
||||
public const USER_EMAIL_ALREADY_EXISTS = 'user_email_already_exists';
|
||||
public const USER_PASSWORD_MISMATCH = 'user_password_mismatch';
|
||||
public const USER_SESSION_NOT_FOUND = 'user_session_not_found';
|
||||
public const USER_UNAUTHORIZED = 'user_unauthorized';
|
||||
public const USER_AUTH_METHOD_UNSUPPORTED = 'user_auth_method_unsupported';
|
||||
public const USER_PHONE_ALREADY_EXISTS = 'user_phone_already_exists';
|
||||
public const USER_PHONE_NOT_FOUND = 'user_phone_not_found';
|
||||
|
||||
/** Teams */
|
||||
const TEAM_NOT_FOUND = 'team_not_found';
|
||||
const TEAM_INVITE_ALREADY_EXISTS = 'team_invite_already_exists';
|
||||
const TEAM_INVITE_NOT_FOUND = 'team_invite_not_found';
|
||||
const TEAM_INVALID_SECRET = 'team_invalid_secret';
|
||||
const TEAM_MEMBERSHIP_MISMATCH = 'team_membership_mismatch';
|
||||
const TEAM_INVITE_MISMATCH = 'team_invite_mismatch';
|
||||
public const TEAM_NOT_FOUND = 'team_not_found';
|
||||
public const TEAM_INVITE_ALREADY_EXISTS = 'team_invite_already_exists';
|
||||
public const TEAM_INVITE_NOT_FOUND = 'team_invite_not_found';
|
||||
public const TEAM_INVALID_SECRET = 'team_invalid_secret';
|
||||
public const TEAM_MEMBERSHIP_MISMATCH = 'team_membership_mismatch';
|
||||
public const TEAM_INVITE_MISMATCH = 'team_invite_mismatch';
|
||||
|
||||
/** Membership */
|
||||
const MEMBERSHIP_NOT_FOUND = 'membership_not_found';
|
||||
public const MEMBERSHIP_NOT_FOUND = 'membership_not_found';
|
||||
|
||||
/** Avatars */
|
||||
const AVATAR_SET_NOT_FOUND = 'avatar_set_not_found';
|
||||
const AVATAR_NOT_FOUND = 'avatar_not_found';
|
||||
const AVATAR_IMAGE_NOT_FOUND = 'avatar_image_not_found';
|
||||
const AVATAR_REMOTE_URL_FAILED = 'avatar_remote_url_failed';
|
||||
const AVATAR_ICON_NOT_FOUND = 'avatar_icon_not_found';
|
||||
public const AVATAR_SET_NOT_FOUND = 'avatar_set_not_found';
|
||||
public const AVATAR_NOT_FOUND = 'avatar_not_found';
|
||||
public const AVATAR_IMAGE_NOT_FOUND = 'avatar_image_not_found';
|
||||
public const AVATAR_REMOTE_URL_FAILED = 'avatar_remote_url_failed';
|
||||
public const AVATAR_ICON_NOT_FOUND = 'avatar_icon_not_found';
|
||||
|
||||
/** Storage */
|
||||
const STORAGE_FILE_NOT_FOUND = 'storage_file_not_found';
|
||||
const STORAGE_DEVICE_NOT_FOUND = 'storage_device_not_found';
|
||||
const STORAGE_FILE_EMPTY = 'storage_file_empty';
|
||||
const STORAGE_FILE_TYPE_UNSUPPORTED = 'storage_file_type_unsupported';
|
||||
const STORAGE_INVALID_FILE_SIZE = 'storage_invalid_file_size';
|
||||
const STORAGE_INVALID_FILE = 'storage_invalid_file';
|
||||
const STORAGE_BUCKET_ALREADY_EXISTS = 'storage_bucket_already_exists';
|
||||
const STORAGE_BUCKET_NOT_FOUND = 'storage_bucket_not_found';
|
||||
const STORAGE_INVALID_CONTENT_RANGE = 'storage_invalid_content_range';
|
||||
const STORAGE_INVALID_RANGE = 'storage_invalid_range';
|
||||
public const STORAGE_FILE_NOT_FOUND = 'storage_file_not_found';
|
||||
public const STORAGE_DEVICE_NOT_FOUND = 'storage_device_not_found';
|
||||
public const STORAGE_FILE_EMPTY = 'storage_file_empty';
|
||||
public const STORAGE_FILE_TYPE_UNSUPPORTED = 'storage_file_type_unsupported';
|
||||
public const STORAGE_INVALID_FILE_SIZE = 'storage_invalid_file_size';
|
||||
public const STORAGE_INVALID_FILE = 'storage_invalid_file';
|
||||
public const STORAGE_BUCKET_ALREADY_EXISTS = 'storage_bucket_already_exists';
|
||||
public const STORAGE_BUCKET_NOT_FOUND = 'storage_bucket_not_found';
|
||||
public const STORAGE_INVALID_CONTENT_RANGE = 'storage_invalid_content_range';
|
||||
public const STORAGE_INVALID_RANGE = 'storage_invalid_range';
|
||||
|
||||
/** Functions */
|
||||
const FUNCTION_NOT_FOUND = 'function_not_found';
|
||||
const FUNCTION_RUNTIME_UNSUPPORTED = 'function_runtime_unsupported';
|
||||
public const FUNCTION_NOT_FOUND = 'function_not_found';
|
||||
public const FUNCTION_RUNTIME_UNSUPPORTED = 'function_runtime_unsupported';
|
||||
|
||||
/** Deployments */
|
||||
const DEPLOYMENT_NOT_FOUND = 'deployment_not_found';
|
||||
public const DEPLOYMENT_NOT_FOUND = 'deployment_not_found';
|
||||
|
||||
/** Builds */
|
||||
const BUILD_NOT_FOUND = 'build_not_found';
|
||||
const BUILD_NOT_READY = 'build_not_ready';
|
||||
const BUILD_IN_PROGRESS = 'build_in_progress';
|
||||
public const BUILD_NOT_FOUND = 'build_not_found';
|
||||
public const BUILD_NOT_READY = 'build_not_ready';
|
||||
public const BUILD_IN_PROGRESS = 'build_in_progress';
|
||||
|
||||
/** Execution */
|
||||
const EXECUTION_NOT_FOUND = 'execution_not_found';
|
||||
public const EXECUTION_NOT_FOUND = 'execution_not_found';
|
||||
|
||||
/** Databases */
|
||||
public const DATABASE_NOT_FOUND = 'database_not_found';
|
||||
public const DATABASE_ALREADY_EXISTS = 'database_already_exists';
|
||||
|
||||
/** Collections */
|
||||
const COLLECTION_NOT_FOUND = 'collection_not_found';
|
||||
const COLLECTION_ALREADY_EXISTS = 'collection_already_exists';
|
||||
const COLLECTION_LIMIT_EXCEEDED = 'collection_limit_exceeded';
|
||||
|
||||
public const COLLECTION_NOT_FOUND = 'collection_not_found';
|
||||
public const COLLECTION_ALREADY_EXISTS = 'collection_already_exists';
|
||||
public const COLLECTION_LIMIT_EXCEEDED = 'collection_limit_exceeded';
|
||||
|
||||
/** Documents */
|
||||
const DOCUMENT_NOT_FOUND = 'document_not_found';
|
||||
const DOCUMENT_INVALID_STRUCTURE = 'document_invalid_structure';
|
||||
const DOCUMENT_MISSING_PAYLOAD = 'document_missing_payload';
|
||||
const DOCUMENT_ALREADY_EXISTS = 'document_already_exists';
|
||||
public const DOCUMENT_NOT_FOUND = 'document_not_found';
|
||||
public const DOCUMENT_INVALID_STRUCTURE = 'document_invalid_structure';
|
||||
public const DOCUMENT_MISSING_PAYLOAD = 'document_missing_payload';
|
||||
public const DOCUMENT_ALREADY_EXISTS = 'document_already_exists';
|
||||
|
||||
/** Attribute */
|
||||
const ATTRIBUTE_NOT_FOUND = 'attribute_not_found';
|
||||
const ATTRIBUTE_UNKNOWN = 'attribute_unknown';
|
||||
const ATTRIBUTE_NOT_AVAILABLE = 'attribute_not_available';
|
||||
const ATTRIBUTE_FORMAT_UNSUPPORTED = 'attribute_format_unsupported';
|
||||
const ATTRIBUTE_DEFAULT_UNSUPPORTED = 'attribute_default_unsupported';
|
||||
const ATTRIBUTE_ALREADY_EXISTS = 'attribute_already_exists';
|
||||
const ATTRIBUTE_LIMIT_EXCEEDED = 'attribute_limit_exceeded';
|
||||
const ATTRIBUTE_VALUE_INVALID = 'attribute_value_invalid';
|
||||
public const ATTRIBUTE_NOT_FOUND = 'attribute_not_found';
|
||||
public const ATTRIBUTE_UNKNOWN = 'attribute_unknown';
|
||||
public const ATTRIBUTE_NOT_AVAILABLE = 'attribute_not_available';
|
||||
public const ATTRIBUTE_FORMAT_UNSUPPORTED = 'attribute_format_unsupported';
|
||||
public const ATTRIBUTE_DEFAULT_UNSUPPORTED = 'attribute_default_unsupported';
|
||||
public const ATTRIBUTE_ALREADY_EXISTS = 'attribute_already_exists';
|
||||
public const ATTRIBUTE_LIMIT_EXCEEDED = 'attribute_limit_exceeded';
|
||||
public const ATTRIBUTE_VALUE_INVALID = 'attribute_value_invalid';
|
||||
|
||||
/** Indexes */
|
||||
const INDEX_NOT_FOUND = 'index_not_found';
|
||||
const INDEX_LIMIT_EXCEEDED = 'index_limit_exceeded';
|
||||
const INDEX_ALREADY_EXISTS = 'index_already_exists';
|
||||
public const INDEX_NOT_FOUND = 'index_not_found';
|
||||
public const INDEX_LIMIT_EXCEEDED = 'index_limit_exceeded';
|
||||
public const INDEX_ALREADY_EXISTS = 'index_already_exists';
|
||||
|
||||
/** Projects */
|
||||
const PROJECT_NOT_FOUND = 'project_not_found';
|
||||
const PROJECT_UNKNOWN = 'project_unknown';
|
||||
const PROJECT_PROVIDER_DISABLED = 'project_provider_disabled';
|
||||
const PROJECT_PROVIDER_UNSUPPORTED = 'project_provider_unsupported';
|
||||
const PROJECT_INVALID_SUCCESS_URL = 'project_invalid_success_url';
|
||||
const PROJECT_INVALID_FAILURE_URL = 'project_invalid_failure_url';
|
||||
const PROJECT_MISSING_USER_ID = 'project_missing_user_id';
|
||||
public const PROJECT_NOT_FOUND = 'project_not_found';
|
||||
public const PROJECT_UNKNOWN = 'project_unknown';
|
||||
public const PROJECT_PROVIDER_DISABLED = 'project_provider_disabled';
|
||||
public const PROJECT_PROVIDER_UNSUPPORTED = 'project_provider_unsupported';
|
||||
public const PROJECT_INVALID_SUCCESS_URL = 'project_invalid_success_url';
|
||||
public const PROJECT_INVALID_FAILURE_URL = 'project_invalid_failure_url';
|
||||
public const PROJECT_MISSING_USER_ID = 'project_missing_user_id';
|
||||
public const PROJECT_RESERVED_PROJECT = 'project_reserved_project';
|
||||
public const PROJECT_KEY_EXPIRED = 'project_key_expired';
|
||||
|
||||
/** Webhooks */
|
||||
const WEBHOOK_NOT_FOUND = 'webhook_not_found';
|
||||
public const WEBHOOK_NOT_FOUND = 'webhook_not_found';
|
||||
|
||||
/** Keys */
|
||||
const KEY_NOT_FOUND = 'key_not_found';
|
||||
public const KEY_NOT_FOUND = 'key_not_found';
|
||||
|
||||
/** Platform */
|
||||
const PLATFORM_NOT_FOUND = 'platform_not_found';
|
||||
public const PLATFORM_NOT_FOUND = 'platform_not_found';
|
||||
|
||||
/** Domain */
|
||||
const DOMAIN_NOT_FOUND = 'domain_not_found';
|
||||
const DOMAIN_ALREADY_EXISTS = 'domain_already_exists';
|
||||
const DOMAIN_VERIFICATION_FAILED = 'domain_verification_failed';
|
||||
public const DOMAIN_NOT_FOUND = 'domain_not_found';
|
||||
public const DOMAIN_ALREADY_EXISTS = 'domain_already_exists';
|
||||
public const DOMAIN_VERIFICATION_FAILED = 'domain_verification_failed';
|
||||
|
||||
|
||||
private $type = '';
|
||||
@@ -172,9 +182,9 @@ class Exception extends \Exception
|
||||
|
||||
/**
|
||||
* Get the type of the exception.
|
||||
*
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
*/
|
||||
public function getType(): string
|
||||
{
|
||||
return $this->type;
|
||||
@@ -182,14 +192,13 @@ class Exception extends \Exception
|
||||
|
||||
/**
|
||||
* Set the type of the exception.
|
||||
*
|
||||
*
|
||||
* @param string $type
|
||||
*
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setType(string $type): void
|
||||
{
|
||||
$this->type = $type;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,8 +99,8 @@ class PDO extends PDONative
|
||||
{
|
||||
$this->pdo = new PDONative($this->dsn, $this->username, $this->passwd, $this->options);
|
||||
|
||||
echo '[PDO] MySQL connection restarted'.PHP_EOL;
|
||||
|
||||
echo '[PDO] MySQL connection restarted' . PHP_EOL;
|
||||
|
||||
// Connection settings
|
||||
$this->pdo->setAttribute(PDONative::ATTR_DEFAULT_FETCH_MODE, PDONative::FETCH_ASSOC); // Return arrays
|
||||
$this->pdo->setAttribute(PDONative::ATTR_ERRMODE, PDONative::ERRMODE_EXCEPTION); // Handle all errors with exceptions
|
||||
|
||||
@@ -76,7 +76,7 @@ class PDOStatement extends PDOStatementNative
|
||||
foreach ($this->values as $key => $set) {
|
||||
$this->PDOStatement->bindValue($key, $set['value'], $set['data_type']);
|
||||
}
|
||||
|
||||
|
||||
foreach ($this->params as $key => $set) {
|
||||
$this->PDOStatement->bindParam($key, $set['variable'], $set['data_type'], $set['length'], $set['driver_options']);
|
||||
}
|
||||
@@ -109,7 +109,7 @@ class PDOStatement extends PDOStatementNative
|
||||
public function fetchAll(int $fetch_style = PDO::FETCH_BOTH, mixed ...$fetch_args)
|
||||
{
|
||||
$result = $this->PDOStatement->fetchAll();
|
||||
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ namespace Appwrite\Messaging;
|
||||
|
||||
abstract class Adapter
|
||||
{
|
||||
public abstract function subscribe(string $projectId, mixed $identifier, array $roles, array $channels): void;
|
||||
public abstract function unsubscribe(mixed $identifier): void;
|
||||
public static abstract function send(string $projectId, array $payload, string $event, array $channels, array $roles, array $options): void;
|
||||
abstract public function subscribe(string $projectId, mixed $identifier, array $roles, array $channels): void;
|
||||
abstract public function unsubscribe(mixed $identifier): void;
|
||||
abstract public static function send(string $projectId, array $payload, array $events, array $channels, array $roles, array $options): void;
|
||||
}
|
||||
|
||||
@@ -10,8 +10,8 @@ class Realtime extends Adapter
|
||||
{
|
||||
/**
|
||||
* Connection Tree
|
||||
*
|
||||
* [CONNECTION_ID] ->
|
||||
*
|
||||
* [CONNECTION_ID] ->
|
||||
* 'projectId' -> [PROJECT_ID]
|
||||
* 'roles' -> [ROLE_x, ROLE_Y]
|
||||
* 'channels' -> [CHANNEL_NAME_X, CHANNEL_NAME_Y, CHANNEL_NAME_Z]
|
||||
@@ -20,13 +20,13 @@ class Realtime extends Adapter
|
||||
|
||||
/**
|
||||
* Subscription Tree
|
||||
*
|
||||
* [PROJECT_ID] ->
|
||||
* [ROLE_X] ->
|
||||
*
|
||||
* [PROJECT_ID] ->
|
||||
* [ROLE_X] ->
|
||||
* [CHANNEL_NAME_X] -> [CONNECTION_ID]
|
||||
* [CHANNEL_NAME_Y] -> [CONNECTION_ID]
|
||||
* [CHANNEL_NAME_Z] -> [CONNECTION_ID]
|
||||
* [ROLE_Y] ->
|
||||
* [ROLE_Y] ->
|
||||
* [CHANNEL_NAME_X] -> [CONNECTION_ID]
|
||||
* [CHANNEL_NAME_Y] -> [CONNECTION_ID]
|
||||
* [CHANNEL_NAME_Z] -> [CONNECTION_ID]
|
||||
@@ -35,12 +35,12 @@ class Realtime extends Adapter
|
||||
|
||||
/**
|
||||
* Adds a subscription.
|
||||
*
|
||||
* @param string $projectId
|
||||
* @param mixed $identifier
|
||||
* @param array $roles
|
||||
* @param array $channels
|
||||
* @return void
|
||||
*
|
||||
* @param string $projectId
|
||||
* @param mixed $identifier
|
||||
* @param array $roles
|
||||
* @param array $channels
|
||||
* @return void
|
||||
*/
|
||||
public function subscribe(string $projectId, mixed $identifier, array $roles, array $channels): void
|
||||
{
|
||||
@@ -67,9 +67,9 @@ class Realtime extends Adapter
|
||||
|
||||
/**
|
||||
* Removes Subscription.
|
||||
*
|
||||
*
|
||||
* @param mixed $connection
|
||||
* @return void
|
||||
* @return void
|
||||
*/
|
||||
public function unsubscribe(mixed $connection): void
|
||||
{
|
||||
@@ -99,9 +99,9 @@ class Realtime extends Adapter
|
||||
|
||||
/**
|
||||
* Checks if Channel has a subscriber.
|
||||
* @param string $projectId
|
||||
* @param string $role
|
||||
* @param string $channel
|
||||
* @param string $projectId
|
||||
* @param string $role
|
||||
* @param string $channel
|
||||
* @return bool
|
||||
*/
|
||||
public function hasSubscriber(string $projectId, string $role, string $channel = ''): bool
|
||||
@@ -118,18 +118,20 @@ class Realtime extends Adapter
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends an event to the Realtime Server.
|
||||
* @param string $projectId
|
||||
* @param array $payload
|
||||
* @param string $event
|
||||
* @param array $channels
|
||||
* @param array $roles
|
||||
* @param array $options
|
||||
* @return void
|
||||
* Sends an event to the Realtime Server
|
||||
* @param string $projectId
|
||||
* @param array $payload
|
||||
* @param string $event
|
||||
* @param array $channels
|
||||
* @param array $roles
|
||||
* @param array $options
|
||||
* @return void
|
||||
*/
|
||||
public static function send(string $projectId, array $payload, string $event, array $channels, array $roles, array $options = []): void
|
||||
public static function send(string $projectId, array $payload, array $events, array $channels, array $roles, array $options = []): void
|
||||
{
|
||||
if (empty($channels) || empty($roles) || empty($projectId)) return;
|
||||
if (empty($channels) || empty($roles) || empty($projectId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$permissionsChanged = array_key_exists('permissionsChanged', $options) && $options['permissionsChanged'];
|
||||
$userId = array_key_exists('userId', $options) ? $options['userId'] : null;
|
||||
@@ -142,7 +144,7 @@ class Realtime extends Adapter
|
||||
'permissionsChanged' => $permissionsChanged,
|
||||
'userId' => $userId,
|
||||
'data' => [
|
||||
'event' => $event,
|
||||
'events' => $events,
|
||||
'channels' => $channels,
|
||||
'timestamp' => time(),
|
||||
'payload' => $payload
|
||||
@@ -152,15 +154,15 @@ class Realtime extends Adapter
|
||||
|
||||
/**
|
||||
* Identifies the receivers of all subscriptions, based on the permissions and event.
|
||||
*
|
||||
*
|
||||
* Example of performance with an event with user:XXX permissions and with X users spread across 10 different channels:
|
||||
* - 0.014 ms (±6.88%) | 10 Connections / 100 Subscriptions
|
||||
* - 0.070 ms (±3.71%) | 100 Connections / 1,000 Subscriptions
|
||||
* - 0.014 ms (±6.88%) | 10 Connections / 100 Subscriptions
|
||||
* - 0.070 ms (±3.71%) | 100 Connections / 1,000 Subscriptions
|
||||
* - 0.846 ms (±2.74%) | 1,000 Connections / 10,000 Subscriptions
|
||||
* - 10.866 ms (±1.01%) | 10,000 Connections / 100,000 Subscriptions
|
||||
* - 110.201 ms (±2.32%) | 100,000 Connections / 1,000,000 Subscriptions
|
||||
* - 1,121.328 ms (±0.84%) | 1,000,000 Connections / 10,000,000 Subscriptions
|
||||
*
|
||||
* - 1,121.328 ms (±0.84%) | 1,000,000 Connections / 10,000,000 Subscriptions
|
||||
*
|
||||
* @param array $event
|
||||
*/
|
||||
public function getSubscribers(array $event)
|
||||
@@ -205,11 +207,11 @@ class Realtime extends Adapter
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the channels from the Query Params into an array.
|
||||
* Converts the channels from the Query Params into an array.
|
||||
* Also renames the account channel to account.USER_ID and removes all illegal account channel variations.
|
||||
* @param array $channels
|
||||
* @param string $userId
|
||||
* @return array
|
||||
* @param array $channels
|
||||
* @param string $userId
|
||||
* @return array
|
||||
*/
|
||||
public static function convertChannels(array $channels, string $userId): array
|
||||
{
|
||||
@@ -235,88 +237,87 @@ class Realtime extends Adapter
|
||||
/**
|
||||
* Create channels array based on the event name and payload.
|
||||
*
|
||||
* @param string $event
|
||||
* @param Document $payload
|
||||
* @param Document|null $project
|
||||
* @return array
|
||||
* @param string $event
|
||||
* @param Document $payload
|
||||
* @param Document|null $project
|
||||
* @return array
|
||||
*/
|
||||
public static function fromPayload(string $event, Document $payload, Document $project = null, Document $collection = null, Document $bucket = null): array
|
||||
public static function fromPayload(string $event, Document $payload, Document $project = null, Document $database = null, Document $collection = null, Document $bucket = null): array
|
||||
{
|
||||
$channels = [];
|
||||
$roles = [];
|
||||
$permissionsChanged = false;
|
||||
$projectId = null;
|
||||
// TODO: add method here to remove all the magic index accesses
|
||||
$parts = explode('.', $event);
|
||||
|
||||
switch (true) {
|
||||
case strpos($event, 'account.recovery.') === 0:
|
||||
case strpos($event, 'account.sessions.') === 0:
|
||||
case strpos($event, 'account.verification.') === 0:
|
||||
switch ($parts[0]) {
|
||||
case 'users':
|
||||
$channels[] = 'account';
|
||||
$channels[] = 'account.' . $payload->getAttribute('userId');
|
||||
$roles = ['user:' . $payload->getAttribute('userId')];
|
||||
$channels[] = 'account.' . $parts[1];
|
||||
$roles = ['user:' . $parts[1]];
|
||||
|
||||
break;
|
||||
case strpos($event, 'account.') === 0:
|
||||
$channels[] = 'account';
|
||||
$channels[] = 'account.' . $payload->getId();
|
||||
$roles = ['user:' . $payload->getId()];
|
||||
|
||||
break;
|
||||
case strpos($event, 'teams.memberships') === 0:
|
||||
$permissionsChanged = in_array($event, ['teams.memberships.update', 'teams.memberships.delete', 'teams.memberships.update.status']);
|
||||
$channels[] = 'memberships';
|
||||
$channels[] = 'memberships.' . $payload->getId();
|
||||
$roles = ['team:' . $payload->getAttribute('teamId')];
|
||||
|
||||
break;
|
||||
case strpos($event, 'teams.') === 0:
|
||||
$permissionsChanged = $event === 'teams.create';
|
||||
$channels[] = 'teams';
|
||||
$channels[] = 'teams.' . $payload->getId();
|
||||
$roles = ['team:' . $payload->getId()];
|
||||
|
||||
break;
|
||||
case strpos($event, 'database.attributes.') === 0:
|
||||
case strpos($event, 'database.indexes.') === 0:
|
||||
$channels[] = 'console';
|
||||
$projectId = 'console';
|
||||
$roles = ['team:' . $project->getAttribute('teamId')];
|
||||
|
||||
break;
|
||||
case strpos($event, 'database.documents.') === 0:
|
||||
if ($collection->isEmpty()) {
|
||||
throw new \Exception('Collection needs to be passed to Realtime for Document events in the Database.');
|
||||
case 'teams':
|
||||
if ($parts[2] === 'memberships') {
|
||||
$permissionsChanged = $parts[4] ?? false;
|
||||
$channels[] = 'memberships';
|
||||
$channels[] = 'memberships.' . $parts[3];
|
||||
$roles = ['team:' . $parts[1]];
|
||||
} else {
|
||||
$permissionsChanged = $parts[2] === 'create';
|
||||
$channels[] = 'teams';
|
||||
$channels[] = 'teams.' . $parts[1];
|
||||
$roles = ['team:' . $parts[1]];
|
||||
}
|
||||
|
||||
$channels[] = 'documents';
|
||||
$channels[] = 'collections.' . $payload->getAttribute('$collection') . '.documents';
|
||||
$channels[] = 'collections.' . $payload->getAttribute('$collection') . '.documents.' . $payload->getId();
|
||||
|
||||
$roles = ($collection->getAttribute('permission') === 'collection') ? $collection->getRead() : $payload->getRead();
|
||||
|
||||
break;
|
||||
case strpos($event, 'storage.files') === 0:
|
||||
if($bucket->isEmpty()) {
|
||||
throw new \Exception('Bucket needs to be pased to Realtime for File events in the Storage.');
|
||||
}
|
||||
$channels[] = 'files';
|
||||
$channels[] = 'buckets.' . $payload->getAttribute('bucketId') . '.files';
|
||||
$channels[] = 'buckets.' . $payload->getAttribute('bucketId') . '.files.' . $payload->getId();
|
||||
$roles = $payload->getRead();
|
||||
|
||||
break;
|
||||
case strpos($event, 'functions.executions.') === 0:
|
||||
if (!empty($payload->getRead())) {
|
||||
case 'databases':
|
||||
if (in_array($parts[4] ?? [], ['attributes', 'indexes'])) {
|
||||
$channels[] = 'console';
|
||||
$channels[] = 'executions';
|
||||
$channels[] = 'executions.' . $payload->getId();
|
||||
$channels[] = 'functions.' . $payload->getAttribute('functionId');
|
||||
$roles = $payload->getRead();
|
||||
$projectId = 'console';
|
||||
$roles = ['team:' . $project->getAttribute('teamId')];
|
||||
} elseif (($parts[4] ?? '') === 'documents') {
|
||||
if ($database->isEmpty()) {
|
||||
throw new \Exception('Database needs to be passed to Realtime for Document events in the Database.');
|
||||
}
|
||||
if ($collection->isEmpty()) {
|
||||
throw new \Exception('Collection needs to be passed to Realtime for Document events in the Database.');
|
||||
}
|
||||
|
||||
$channels[] = 'documents';
|
||||
$channels[] = 'databases.' . $database->getId() . '.collections.' . $payload->getCollection() . '.documents';
|
||||
$channels[] = 'databases.' . $database->getId() . '.collections.' . $payload->getCollection() . '.documents.' . $payload->getId();
|
||||
|
||||
$roles = ($collection->getAttribute('permission') === 'collection') ? $collection->getRead() : $payload->getRead();
|
||||
}
|
||||
break;
|
||||
case strpos($event, 'functions.deployments.') === 0:
|
||||
$channels[] = 'console';
|
||||
$roles = ['team:' . $project->getAttribute('teamId')];
|
||||
case 'buckets':
|
||||
if ($parts[2] === 'files') {
|
||||
if ($bucket->isEmpty()) {
|
||||
throw new \Exception('Bucket needs to be pased to Realtime for File events in the Storage.');
|
||||
}
|
||||
$channels[] = 'files';
|
||||
$channels[] = 'buckets.' . $payload->getAttribute('bucketId') . '.files';
|
||||
$channels[] = 'buckets.' . $payload->getAttribute('bucketId') . '.files.' . $payload->getId();
|
||||
$roles = ($bucket->getAttribute('permission') === 'bucket') ? $bucket->getRead() : $payload->getRead();
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'functions':
|
||||
if ($parts[2] === 'executions') {
|
||||
if (!empty($payload->getRead())) {
|
||||
$channels[] = 'console';
|
||||
$channels[] = 'executions';
|
||||
$channels[] = 'executions.' . $payload->getId();
|
||||
$channels[] = 'functions.' . $payload->getAttribute('functionId');
|
||||
$roles = $payload->getRead();
|
||||
}
|
||||
} elseif ($parts[2] === 'deployments') {
|
||||
$channels[] = 'console';
|
||||
$roles = ['team:' . $project->getAttribute('teamId')];
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,8 @@ use Utopia\Database\Database;
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Config\Config;
|
||||
use Exception;
|
||||
use Utopia\App;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
|
||||
abstract class Migration
|
||||
{
|
||||
@@ -38,6 +40,14 @@ abstract class Migration
|
||||
'0.13.0' => 'V12',
|
||||
'0.13.1' => 'V12',
|
||||
'0.13.2' => 'V12',
|
||||
'0.13.3' => 'V12',
|
||||
'0.13.4' => 'V12',
|
||||
'0.14.0' => 'V13',
|
||||
'0.14.1' => 'V13',
|
||||
'0.14.2' => 'V13',
|
||||
'0.15.0' => 'V14',
|
||||
'0.15.1' => 'V14',
|
||||
'0.15.2' => 'V14'
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -47,15 +57,20 @@ abstract class Migration
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
Authorization::disable();
|
||||
Authorization::setDefaultStatus(false);
|
||||
$this->collections = array_merge([
|
||||
'_metadata' => [
|
||||
'$id' => '_metadata'
|
||||
'$id' => '_metadata',
|
||||
'$collection' => Database::METADATA
|
||||
],
|
||||
'audit' => [
|
||||
'$id' => 'audit'
|
||||
'$id' => 'audit',
|
||||
'$collection' => Database::METADATA
|
||||
],
|
||||
'abuse' => [
|
||||
'$id' => 'abuse'
|
||||
'$id' => 'abuse',
|
||||
'$collection' => Database::METADATA
|
||||
]
|
||||
], Config::getParam('collections', []));
|
||||
}
|
||||
@@ -90,6 +105,10 @@ abstract class Migration
|
||||
Runtime::enableCoroutine(SWOOLE_HOOK_ALL);
|
||||
|
||||
foreach ($this->collections as $collection) {
|
||||
if ($collection['$collection'] !== Database::METADATA) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$sum = 0;
|
||||
$nextDocument = null;
|
||||
$collectionCount = $this->projectDB->count($collection['$id']);
|
||||
@@ -112,21 +131,7 @@ abstract class Migration
|
||||
$old = $document->getArrayCopy();
|
||||
$new = call_user_func($callback, $document);
|
||||
|
||||
foreach ($document as &$attr) {
|
||||
if ($attr instanceof Document) {
|
||||
$attr = call_user_func($callback, $attr);
|
||||
}
|
||||
|
||||
if (\is_array($attr)) {
|
||||
foreach ($attr as &$child) {
|
||||
if ($child instanceof Document) {
|
||||
$child = call_user_func($callback, $child);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$this->check_diff_multi($new->getArrayCopy(), $old)) {
|
||||
if (is_null($new) || !self::hasDifference($new->getArrayCopy(), $old)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -155,35 +160,156 @@ abstract class Migration
|
||||
|
||||
/**
|
||||
* Checks 2 arrays for differences.
|
||||
*
|
||||
* @param array $array1
|
||||
* @param array $array2
|
||||
* @return array
|
||||
*
|
||||
* @param array $array1
|
||||
* @param array $array2
|
||||
* @return bool
|
||||
*/
|
||||
public function check_diff_multi(array $array1, array $array2): array
|
||||
public static function hasDifference(array $array1, array $array2): bool
|
||||
{
|
||||
$result = array();
|
||||
|
||||
foreach ($array1 as $key => $val) {
|
||||
if (is_array($val) && isset($array2[$key])) {
|
||||
$tmp = $this->check_diff_multi($val, $array2[$key]);
|
||||
if ($tmp) {
|
||||
$result[$key] = $tmp;
|
||||
foreach ($array1 as $key => $value) {
|
||||
if (is_array($value)) {
|
||||
if (!isset($array2[$key]) || !is_array($array2[$key])) {
|
||||
return true;
|
||||
} else {
|
||||
if (self::hasDifference($value, $array2[$key])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} elseif (!isset($array2[$key])) {
|
||||
$result[$key] = null;
|
||||
} elseif ($val !== $array2[$key]) {
|
||||
$result[$key] = $array2[$key];
|
||||
}
|
||||
|
||||
if (isset($array2[$key])) {
|
||||
unset($array2[$key]);
|
||||
} elseif (!array_key_exists($key, $array2) || $array2[$key] !== $value) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
$result = array_merge($result, $array2);
|
||||
return false;
|
||||
}
|
||||
|
||||
return $result;
|
||||
/**
|
||||
* Creates colletion from the config collection.
|
||||
*
|
||||
* @param string $id
|
||||
* @param string|null $name
|
||||
* @return void
|
||||
* @throws \Throwable
|
||||
*/
|
||||
protected function createCollection(string $id, string $name = null): void
|
||||
{
|
||||
$name ??= $id;
|
||||
|
||||
if (!$this->projectDB->exists(App::getEnv('_APP_DB_SCHEMA', 'appwrite'), $name)) {
|
||||
$attributes = [];
|
||||
$indexes = [];
|
||||
$collection = $this->collections[$id];
|
||||
|
||||
foreach ($collection['attributes'] as $attribute) {
|
||||
$attributes[] = new Document([
|
||||
'$id' => $attribute['$id'],
|
||||
'type' => $attribute['type'],
|
||||
'size' => $attribute['size'],
|
||||
'required' => $attribute['required'],
|
||||
'signed' => $attribute['signed'],
|
||||
'array' => $attribute['array'],
|
||||
'filters' => $attribute['filters'],
|
||||
]);
|
||||
}
|
||||
|
||||
foreach ($collection['indexes'] as $index) {
|
||||
$indexes[] = new Document([
|
||||
'$id' => $index['$id'],
|
||||
'type' => $index['type'],
|
||||
'attributes' => $index['attributes'],
|
||||
'lengths' => $index['lengths'],
|
||||
'orders' => $index['orders'],
|
||||
]);
|
||||
}
|
||||
|
||||
try {
|
||||
$this->projectDB->createCollection($name, $attributes, $indexes);
|
||||
} catch (\Throwable $th) {
|
||||
throw $th;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates attribute from collections.php
|
||||
*
|
||||
* @param \Utopia\Database\Database $database
|
||||
* @param string $collectionId
|
||||
* @param string $attributeId
|
||||
* @return void
|
||||
* @throws \Exception
|
||||
* @throws \Utopia\Database\Exception\Duplicate
|
||||
* @throws \Utopia\Database\Exception\Limit
|
||||
*/
|
||||
public function createAttributeFromCollection(Database $database, string $collectionId, string $attributeId, string $from = null): void
|
||||
{
|
||||
$from ??= $collectionId;
|
||||
$collection = Config::getParam('collections', [])[$from] ?? null;
|
||||
if (is_null($collection)) {
|
||||
throw new Exception("Collection {$collectionId} not found");
|
||||
}
|
||||
$attributes = $collection['attributes'];
|
||||
|
||||
$attributeKey = array_search($attributeId, array_column($attributes, '$id'));
|
||||
|
||||
if ($attributeKey === false) {
|
||||
throw new Exception("Attribute {$attributeId} not found");
|
||||
}
|
||||
|
||||
$attribute = $attributes[$attributeKey];
|
||||
|
||||
$database->createAttribute(
|
||||
collection: $collectionId,
|
||||
id: $attributeId,
|
||||
type: $attribute['type'],
|
||||
size: $attribute['size'],
|
||||
required: $attribute['required'] ?? false,
|
||||
default: $attribute['default'] ?? null,
|
||||
signed: $attribute['signed'] ?? false,
|
||||
array: $attribute['array'] ?? false,
|
||||
format: $attribute['format'] ?? '',
|
||||
formatOptions: $attribute['formatOptions'] ?? [],
|
||||
filters: $attribute['filters'] ?? [],
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates index from collections.php
|
||||
*
|
||||
* @param \Utopia\Database\Database $database
|
||||
* @param string $collectionId
|
||||
* @param string $indexId
|
||||
* @return void
|
||||
* @throws \Exception
|
||||
* @throws \Utopia\Database\Exception\Duplicate
|
||||
* @throws \Utopia\Database\Exception\Limit
|
||||
*/
|
||||
public function createIndexFromCollection(Database $database, string $collectionId, string $indexId): void
|
||||
{
|
||||
$collection = Config::getParam('collections', [])[$collectionId] ?? null;
|
||||
|
||||
if (is_null($collection)) {
|
||||
throw new Exception("Collection {$collectionId} not found");
|
||||
}
|
||||
$indexes = $collection['indexes'];
|
||||
|
||||
$indexKey = array_search($indexId, array_column($indexes, '$id'));
|
||||
|
||||
if ($indexKey === false) {
|
||||
throw new Exception("Attribute {$indexId} not found");
|
||||
}
|
||||
|
||||
$index = $indexes[$indexKey];
|
||||
|
||||
$database->createIndex(
|
||||
collection: $collectionId,
|
||||
id: $indexId,
|
||||
type: $index['type'],
|
||||
attributes: $index['attributes'],
|
||||
lengths: $index['lengths'] ?? [],
|
||||
orders: $index['orders'] ?? []
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -297,61 +297,14 @@ class V12 extends Migration
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates colletion from the config collection.
|
||||
*
|
||||
* @param string $id
|
||||
* @param string|null $name
|
||||
* @return void
|
||||
* @throws \Throwable
|
||||
*/
|
||||
protected function createCollection(string $id, string $name = null): void
|
||||
{
|
||||
$name ??= $id;
|
||||
|
||||
if (!$this->projectDB->exists(App::getEnv('_APP_DB_SCHEMA', 'appwrite'), $name)) {
|
||||
$attributes = [];
|
||||
$indexes = [];
|
||||
$collection = $this->collections[$id];
|
||||
|
||||
foreach ($collection['attributes'] as $attribute) {
|
||||
$attributes[] = new Document([
|
||||
'$id' => $attribute['$id'],
|
||||
'type' => $attribute['type'],
|
||||
'size' => $attribute['size'],
|
||||
'required' => $attribute['required'],
|
||||
'signed' => $attribute['signed'],
|
||||
'array' => $attribute['array'],
|
||||
'filters' => $attribute['filters'],
|
||||
]);
|
||||
}
|
||||
|
||||
foreach ($collection['indexes'] as $index) {
|
||||
$indexes[] = new Document([
|
||||
'$id' => $index['$id'],
|
||||
'type' => $index['type'],
|
||||
'attributes' => $index['attributes'],
|
||||
'lengths' => $index['lengths'],
|
||||
'orders' => $index['orders'],
|
||||
]);
|
||||
}
|
||||
|
||||
try {
|
||||
$this->projectDB->createCollection($name, $attributes, $indexes);
|
||||
} catch (\Throwable $th) {
|
||||
throw $th;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates permissions to dedicated table.
|
||||
*
|
||||
* @param \Utopia\Database\Document $document
|
||||
* @param string $internalId
|
||||
* @return void
|
||||
* @throws \Exception
|
||||
* @throws \PDOException
|
||||
* @param \Utopia\Database\Document $document
|
||||
* @param string $internalId
|
||||
* @return void
|
||||
* @throws \Exception
|
||||
* @throws \PDOException
|
||||
*/
|
||||
protected function migratePermissionsToDedicatedTable(string $collection, Document $document): void
|
||||
{
|
||||
@@ -546,8 +499,8 @@ class V12 extends Migration
|
||||
/**
|
||||
* Fix run on each document
|
||||
*
|
||||
* @param \Utopia\Database\Document $document
|
||||
* @return \Utopia\Database\Document
|
||||
* @param \Utopia\Database\Document $document
|
||||
* @return \Utopia\Database\Document
|
||||
*/
|
||||
protected function fixDocument(Document $document)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,337 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Migration\Version;
|
||||
|
||||
use Appwrite\Migration\Migration;
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
|
||||
class V13 extends Migration
|
||||
{
|
||||
public array $events = [
|
||||
'account.create',
|
||||
'account.update.email',
|
||||
'account.update.name',
|
||||
'account.update.password',
|
||||
'account.update.prefs',
|
||||
'account.recovery.create',
|
||||
'account.recovery.update',
|
||||
'account.verification.create',
|
||||
'account.verification.update',
|
||||
'account.delete',
|
||||
'account.sessions.create',
|
||||
'account.sessions.delete',
|
||||
'database.collections.create',
|
||||
'database.collections.update',
|
||||
'database.collections.delete',
|
||||
'database.attributes.create',
|
||||
'database.attributes.delete',
|
||||
'database.indexes.create',
|
||||
'database.indexes.delete',
|
||||
'database.documents.create',
|
||||
'database.documents.update',
|
||||
'database.documents.delete',
|
||||
'functions.create',
|
||||
'functions.update',
|
||||
'functions.delete',
|
||||
'functions.deployments.create',
|
||||
'functions.deployments.update',
|
||||
'functions.deployments.delete',
|
||||
'functions.executions.create',
|
||||
'functions.executions.update',
|
||||
'storage.files.create',
|
||||
'storage.files.update',
|
||||
'storage.files.delete',
|
||||
'storage.buckets.create',
|
||||
'storage.buckets.update',
|
||||
'storage.buckets.delete',
|
||||
'users.create',
|
||||
'users.update.prefs',
|
||||
'users.update.email',
|
||||
'users.update.name',
|
||||
'users.update.password',
|
||||
'users.update.status',
|
||||
'users.sessions.delete',
|
||||
'users.delete',
|
||||
'teams.create',
|
||||
'teams.update',
|
||||
'teams.delete',
|
||||
'teams.memberships.create',
|
||||
'teams.memberships.update',
|
||||
'teams.memberships.update.status',
|
||||
'teams.memberships.delete'
|
||||
];
|
||||
|
||||
public function execute(): void
|
||||
{
|
||||
Console::log('Migrating project: ' . $this->project->getAttribute('name') . ' (' . $this->project->getId() . ')');
|
||||
Console::info('Migrating Collections');
|
||||
$this->migrateCollections();
|
||||
Console::info('Migrating Documents');
|
||||
$this->forEachDocument([$this, 'fixDocument']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate all Collections.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function migrateCollections(): void
|
||||
{
|
||||
foreach ($this->collections as $collection) {
|
||||
$id = $collection['$id'];
|
||||
|
||||
Console::log("- {$id}");
|
||||
switch ($id) {
|
||||
case 'projects':
|
||||
try {
|
||||
/**
|
||||
* Rename providers to authProviders.
|
||||
*/
|
||||
$this->projectDB->renameAttribute($id, 'providers', 'authProviders');
|
||||
} catch (\Throwable $th) {
|
||||
Console::warning("'providers' from {$id}: {$th->getMessage()}");
|
||||
}
|
||||
break;
|
||||
case 'users':
|
||||
try {
|
||||
/**
|
||||
* Recreate sessions for new subquery.
|
||||
*/
|
||||
$this->projectDB->deleteAttribute($id, 'sessions');
|
||||
$this->projectDB->createAttribute(
|
||||
collection: $id,
|
||||
id: 'sessions',
|
||||
required: false,
|
||||
type: Database::VAR_STRING,
|
||||
format: '',
|
||||
size: 16384,
|
||||
filters: ['subQuerySessions']
|
||||
);
|
||||
} catch (\Throwable $th) {
|
||||
Console::warning("'sessions' from {$id}: {$th->getMessage()}");
|
||||
}
|
||||
try {
|
||||
/**
|
||||
* Recreate tokens for new subquery.
|
||||
*/
|
||||
$this->projectDB->deleteAttribute($id, 'tokens');
|
||||
$this->projectDB->createAttribute(
|
||||
collection: $id,
|
||||
id: 'tokens',
|
||||
required: false,
|
||||
type: Database::VAR_STRING,
|
||||
format: '',
|
||||
size: 16384,
|
||||
filters: ['subQueryTokens']
|
||||
);
|
||||
} catch (\Throwable $th) {
|
||||
Console::warning("'tokens' from {$id}: {$th->getMessage()}");
|
||||
}
|
||||
try {
|
||||
/**
|
||||
* Recreate memberships for new subquery.
|
||||
*/
|
||||
$this->projectDB->deleteAttribute($id, 'memberships');
|
||||
$this->projectDB->createAttribute(
|
||||
collection: $id,
|
||||
id: 'memberships',
|
||||
required: false,
|
||||
type: Database::VAR_STRING,
|
||||
format: '',
|
||||
size: 16384,
|
||||
filters: ['subQueryMemberships']
|
||||
);
|
||||
} catch (\Throwable $th) {
|
||||
Console::warning("'memberships' from {$id}: {$th->getMessage()}");
|
||||
}
|
||||
break;
|
||||
case 'sessions':
|
||||
try {
|
||||
/**
|
||||
* Add new index for users.
|
||||
*/
|
||||
$this->projectDB->createIndex(collection: $id, id: '_key_user', type: Database::INDEX_KEY, attributes: ['userId'], orders: [Database::ORDER_ASC]);
|
||||
} catch (\Throwable $th) {
|
||||
Console::warning("'_key_user' from {$id}: {$th->getMessage()}");
|
||||
}
|
||||
break;
|
||||
case 'builds':
|
||||
try {
|
||||
/**
|
||||
* Increase stdout size.
|
||||
*/
|
||||
$this->projectDB->updateAttribute($id, 'stdout', size: 1_000_000);
|
||||
} catch (\Throwable $th) {
|
||||
Console::warning("'stdout' from {$id}: {$th->getMessage()}");
|
||||
}
|
||||
try {
|
||||
/**
|
||||
* Increase stderr size.
|
||||
*/
|
||||
$this->projectDB->updateAttribute($id, 'stderr', size: 1_000_000);
|
||||
} catch (\Throwable $th) {
|
||||
Console::warning("'stderr' from {$id}: {$th->getMessage()}");
|
||||
}
|
||||
break;
|
||||
case 'executions':
|
||||
try {
|
||||
/**
|
||||
* Rename stdout to response.
|
||||
* Increase response size.
|
||||
*/
|
||||
$this->projectDB->renameAttribute($id, 'stdout', 'response');
|
||||
$this->projectDB->updateAttribute($id, 'response', size: 1_000_000);
|
||||
} catch (\Throwable $th) {
|
||||
Console::warning("'stdout' from {$id}: {$th->getMessage()}");
|
||||
}
|
||||
try {
|
||||
/**
|
||||
* Increase stderr size.
|
||||
*/
|
||||
$this->projectDB->updateAttribute($id, 'stderr', size: 1_000_000);
|
||||
} catch (\Throwable $th) {
|
||||
Console::warning("'stderr' from {$id}: {$th->getMessage()}");
|
||||
}
|
||||
break;
|
||||
case 'stats':
|
||||
try {
|
||||
/**
|
||||
* Increase value size ot BIGINT.
|
||||
*/
|
||||
$this->projectDB->updateAttribute($id, 'value', size: 8);
|
||||
} catch (\Throwable $th) {
|
||||
Console::warning("'size' from {$id}: {$th->getMessage()}");
|
||||
}
|
||||
break;
|
||||
case 'tokens':
|
||||
try {
|
||||
/**
|
||||
* Create new Tokens collection.
|
||||
*/
|
||||
$this->createCollection('tokens');
|
||||
} catch (\Throwable $th) {
|
||||
Console::warning("'tokens': {$th->getMessage()}");
|
||||
}
|
||||
break;
|
||||
}
|
||||
usleep(100000);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fix run on each document
|
||||
*
|
||||
* @param \Utopia\Database\Document $document
|
||||
* @return \Utopia\Database\Document
|
||||
*/
|
||||
protected function fixDocument(Document $document)
|
||||
{
|
||||
switch ($document->getCollection()) {
|
||||
case 'projects':
|
||||
/**
|
||||
* Bump Project version number.
|
||||
*/
|
||||
$document->setAttribute('version', '0.14.0');
|
||||
|
||||
break;
|
||||
|
||||
case 'functions':
|
||||
/**
|
||||
* Migrate events.
|
||||
*/
|
||||
if (!empty($document->getAttribute('events'))) {
|
||||
$document->setAttribute('events', $this->migrateEvents($document->getAttribute('events')));
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'webhooks':
|
||||
/**
|
||||
* Migrate events.
|
||||
*/
|
||||
if (!empty($document->getAttribute('events'))) {
|
||||
$document->setAttribute('events', $this->migrateEvents($document->getAttribute('events')));
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'users':
|
||||
/**
|
||||
* Remove deleted users.
|
||||
*/
|
||||
if ($document->getAttribute('deleted', false) === true) {
|
||||
$this->projectDB->deleteDocument('users', $document->getId());
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return $document;
|
||||
}
|
||||
|
||||
public function migrateEvents(array $events): array
|
||||
{
|
||||
return array_filter(array_unique(array_map(function ($event) {
|
||||
if (!in_array($event, $this->events)) {
|
||||
return $event;
|
||||
}
|
||||
$parts = \explode('.', $event);
|
||||
$first = array_shift($parts);
|
||||
switch ($first) {
|
||||
case 'account':
|
||||
case 'users':
|
||||
$first = 'users';
|
||||
|
||||
switch ($parts[0]) {
|
||||
case 'recovery':
|
||||
case 'sessions':
|
||||
case 'verification':
|
||||
$second = array_shift($parts);
|
||||
return 'users.*.' . $second . '.*.' . implode('.', $parts);
|
||||
|
||||
default:
|
||||
return 'users.*.' . implode('.', $parts);
|
||||
}
|
||||
case 'functions':
|
||||
switch ($parts[0]) {
|
||||
case 'deployments':
|
||||
case 'executions':
|
||||
$second = array_shift($parts);
|
||||
return 'functions.*.' . $second . '.*.' . implode('.', $parts);
|
||||
|
||||
default:
|
||||
return 'functions.*.' . implode('.', $parts);
|
||||
}
|
||||
case 'teams':
|
||||
switch ($parts[0]) {
|
||||
case 'memberships':
|
||||
$second = array_shift($parts);
|
||||
return 'teams.*.' . $second . '.*.' . implode('.', $parts);
|
||||
|
||||
default:
|
||||
return 'teams.*.' . implode('.', $parts);
|
||||
}
|
||||
case 'storage':
|
||||
$second = array_shift($parts);
|
||||
switch ($second) {
|
||||
case 'buckets':
|
||||
return 'buckets.*.' . implode('.', $parts);
|
||||
case 'files':
|
||||
return 'buckets.*.' . $second . '.*.' . implode('.', $parts);
|
||||
} // intentional fallthrough
|
||||
case 'database':
|
||||
$second = array_shift($parts);
|
||||
switch ($second) {
|
||||
case 'collections':
|
||||
return 'collections.*.' . implode('.', $parts);
|
||||
case 'documents':
|
||||
case 'indexes':
|
||||
case 'attributes':
|
||||
return 'collections.*.' . $second . '.*.' . implode('.', $parts);
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}, $events)));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,771 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Migration\Version;
|
||||
|
||||
use Appwrite\Migration\Migration;
|
||||
use Utopia\App;
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
|
||||
class V14 extends Migration
|
||||
{
|
||||
/**
|
||||
* @var \PDO $pdo
|
||||
*/
|
||||
private $pdo;
|
||||
|
||||
public function execute(): void
|
||||
{
|
||||
global $register;
|
||||
$this->pdo = $register->get('db');
|
||||
|
||||
if ($this->project->getId() === 'console' && $this->project->getInternalId() !== 'console') {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable SubQueries for Speed.
|
||||
*/
|
||||
foreach (['subQueryAttributes', 'subQueryIndexes', 'subQueryPlatforms', 'subQueryDomains', 'subQueryKeys', 'subQueryWebhooks', 'subQuerySessions', 'subQueryTokens', 'subQueryMemberships'] as $name) {
|
||||
Database::addFilter($name, fn () => null, fn () => []);
|
||||
}
|
||||
|
||||
Console::log('Migrating project: ' . $this->project->getAttribute('name') . ' (' . $this->project->getId() . ')');
|
||||
Console::info('Migrating Collections');
|
||||
$this->migrateCollections();
|
||||
Console::info('Create Default Database Layer');
|
||||
$this->createDatabaseLayer();
|
||||
if ($this->project->getId() !== 'console') {
|
||||
Console::info('Migrating Database Collections');
|
||||
$this->migrateCustomCollections();
|
||||
}
|
||||
Console::info('Migrating Documents');
|
||||
$this->forEachDocument([$this, 'fixDocument']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the default Database for existing Projects.
|
||||
*
|
||||
* @return void
|
||||
* @throws \Throwable
|
||||
*/
|
||||
public function createDatabaseLayer(): void
|
||||
{
|
||||
try {
|
||||
if (!$this->projectDB->exists('databases')) {
|
||||
$this->createCollection('databases');
|
||||
}
|
||||
} catch (\Throwable $th) {
|
||||
Console::warning($th->getMessage());
|
||||
}
|
||||
|
||||
if ($this->project->getInternalId() === 'console') {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$this->projectDB->createDocument('databases', new Document([
|
||||
'$id' => 'default',
|
||||
'name' => 'Default',
|
||||
'search' => 'default Default'
|
||||
]));
|
||||
} catch (\Throwable $th) {
|
||||
Console::warning($th->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates all Files.
|
||||
*
|
||||
* @param \Utopia\Database\Document $bucket
|
||||
* @return void
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function migrateBucketFiles(Document $bucket): void
|
||||
{
|
||||
$nextFile = null;
|
||||
do {
|
||||
$documents = $this->projectDB->find("bucket_{$bucket->getInternalId()}", limit: $this->limit, cursor: $nextFile);
|
||||
$count = count($documents);
|
||||
|
||||
foreach ($documents as $document) {
|
||||
go(function (Document $bucket, Document $document) {
|
||||
Console::log("Migrating File {$document->getId()}");
|
||||
try {
|
||||
/**
|
||||
* Migrate $createdAt.
|
||||
*/
|
||||
if (empty($document->getCreatedAt())) {
|
||||
$document->setAttribute('$createdAt', $document->getAttribute('dateCreated'));
|
||||
$this->projectDB->updateDocument("bucket_{$bucket->getInternalId()}", $document->getId(), $document);
|
||||
}
|
||||
} catch (\Throwable $th) {
|
||||
Console::warning($th->getMessage());
|
||||
}
|
||||
}, $bucket, $document);
|
||||
}
|
||||
|
||||
if ($count !== $this->limit) {
|
||||
$nextFile = null;
|
||||
} else {
|
||||
$nextFile = end($documents);
|
||||
}
|
||||
} while (!is_null($nextFile));
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates all Database Collections.
|
||||
* @return void
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function migrateCustomCollections(): void
|
||||
{
|
||||
try {
|
||||
$this->pdo->prepare("ALTER TABLE IF EXISTS `{$this->projectDB->getDefaultDatabase()}`.`_{$this->project->getInternalId()}_collections` RENAME TO `_{$this->project->getInternalId()}_database_1`")->execute();
|
||||
} catch (\Throwable $th) {
|
||||
Console::warning($th->getMessage());
|
||||
}
|
||||
try {
|
||||
$this->pdo->prepare("ALTER TABLE IF EXISTS `{$this->projectDB->getDefaultDatabase()}`.`_{$this->project->getInternalId()}_collections_perms` RENAME TO `_{$this->project->getInternalId()}_database_1_perms`")->execute();
|
||||
} catch (\Throwable $th) {
|
||||
Console::warning($th->getMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* Update metadata table.
|
||||
*/
|
||||
try {
|
||||
$this->pdo->prepare("UPDATE `{$this->projectDB->getDefaultDatabase()}`.`_{$this->project->getInternalId()}__metadata`
|
||||
SET
|
||||
_uid = 'database_1',
|
||||
name = 'database_1'
|
||||
WHERE _uid = 'collections';
|
||||
")->execute();
|
||||
} catch (\Throwable $th) {
|
||||
Console::warning($th->getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
/**
|
||||
* Add Database ID for Collections.
|
||||
*/
|
||||
$this->createAttributeFromCollection($this->projectDB, 'database_1', 'databaseId', 'collections');
|
||||
|
||||
/**
|
||||
* Add Database Internal ID for Collections.
|
||||
*/
|
||||
$this->createAttributeFromCollection($this->projectDB, 'database_1', 'databaseInternalId', 'collections');
|
||||
} catch (\Throwable $th) {
|
||||
Console::warning($th->getMessage());
|
||||
}
|
||||
|
||||
$nextCollection = null;
|
||||
|
||||
do {
|
||||
$documents = $this->projectDB->find('database_1', limit: $this->limit, cursor: $nextCollection);
|
||||
$count = count($documents);
|
||||
|
||||
\Co\run(function (array $documents) {
|
||||
foreach ($documents as $document) {
|
||||
go(function (Document $collection) {
|
||||
$id = $collection->getId();
|
||||
$internalId = $collection->getInternalId();
|
||||
|
||||
Console::log("- {$id} ({$collection->getAttribute('name')})");
|
||||
|
||||
try {
|
||||
/**
|
||||
* Rename user's colletion table schema
|
||||
*/
|
||||
$this->createNewMetaData("collection_{$internalId}", "database_1_collection_{$internalId}");
|
||||
} catch (\Throwable $th) {
|
||||
Console::warning($th->getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
/**
|
||||
* Update metadata table.
|
||||
*/
|
||||
$this->pdo->prepare("UPDATE `{$this->projectDB->getDefaultDatabase()}`.`_{$this->project->getInternalId()}__metadata`
|
||||
SET
|
||||
_uid = 'database_1_collection_{$internalId}',
|
||||
name = 'database_1_collection_{$internalId}'
|
||||
WHERE _uid = 'collection_{$internalId}';
|
||||
")->execute();
|
||||
} catch (\Throwable $th) {
|
||||
Console::warning($th->getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
/**
|
||||
* Update internal ID's.
|
||||
*/
|
||||
$collection
|
||||
->setAttribute('databaseId', 'default')
|
||||
->setAttribute('databaseInternalId', '1');
|
||||
$this->projectDB->updateDocument('database_1', $collection->getId(), $collection);
|
||||
} catch (\Throwable $th) {
|
||||
Console::warning($th->getMessage());
|
||||
}
|
||||
}, $document);
|
||||
}
|
||||
}, $documents);
|
||||
|
||||
if ($count !== $this->limit) {
|
||||
$nextCollection = null;
|
||||
} else {
|
||||
$nextCollection = end($documents);
|
||||
}
|
||||
} while (!is_null($nextCollection));
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate all Collections.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function migrateCollections(): void
|
||||
{
|
||||
foreach ($this->collections as $collection) {
|
||||
$id = $collection['$id'];
|
||||
|
||||
Console::log("- {$id}");
|
||||
|
||||
$this->createNewMetaData($id);
|
||||
|
||||
$this->projectDB->setNamespace("_{$this->project->getInternalId()}");
|
||||
|
||||
switch ($id) {
|
||||
case 'attributes':
|
||||
case 'indexes':
|
||||
try {
|
||||
/**
|
||||
* Create 'databaseInternalId' attribute
|
||||
*/
|
||||
$this->createAttributeFromCollection($this->projectDB, $id, 'databaseId');
|
||||
} catch (\Throwable $th) {
|
||||
Console::warning("'databaseInternalId' from {$id}: {$th->getMessage()}");
|
||||
}
|
||||
try {
|
||||
/**
|
||||
* Create 'databaseInternalId' attribute
|
||||
*/
|
||||
$this->createAttributeFromCollection($this->projectDB, $id, 'databaseInternalId');
|
||||
} catch (\Throwable $th) {
|
||||
Console::warning("'databaseInternalId' from {$id}: {$th->getMessage()}");
|
||||
}
|
||||
|
||||
try {
|
||||
/**
|
||||
* Create 'collectionInternalId' attribute
|
||||
*/
|
||||
$this->createAttributeFromCollection($this->projectDB, $id, 'collectionInternalId');
|
||||
} catch (\Throwable $th) {
|
||||
Console::warning("'collectionInternalId' from {$id}: {$th->getMessage()}");
|
||||
}
|
||||
|
||||
try {
|
||||
/**
|
||||
* Re-Create '_key_collection' index
|
||||
*/
|
||||
@$this->projectDB->deleteIndex($id, '_key_collection');
|
||||
$this->createIndexFromCollection($this->projectDB, $id, '_key_db_collection');
|
||||
} catch (\Throwable $th) {
|
||||
Console::warning("'_key_collection' from {$id}: {$th->getMessage()}");
|
||||
}
|
||||
|
||||
break;
|
||||
case 'projects':
|
||||
try {
|
||||
/**
|
||||
* Create 'teamInternalId' attribute
|
||||
*/
|
||||
$this->createAttributeFromCollection($this->projectDB, $id, 'teamInternalId');
|
||||
} catch (\Throwable $th) {
|
||||
Console::warning("'teamInternalId' from {$id}: {$th->getMessage()}");
|
||||
}
|
||||
|
||||
break;
|
||||
case 'platforms':
|
||||
case 'domains':
|
||||
try {
|
||||
/**
|
||||
* Create 'projectInternalId' attribute
|
||||
*/
|
||||
$this->createAttributeFromCollection($this->projectDB, $id, 'projectInternalId');
|
||||
} catch (\Throwable $th) {
|
||||
Console::warning("'projectInternalId' from {$id}: {$th->getMessage()}");
|
||||
}
|
||||
try {
|
||||
/**
|
||||
* Re-Create '_key_project' index
|
||||
*/
|
||||
@$this->projectDB->deleteIndex($id, '_key_project');
|
||||
$this->createIndexFromCollection($this->projectDB, $id, '_key_project');
|
||||
} catch (\Throwable $th) {
|
||||
Console::warning("'_key_project' from {$id}: {$th->getMessage()}");
|
||||
}
|
||||
|
||||
break;
|
||||
case 'keys':
|
||||
try {
|
||||
/**
|
||||
* Create 'projectInternalId' attribute
|
||||
*/
|
||||
$this->createAttributeFromCollection($this->projectDB, $id, 'projectInternalId');
|
||||
} catch (\Throwable $th) {
|
||||
Console::warning("'projectInternalId' from {$id}: {$th->getMessage()}");
|
||||
}
|
||||
try {
|
||||
/**
|
||||
* Create 'expire' attribute
|
||||
*/
|
||||
$this->createAttributeFromCollection($this->projectDB, $id, 'expire');
|
||||
} catch (\Throwable $th) {
|
||||
Console::warning("'expire' from {$id}: {$th->getMessage()}");
|
||||
}
|
||||
try {
|
||||
/**
|
||||
* Re-Create '_key_project' index
|
||||
*/
|
||||
@$this->projectDB->deleteIndex($id, '_key_project');
|
||||
$this->createIndexFromCollection($this->projectDB, $id, '_key_project');
|
||||
} catch (\Throwable $th) {
|
||||
Console::warning("'_key_project' from {$id}: {$th->getMessage()}");
|
||||
}
|
||||
|
||||
break;
|
||||
case 'webhooks':
|
||||
try {
|
||||
/**
|
||||
* Create 'signatureKey' attribute
|
||||
*/
|
||||
$this->createAttributeFromCollection($this->projectDB, $id, 'signatureKey');
|
||||
} catch (\Throwable $th) {
|
||||
Console::warning("'signatureKey' from {$id}: {$th->getMessage()}");
|
||||
}
|
||||
try {
|
||||
/**
|
||||
* Create 'projectInternalId' attribute
|
||||
*/
|
||||
$this->createAttributeFromCollection($this->projectDB, $id, 'projectInternalId');
|
||||
} catch (\Throwable $th) {
|
||||
Console::warning("'projectInternalId' from {$id}: {$th->getMessage()}");
|
||||
}
|
||||
try {
|
||||
/**
|
||||
* Re-Create '_key_project' index
|
||||
*/
|
||||
@$this->projectDB->deleteIndex($id, '_key_project');
|
||||
$this->createIndexFromCollection($this->projectDB, $id, '_key_project');
|
||||
} catch (\Throwable $th) {
|
||||
Console::warning("'_key_project' from {$id}: {$th->getMessage()}");
|
||||
}
|
||||
|
||||
break;
|
||||
case 'users':
|
||||
try {
|
||||
/**
|
||||
* Create 'phone' attribute
|
||||
*/
|
||||
$this->createAttributeFromCollection($this->projectDB, $id, 'phone');
|
||||
} catch (\Throwable $th) {
|
||||
Console::warning("'phone' from {$id}: {$th->getMessage()}");
|
||||
}
|
||||
try {
|
||||
/**
|
||||
* Create 'phoneVerification' attribute
|
||||
*/
|
||||
$this->createAttributeFromCollection($this->projectDB, $id, 'phoneVerification');
|
||||
} catch (\Throwable $th) {
|
||||
Console::warning("'phoneVerification' from {$id}: {$th->getMessage()}");
|
||||
}
|
||||
try {
|
||||
/**
|
||||
* Create '_key_phone' index
|
||||
*/
|
||||
$this->createIndexFromCollection($this->projectDB, $id, '_key_phone');
|
||||
} catch (\Throwable $th) {
|
||||
Console::warning("'_key_phone' from {$id}: {$th->getMessage()}");
|
||||
}
|
||||
|
||||
break;
|
||||
case 'tokens':
|
||||
case 'sessions':
|
||||
try {
|
||||
/**
|
||||
* Create 'userInternalId' attribute
|
||||
*/
|
||||
$this->createAttributeFromCollection($this->projectDB, $id, 'userInternalId');
|
||||
} catch (\Throwable $th) {
|
||||
Console::warning("'userInternalId' from {$id}: {$th->getMessage()}");
|
||||
}
|
||||
try {
|
||||
/**
|
||||
* Re-Create '_key_user' index
|
||||
*/
|
||||
@$this->projectDB->deleteIndex($id, '_key_user');
|
||||
$this->createIndexFromCollection($this->projectDB, $id, '_key_user');
|
||||
} catch (\Throwable $th) {
|
||||
Console::warning("'_key_user' from {$id}: {$th->getMessage()}");
|
||||
}
|
||||
|
||||
break;
|
||||
case 'memberships':
|
||||
try {
|
||||
/**
|
||||
* Create 'teamInternalId' attribute
|
||||
*/
|
||||
$this->createAttributeFromCollection($this->projectDB, $id, 'teamInternalId');
|
||||
} catch (\Throwable $th) {
|
||||
Console::warning("'teamInternalId' from {$id}: {$th->getMessage()}");
|
||||
}
|
||||
try {
|
||||
/**
|
||||
* Create 'userInternalId' attribute
|
||||
*/
|
||||
$this->createAttributeFromCollection($this->projectDB, $id, 'userInternalId');
|
||||
} catch (\Throwable $th) {
|
||||
Console::warning("'userInternalId' from {$id}: {$th->getMessage()}");
|
||||
}
|
||||
try {
|
||||
/**
|
||||
* Re-Create '_key_unique' index
|
||||
*/
|
||||
@$this->projectDB->deleteIndex($id, '_key_unique');
|
||||
$this->createIndexFromCollection($this->projectDB, $id, '_key_unique');
|
||||
} catch (\Throwable $th) {
|
||||
Console::warning("'_key_unique' from {$id}: {$th->getMessage()}");
|
||||
}
|
||||
try {
|
||||
/**
|
||||
* Re-Create '_key_team' index
|
||||
*/
|
||||
@$this->projectDB->deleteIndex($id, '_key_team');
|
||||
$this->createIndexFromCollection($this->projectDB, $id, '_key_team');
|
||||
} catch (\Throwable $th) {
|
||||
Console::warning("'_key_team' from {$id}: {$th->getMessage()}");
|
||||
}
|
||||
try {
|
||||
/**
|
||||
* Re-Create '_key_user' index
|
||||
*/
|
||||
@$this->projectDB->deleteIndex($id, '_key_user');
|
||||
$this->createIndexFromCollection($this->projectDB, $id, '_key_user');
|
||||
} catch (\Throwable $th) {
|
||||
Console::warning("'_key_user' from {$id}: {$th->getMessage()}");
|
||||
}
|
||||
break;
|
||||
}
|
||||
usleep(50000);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fix run on each document
|
||||
*
|
||||
* @param \Utopia\Database\Document $document
|
||||
* @return \Utopia\Database\Document
|
||||
*/
|
||||
protected function fixDocument(Document $document)
|
||||
{
|
||||
switch ($document->getCollection()) {
|
||||
case 'projects':
|
||||
/**
|
||||
* Bump Project version number.
|
||||
*/
|
||||
$document->setAttribute('version', '0.15.0');
|
||||
|
||||
if (!empty($document->getAttribute('teamId')) && is_null($document->getAttribute('teamInternalId'))) {
|
||||
$internalId = $this->projectDB->getDocument('teams', $document->getAttribute('teamId'))->getInternalId();
|
||||
$document->setAttribute('teamInternalId', $internalId);
|
||||
}
|
||||
|
||||
break;
|
||||
case 'keys':
|
||||
/**
|
||||
* Add new 'expire' attribute and default to never (0).
|
||||
*/
|
||||
if (is_null($document->getAttribute('expire'))) {
|
||||
$document->setAttribute('expire', 0);
|
||||
}
|
||||
/**
|
||||
* Add Internal ID 'projectId' for Subqueries.
|
||||
*/
|
||||
if (!empty($document->getAttribute('projectId')) && is_null($document->getAttribute('projectInternalId'))) {
|
||||
$internalId = $this->projectDB->getDocument('projects', $document->getAttribute('projectId'))->getInternalId();
|
||||
$document->setAttribute('projectInternalId', $internalId);
|
||||
}
|
||||
|
||||
break;
|
||||
case 'audit':
|
||||
/**
|
||||
* Add Database Layer to collection resource.
|
||||
*/
|
||||
if (str_starts_with($document->getAttribute('resource'), 'collection/')) {
|
||||
$document
|
||||
->setAttribute('resource', "database/default/{$document->getAttribute('resource')}")
|
||||
->setAttribute('event', "databases.default.{$document->getAttribute('event')}");
|
||||
}
|
||||
|
||||
if (str_starts_with($document->getAttribute('resource'), 'document/')) {
|
||||
$collectionId = explode('.', $document->getAttribute('event'))[1];
|
||||
$document
|
||||
->setAttribute('resource', "database/default/collection/{$collectionId}/{$document->getAttribute('resource')}")
|
||||
->setAttribute('event', "databases.default.{$document->getAttribute('event')}");
|
||||
}
|
||||
|
||||
break;
|
||||
case 'stats':
|
||||
/**
|
||||
* Add Database Layer to stats metric.
|
||||
*/
|
||||
if (str_starts_with($document->getAttribute('metric'), 'database.')) {
|
||||
$metric = ltrim($document->getAttribute('metric'), 'database.');
|
||||
$document->setAttribute('metric', "databases.default.{$metric}");
|
||||
}
|
||||
|
||||
break;
|
||||
case 'webhooks':
|
||||
/**
|
||||
* Add new 'signatureKey' attribute and generate a random value.
|
||||
*/
|
||||
if (empty($document->getAttribute('signatureKey'))) {
|
||||
$document->setAttribute('signatureKey', \bin2hex(\random_bytes(64)));
|
||||
}
|
||||
/**
|
||||
* Add Internal ID 'projectId' for Subqueries.
|
||||
*/
|
||||
if (!empty($document->getAttribute('projectId')) && is_null($document->getAttribute('projectInternalId'))) {
|
||||
$internalId = $this->projectDB->getDocument('projects', $document->getAttribute('projectId'))->getInternalId();
|
||||
$document->setAttribute('projectInternalId', $internalId);
|
||||
}
|
||||
|
||||
break;
|
||||
case 'domains':
|
||||
/**
|
||||
* Add Internal ID 'projectId' for Subqueries.
|
||||
*/
|
||||
if (!empty($document->getAttribute('projectId')) && is_null($document->getAttribute('projectInternalId'))) {
|
||||
$internalId = $this->projectDB->getDocument('projects', $document->getAttribute('projectId'))->getInternalId();
|
||||
$document->setAttribute('projectInternalId', $internalId);
|
||||
}
|
||||
|
||||
break;
|
||||
case 'tokens':
|
||||
case 'sessions':
|
||||
/**
|
||||
* Add Internal ID 'userId' for Subqueries.
|
||||
*/
|
||||
if (!empty($document->getAttribute('userId')) && is_null($document->getAttribute('userInternalId'))) {
|
||||
$internalId = $this->projectDB->getDocument('users', $document->getAttribute('userId'))->getInternalId();
|
||||
$document->setAttribute('userInternalId', $internalId);
|
||||
}
|
||||
|
||||
break;
|
||||
case 'memberships':
|
||||
/**
|
||||
* Add Internal ID 'userId' for Subqueries.
|
||||
*/
|
||||
if (!empty($document->getAttribute('userId')) && is_null($document->getAttribute('userInternalId'))) {
|
||||
$internalId = $this->projectDB->getDocument('users', $document->getAttribute('userId'))->getInternalId();
|
||||
$document->setAttribute('userInternalId', $internalId);
|
||||
}
|
||||
/**
|
||||
* Add Internal ID 'teamId' for Subqueries.
|
||||
*/
|
||||
if (!empty($document->getAttribute('teamId')) && is_null($document->getAttribute('teamInternalId'))) {
|
||||
$internalId = $this->projectDB->getDocument('teams', $document->getAttribute('teamId'))->getInternalId();
|
||||
$document->setAttribute('teamInternalId', $internalId);
|
||||
}
|
||||
|
||||
break;
|
||||
case 'attributes':
|
||||
case 'indexes':
|
||||
/**
|
||||
* Add Internal ID 'collectionId' for Subqueries.
|
||||
*/
|
||||
if (!empty($document->getAttribute('collectionId')) && is_null($document->getAttribute('collectionInternalId'))) {
|
||||
$internalId = $this->projectDB->getDocument('database_1', $document->getAttribute('collectionId'))->getInternalId();
|
||||
$document->setAttribute('collectionInternalId', $internalId);
|
||||
}
|
||||
/**
|
||||
* Add Internal ID 'databaseInternalId' for Subqueries.
|
||||
*/
|
||||
if (is_null($document->getAttribute('databaseInternalId'))) {
|
||||
$document->setAttribute('databaseInternalId', '1');
|
||||
}
|
||||
/**
|
||||
* Add Internal ID 'databaseInternalId' for Subqueries.
|
||||
*/
|
||||
if (is_null($document->getAttribute('databaseId'))) {
|
||||
$document->setAttribute('databaseId', 'default');
|
||||
}
|
||||
|
||||
try {
|
||||
/**
|
||||
* Re-create Collection Document
|
||||
*/
|
||||
$internalId = $this->projectDB->getDocument('database_1', $document->getAttribute('collectionId'))->getInternalId();
|
||||
$this->projectDB->deleteDocument($document->getCollection(), $document->getId());
|
||||
$this->projectDB->createDocument($document->getCollection(), $document->setAttribute('$id', "1_{$internalId}_{$document->getAttribute('key')}"));
|
||||
} catch (\Throwable $th) {
|
||||
Console::warning("Create Collection Document - {$th->getMessage()}");
|
||||
}
|
||||
$document = null;
|
||||
break;
|
||||
case 'platforms':
|
||||
/**
|
||||
* Migrate dateCreated to $createdAt.
|
||||
*/
|
||||
if (empty($document->getCreatedAt())) {
|
||||
$document->setAttribute('$createdAt', $document->getAttribute('dateCreated'));
|
||||
}
|
||||
/**
|
||||
* Migrate dateUpdated to $updatedAt.
|
||||
*/
|
||||
if (empty($document->getUpdatedAt())) {
|
||||
$document->setAttribute('$updatedAt', $document->getAttribute('dateUpdated'));
|
||||
}
|
||||
/**
|
||||
* Add Internal ID 'projectId' for Subqueries.
|
||||
*/
|
||||
if (!empty($document->getAttribute('projectId')) && is_null($document->getAttribute('projectInternalId'))) {
|
||||
$internalId = $this->projectDB->getDocument('projects', $document->getAttribute('projectId'))->getInternalId();
|
||||
$document->setAttribute('projectInternalId', $internalId);
|
||||
}
|
||||
|
||||
break;
|
||||
case 'buckets':
|
||||
/**
|
||||
* Migrate dateCreated to $createdAt.
|
||||
*/
|
||||
if (empty($document->getCreatedAt())) {
|
||||
$document->setAttribute('$createdAt', $document->getAttribute('dateCreated'));
|
||||
}
|
||||
/**
|
||||
* Migrate dateUpdated to $updatedAt.
|
||||
*/
|
||||
if (empty($document->getUpdatedAt())) {
|
||||
$document->setAttribute('$updatedAt', $document->getAttribute('dateUpdated'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate all Storage Buckets to use Internal ID.
|
||||
*/
|
||||
$internalId = $this->projectDB->getDocument('buckets', $document->getId())->getInternalId();
|
||||
$this->createNewMetaData("bucket_{$internalId}");
|
||||
|
||||
/**
|
||||
* Migrate all Storage Bucket Files.
|
||||
*/
|
||||
$this->migrateBucketFiles($document);
|
||||
|
||||
break;
|
||||
case 'users':
|
||||
/**
|
||||
* Set 'phoneVerification' to false if not set.
|
||||
*/
|
||||
if (is_null($document->getAttribute('phoneVerification'))) {
|
||||
$document->setAttribute('phoneVerification', false);
|
||||
}
|
||||
|
||||
break;
|
||||
case 'functions':
|
||||
/**
|
||||
* Migrate dateCreated to $createdAt.
|
||||
*/
|
||||
if (empty($document->getCreatedAt())) {
|
||||
$document->setAttribute('$createdAt', $document->getAttribute('dateCreated'));
|
||||
}
|
||||
/**
|
||||
* Migrate dateUpdated to $updatedAt.
|
||||
*/
|
||||
if (empty($document->getUpdatedAt())) {
|
||||
$document->setAttribute('$updatedAt', $document->getAttribute('dateUpdated'));
|
||||
}
|
||||
|
||||
break;
|
||||
case 'deployments':
|
||||
case 'executions':
|
||||
case 'teams':
|
||||
/**
|
||||
* Migrate dateCreated to $createdAt.
|
||||
*/
|
||||
if (empty($document->getCreatedAt())) {
|
||||
$document->setAttribute('$createdAt', $document->getAttribute('dateCreated'));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return $document;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new metadata that was introduced for a collection and enforces the Internal ID.
|
||||
*
|
||||
* @param string $id
|
||||
* @return void
|
||||
*/
|
||||
protected function createNewMetaData(string $id, string $to = null): void
|
||||
{
|
||||
$to ??= $id;
|
||||
/**
|
||||
* Skip files collection.
|
||||
*/
|
||||
if (in_array($id, ['files', 'databases'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
/**
|
||||
* Replace project UID with Internal ID.
|
||||
*/
|
||||
$this->pdo->prepare("ALTER TABLE IF EXISTS `{$this->projectDB->getDefaultDatabase()}`.`_{$this->project->getId()}_{$id}` RENAME TO `_{$this->project->getInternalId()}_{$to}`")->execute();
|
||||
} catch (\Throwable $th) {
|
||||
Console::warning("Migrating {$id} Collection: {$th->getMessage()}");
|
||||
}
|
||||
try {
|
||||
/**
|
||||
* Replace project UID with Internal ID on permissions table.
|
||||
*/
|
||||
$this->pdo->prepare("ALTER TABLE IF EXISTS `{$this->projectDB->getDefaultDatabase()}`.`_{$this->project->getId()}_{$id}_perms` RENAME TO `_{$this->project->getInternalId()}_{$to}_perms`")->execute();
|
||||
} catch (\Throwable $th) {
|
||||
Console::warning("Migrating {$id} Collection: {$th->getMessage()}");
|
||||
}
|
||||
try {
|
||||
/**
|
||||
* Add _createdAt attribute.
|
||||
*/
|
||||
$this->pdo->prepare("ALTER TABLE `_{$this->project->getInternalId()}_{$to}` ADD COLUMN IF NOT EXISTS `_createdAt` int unsigned DEFAULT NULL")->execute();
|
||||
} catch (\Throwable $th) {
|
||||
Console::warning("Migrating {$id} Collection: {$th->getMessage()}");
|
||||
}
|
||||
try {
|
||||
/**
|
||||
* Add _updatedAt attribute.
|
||||
*/
|
||||
$this->pdo->prepare("ALTER TABLE `_{$this->project->getInternalId()}_{$to}` ADD COLUMN IF NOT EXISTS `_updatedAt` int unsigned DEFAULT NULL")->execute();
|
||||
} catch (\Throwable $th) {
|
||||
Console::warning("Migrating {$id} Collection: {$th->getMessage()}");
|
||||
}
|
||||
try {
|
||||
/**
|
||||
* Create index for _createdAt.
|
||||
*/
|
||||
$this->pdo->prepare("CREATE INDEX IF NOT EXISTS `_created_at` ON `_{$this->project->getInternalId()}_{$to}` (`_createdAt`)")->execute();
|
||||
} catch (\Throwable $th) {
|
||||
Console::warning("Migrating {$id} Collection: {$th->getMessage()}");
|
||||
}
|
||||
try {
|
||||
/**
|
||||
* Create index for _updatedAt.
|
||||
*/
|
||||
$this->pdo->prepare("CREATE INDEX IF NOT EXISTS `_updated_at` ON `_{$this->project->getInternalId()}_{$to}` (`_updatedAt`)")->execute();
|
||||
} catch (\Throwable $th) {
|
||||
Console::warning("Migrating {$id} Collection: {$th->getMessage()}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace Appwrite\Network\Validator;
|
||||
|
||||
use Utopia\Validator\Hostname;
|
||||
use Utopia\Validator;
|
||||
|
||||
/**
|
||||
@@ -45,17 +46,16 @@ class Host extends Validator
|
||||
*/
|
||||
public function isValid($value): bool
|
||||
{
|
||||
// Check if value is valid URL
|
||||
$urlValidator = new URL();
|
||||
|
||||
if (!$urlValidator->isValid($value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (\in_array(\parse_url($value, PHP_URL_HOST), $this->whitelist)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
$hostname = \parse_url($value, PHP_URL_HOST);
|
||||
$hostnameValidator = new Hostname($this->whitelist);
|
||||
return $hostnameValidator->isValid($hostname);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -66,7 +66,7 @@ class IP extends Validator
|
||||
if (\filter_var($value, FILTER_VALIDATE_IP)) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
break;
|
||||
|
||||
case self::V4:
|
||||
if (\filter_var($value, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace Appwrite\Network\Validator;
|
||||
|
||||
use Utopia\Validator\Hostname;
|
||||
use Utopia\Validator;
|
||||
|
||||
class Origin extends Validator
|
||||
@@ -94,8 +95,8 @@ class Origin extends Validator
|
||||
return 'Unsupported platform';
|
||||
}
|
||||
|
||||
return 'Invalid Origin. Register your new client ('.$this->host.') as a new '
|
||||
.$this->platforms[$this->client].' platform on your project console dashboard';
|
||||
return 'Invalid Origin. Register your new client (' . $this->host . ') as a new '
|
||||
. $this->platforms[$this->client] . ' platform on your project console dashboard';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -122,11 +123,9 @@ class Origin extends Validator
|
||||
return true;
|
||||
}
|
||||
|
||||
if (\in_array($host, $this->clients)) {
|
||||
return true;
|
||||
}
|
||||
$validator = new Hostname($this->clients);
|
||||
|
||||
return false;
|
||||
return $validator->isValid($host);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -12,9 +12,12 @@ use Utopia\Storage\Device;
|
||||
use Utopia\Storage\Storage;
|
||||
use Utopia\Storage\Device\Local;
|
||||
use Utopia\Storage\Device\DOSpaces;
|
||||
use Utopia\Storage\Device\Linode;
|
||||
use Utopia\Storage\Device\Wasabi;
|
||||
use Utopia\Storage\Device\Backblaze;
|
||||
use Utopia\Storage\Device\S3;
|
||||
|
||||
use Exception;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
|
||||
abstract class Worker
|
||||
{
|
||||
@@ -23,7 +26,7 @@ abstract class Worker
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
static protected array $errorCallbacks = [];
|
||||
protected static array $errorCallbacks = [];
|
||||
|
||||
/**
|
||||
* Associative array holding all information passed into the worker
|
||||
@@ -50,7 +53,8 @@ abstract class Worker
|
||||
* @return void
|
||||
* @throws \Exception|\Throwable
|
||||
*/
|
||||
public function init() {
|
||||
public function init()
|
||||
{
|
||||
throw new Exception("Please implement init method in worker");
|
||||
}
|
||||
|
||||
@@ -61,7 +65,8 @@ abstract class Worker
|
||||
* @return void
|
||||
* @throws \Exception|\Throwable
|
||||
*/
|
||||
public function run() {
|
||||
public function run()
|
||||
{
|
||||
throw new Exception("Please implement run method in worker");
|
||||
}
|
||||
|
||||
@@ -72,12 +77,13 @@ abstract class Worker
|
||||
* @return void
|
||||
* @throws \Exception|\Throwable
|
||||
*/
|
||||
public function shutdown() {
|
||||
public function shutdown()
|
||||
{
|
||||
throw new Exception("Please implement shutdown method in worker");
|
||||
}
|
||||
|
||||
const DATABASE_PROJECT = 'project';
|
||||
const DATABASE_CONSOLE = 'console';
|
||||
public const DATABASE_PROJECT = 'project';
|
||||
public const DATABASE_CONSOLE = 'console';
|
||||
|
||||
/**
|
||||
* A wrapper around 'init' function with non-worker-specific code
|
||||
@@ -89,7 +95,7 @@ abstract class Worker
|
||||
{
|
||||
try {
|
||||
$this->init();
|
||||
} catch(\Throwable $error) {
|
||||
} catch (\Throwable $error) {
|
||||
foreach (self::$errorCallbacks as $errorCallback) {
|
||||
$errorCallback($error, "init", $this->getName());
|
||||
}
|
||||
@@ -107,8 +113,13 @@ abstract class Worker
|
||||
public function perform(): void
|
||||
{
|
||||
try {
|
||||
/**
|
||||
* Disabling global authorization in workers.
|
||||
*/
|
||||
Authorization::disable();
|
||||
Authorization::setDefaultStatus(false);
|
||||
$this->run();
|
||||
} catch(\Throwable $error) {
|
||||
} catch (\Throwable $error) {
|
||||
foreach (self::$errorCallbacks as $errorCallback) {
|
||||
$errorCallback($error, "run", $this->getName(), $this->args);
|
||||
}
|
||||
@@ -127,7 +138,7 @@ abstract class Worker
|
||||
{
|
||||
try {
|
||||
$this->shutdown();
|
||||
} catch(\Throwable $error) {
|
||||
} catch (\Throwable $error) {
|
||||
foreach (self::$errorCallbacks as $errorCallback) {
|
||||
$errorCallback($error, "shutdown", $this->getName());
|
||||
}
|
||||
@@ -154,7 +165,16 @@ abstract class Worker
|
||||
*/
|
||||
protected function getProjectDB(string $projectId): Database
|
||||
{
|
||||
return $this->getDB(self::DATABASE_PROJECT, $projectId);
|
||||
$consoleDB = $this->getConsoleDB();
|
||||
|
||||
if ($projectId === 'console') {
|
||||
return $consoleDB;
|
||||
}
|
||||
|
||||
/** @var Document $project */
|
||||
$project = Authorization::skip(fn() => $consoleDB->getDocument('projects', $projectId));
|
||||
|
||||
return $this->getDB(self::DATABASE_PROJECT, $projectId, $project->getInternalId());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -172,7 +192,7 @@ abstract class Worker
|
||||
* @param string $projectId of internal or external DB
|
||||
* @return Database
|
||||
*/
|
||||
private function getDB($type, $projectId = ''): Database
|
||||
private function getDB(string $type, string $projectId = '', string $projectInternalId = ''): Database
|
||||
{
|
||||
global $register;
|
||||
|
||||
@@ -184,7 +204,7 @@ abstract class Worker
|
||||
if (!$projectId) {
|
||||
throw new \Exception('ProjectID not provided - cannot get database');
|
||||
}
|
||||
$namespace = "_{$projectId}";
|
||||
$namespace = "_{$projectInternalId}";
|
||||
break;
|
||||
case self::DATABASE_CONSOLE:
|
||||
$namespace = "_console";
|
||||
@@ -209,15 +229,15 @@ abstract class Worker
|
||||
throw new \Exception("Project does not exist: {$projectId}");
|
||||
}
|
||||
|
||||
if ($type === self::DATABASE_CONSOLE && !$database->exists($database->getDefaultDatabase(), 'realtime')) {
|
||||
if ($type === self::DATABASE_CONSOLE && !$database->exists($database->getDefaultDatabase(), '_metadata')) {
|
||||
throw new \Exception('Console project not ready');
|
||||
}
|
||||
|
||||
break; // leave loop if successful
|
||||
} catch(\Exception $e) {
|
||||
} catch (\Exception $e) {
|
||||
Console::warning("Database not ready. Retrying connection ({$attempts})...");
|
||||
if ($attempts >= DATABASE_RECONNECT_MAX_ATTEMPTS) {
|
||||
throw new \Exception('Failed to connect to database: '. $e->getMessage());
|
||||
throw new \Exception('Failed to connect to database: ' . $e->getMessage());
|
||||
}
|
||||
sleep($sleep);
|
||||
}
|
||||
@@ -231,7 +251,8 @@ abstract class Worker
|
||||
* @param string $projectId of the project
|
||||
* @return Device
|
||||
*/
|
||||
protected function getFunctionsDevice($projectId): Device {
|
||||
protected function getFunctionsDevice($projectId): Device
|
||||
{
|
||||
return $this->getDevice(APP_STORAGE_FUNCTIONS . '/app-' . $projectId);
|
||||
}
|
||||
|
||||
@@ -240,7 +261,8 @@ abstract class Worker
|
||||
* @param string $projectId of the project
|
||||
* @return Device
|
||||
*/
|
||||
protected function getFilesDevice($projectId): Device {
|
||||
protected function getFilesDevice($projectId): Device
|
||||
{
|
||||
return $this->getDevice(APP_STORAGE_UPLOADS . '/app-' . $projectId);
|
||||
}
|
||||
|
||||
@@ -250,7 +272,8 @@ abstract class Worker
|
||||
* @param string $projectId of the project
|
||||
* @return Device
|
||||
*/
|
||||
protected function getBuildsDevice($projectId): Device {
|
||||
protected function getBuildsDevice($projectId): Device
|
||||
{
|
||||
return $this->getDevice(APP_STORAGE_BUILDS . '/app-' . $projectId);
|
||||
}
|
||||
|
||||
@@ -259,25 +282,47 @@ abstract class Worker
|
||||
* @param string $root path of the device
|
||||
* @return Device
|
||||
*/
|
||||
private function getDevice($root): Device
|
||||
public function getDevice($root): Device
|
||||
{
|
||||
switch (App::getEnv('_APP_STORAGE_DEVICE', Storage::DEVICE_LOCAL)) {
|
||||
case Storage::DEVICE_LOCAL:default:
|
||||
return new Local($root);
|
||||
case Storage::DEVICE_S3:
|
||||
$s3AccessKey = App::getEnv('_APP_STORAGE_S3_ACCESS_KEY', '');
|
||||
$s3SecretKey = App::getEnv('_APP_STORAGE_S3_SECRET', '');
|
||||
$s3Region = App::getEnv('_APP_STORAGE_S3_REGION', '');
|
||||
$s3Bucket = App::getEnv('_APP_STORAGE_S3_BUCKET', '');
|
||||
$s3Acl = 'private';
|
||||
return new S3($root, $s3AccessKey, $s3SecretKey, $s3Bucket, $s3Region, $s3Acl);
|
||||
case Storage::DEVICE_DO_SPACES:
|
||||
$doSpacesAccessKey = App::getEnv('_APP_STORAGE_DO_SPACES_ACCESS_KEY', '');
|
||||
$doSpacesSecretKey = App::getEnv('_APP_STORAGE_DO_SPACES_SECRET', '');
|
||||
$doSpacesRegion = App::getEnv('_APP_STORAGE_DO_SPACES_REGION', '');
|
||||
$doSpacesBucket = App::getEnv('_APP_STORAGE_DO_SPACES_BUCKET', '');
|
||||
$doSpacesAcl = 'private';
|
||||
return new DOSpaces($root, $doSpacesAccessKey, $doSpacesSecretKey, $doSpacesBucket, $doSpacesRegion, $doSpacesAcl);
|
||||
case Storage::DEVICE_LOCAL:
|
||||
default:
|
||||
return new Local($root);
|
||||
case Storage::DEVICE_S3:
|
||||
$s3AccessKey = App::getEnv('_APP_STORAGE_S3_ACCESS_KEY', '');
|
||||
$s3SecretKey = App::getEnv('_APP_STORAGE_S3_SECRET', '');
|
||||
$s3Region = App::getEnv('_APP_STORAGE_S3_REGION', '');
|
||||
$s3Bucket = App::getEnv('_APP_STORAGE_S3_BUCKET', '');
|
||||
$s3Acl = 'private';
|
||||
return new S3($root, $s3AccessKey, $s3SecretKey, $s3Bucket, $s3Region, $s3Acl);
|
||||
case Storage::DEVICE_DO_SPACES:
|
||||
$doSpacesAccessKey = App::getEnv('_APP_STORAGE_DO_SPACES_ACCESS_KEY', '');
|
||||
$doSpacesSecretKey = App::getEnv('_APP_STORAGE_DO_SPACES_SECRET', '');
|
||||
$doSpacesRegion = App::getEnv('_APP_STORAGE_DO_SPACES_REGION', '');
|
||||
$doSpacesBucket = App::getEnv('_APP_STORAGE_DO_SPACES_BUCKET', '');
|
||||
$doSpacesAcl = 'private';
|
||||
return new DOSpaces($root, $doSpacesAccessKey, $doSpacesSecretKey, $doSpacesBucket, $doSpacesRegion, $doSpacesAcl);
|
||||
case Storage::DEVICE_BACKBLAZE:
|
||||
$backblazeAccessKey = App::getEnv('_APP_STORAGE_BACKBLAZE_ACCESS_KEY', '');
|
||||
$backblazeSecretKey = App::getEnv('_APP_STORAGE_BACKBLAZE_SECRET', '');
|
||||
$backblazeRegion = App::getEnv('_APP_STORAGE_BACKBLAZE_REGION', '');
|
||||
$backblazeBucket = App::getEnv('_APP_STORAGE_BACKBLAZE_BUCKET', '');
|
||||
$backblazeAcl = 'private';
|
||||
return new Backblaze($root, $backblazeAccessKey, $backblazeSecretKey, $backblazeBucket, $backblazeRegion, $backblazeAcl);
|
||||
case Storage::DEVICE_LINODE:
|
||||
$linodeAccessKey = App::getEnv('_APP_STORAGE_LINODE_ACCESS_KEY', '');
|
||||
$linodeSecretKey = App::getEnv('_APP_STORAGE_LINODE_SECRET', '');
|
||||
$linodeRegion = App::getEnv('_APP_STORAGE_LINODE_REGION', '');
|
||||
$linodeBucket = App::getEnv('_APP_STORAGE_LINODE_BUCKET', '');
|
||||
$linodeAcl = 'private';
|
||||
return new Linode($root, $linodeAccessKey, $linodeSecretKey, $linodeBucket, $linodeRegion, $linodeAcl);
|
||||
case Storage::DEVICE_WASABI:
|
||||
$wasabiAccessKey = App::getEnv('_APP_STORAGE_WASABI_ACCESS_KEY', '');
|
||||
$wasabiSecretKey = App::getEnv('_APP_STORAGE_WASABI_SECRET', '');
|
||||
$wasabiRegion = App::getEnv('_APP_STORAGE_WASABI_REGION', '');
|
||||
$wasabiBucket = App::getEnv('_APP_STORAGE_WASABI_BUCKET', '');
|
||||
$wasabiAcl = 'private';
|
||||
return new Wasabi($root, $wasabiAccessKey, $wasabiSecretKey, $wasabiBucket, $wasabiRegion, $wasabiAcl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,40 +8,22 @@ use Appwrite\Utopia\Response\Model;
|
||||
|
||||
abstract class Format
|
||||
{
|
||||
/**
|
||||
* @var App
|
||||
*/
|
||||
protected $app;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $services;
|
||||
|
||||
protected App $app;
|
||||
|
||||
/**
|
||||
* @var Route[]
|
||||
*/
|
||||
protected $routes;
|
||||
|
||||
protected array $routes;
|
||||
|
||||
/**
|
||||
* @var Model[]
|
||||
*/
|
||||
protected $models;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $keys;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $authCount;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $params = [
|
||||
protected array $models;
|
||||
|
||||
protected array $services;
|
||||
protected array $keys;
|
||||
protected int $authCount;
|
||||
protected array $params = [
|
||||
'name' => '',
|
||||
'description' => '',
|
||||
'endpoint' => 'https://localhost',
|
||||
@@ -56,14 +38,6 @@ abstract class Format
|
||||
'license.url' => '',
|
||||
];
|
||||
|
||||
/**
|
||||
* @param App $app
|
||||
* @param array $services
|
||||
* @param Route[] $routes
|
||||
* @param Model[] $models
|
||||
* @param array $keys
|
||||
* @param int $authCount
|
||||
*/
|
||||
public function __construct(App $app, array $services, array $routes, array $models, array $keys, int $authCount)
|
||||
{
|
||||
$this->app = $app;
|
||||
@@ -99,7 +73,7 @@ abstract class Format
|
||||
*
|
||||
* @param string $key
|
||||
* @param string $value
|
||||
*
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setParam(string $key, string $value): self
|
||||
@@ -116,16 +90,11 @@ abstract class Format
|
||||
*
|
||||
* @param string $key
|
||||
* @param string $default
|
||||
*
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getParam(string $key, string $default = ''): string
|
||||
{
|
||||
if(!isset($this->params[$key])) {
|
||||
return $default;
|
||||
}
|
||||
|
||||
return $this->params[$key];
|
||||
return $this->params[$key] ?? $default;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,35 +4,40 @@ namespace Appwrite\Specification\Format;
|
||||
|
||||
use Appwrite\Specification\Format;
|
||||
use Appwrite\Template\Template;
|
||||
use Appwrite\Utopia\Response\Model;
|
||||
use Utopia\Validator;
|
||||
|
||||
class OpenAPI3 extends Format
|
||||
{
|
||||
/**
|
||||
* Get Name.
|
||||
*
|
||||
* Get format name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName():string
|
||||
public function getName(): string
|
||||
{
|
||||
return 'Open API 3';
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse
|
||||
*
|
||||
* Parses Appwrite App to given format
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getNestedModels(Model $model, array &$usedModels): void
|
||||
{
|
||||
foreach ($model->getRules() as $rule) {
|
||||
if (
|
||||
in_array($model->getType(), $usedModels)
|
||||
&& !in_array($rule['type'], ['string', 'integer', 'boolean', 'json', 'float', 'double'])
|
||||
) {
|
||||
$usedModels[] = $rule['type'];
|
||||
foreach ($this->models as $m) {
|
||||
if ($m->getType() === $rule['type']) {
|
||||
$this->getNestedModels($m, $usedModels);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function parse(): array
|
||||
{
|
||||
/**
|
||||
* Specifications (v3.0.0):
|
||||
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md
|
||||
*/
|
||||
* Specifications (v3.0.0):
|
||||
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md
|
||||
*/
|
||||
$output = [
|
||||
'openapi' => '3.0.0',
|
||||
'info' => [
|
||||
@@ -89,7 +94,7 @@ class OpenAPI3 extends Format
|
||||
|
||||
$usedModels = [];
|
||||
|
||||
foreach ($this->routes as $route) { /** @var \Utopia\Route $route */
|
||||
foreach ($this->routes as $route) {
|
||||
$url = \str_replace('/v1', '', $route->getPath());
|
||||
$scope = $route->getLabel('scope', '');
|
||||
$hide = $route->getLabel('sdk.hide', false);
|
||||
@@ -100,38 +105,36 @@ class OpenAPI3 extends Format
|
||||
}
|
||||
|
||||
$id = $route->getLabel('sdk.method', \uniqid());
|
||||
$desc = (!empty($route->getLabel('sdk.description', ''))) ? \realpath(__DIR__.'/../../../../'.$route->getLabel('sdk.description', '')) : null;
|
||||
$desc = (!empty($route->getLabel('sdk.description', ''))) ? \realpath(__DIR__ . '/../../../../' . $route->getLabel('sdk.description', '')) : null;
|
||||
$produces = $route->getLabel('sdk.response.type', null);
|
||||
$model = $route->getLabel('sdk.response.model', 'none');
|
||||
$routeSecurity = $route->getLabel('sdk.auth', []);
|
||||
$sdkPlatofrms = [];
|
||||
$sdkPlatforms = [];
|
||||
|
||||
foreach ($routeSecurity as $value) {
|
||||
switch ($value) {
|
||||
case APP_AUTH_TYPE_SESSION:
|
||||
$sdkPlatofrms[] = APP_PLATFORM_CLIENT;
|
||||
$sdkPlatforms[] = APP_PLATFORM_CLIENT;
|
||||
break;
|
||||
case APP_AUTH_TYPE_KEY:
|
||||
$sdkPlatofrms[] = APP_PLATFORM_SERVER;
|
||||
$sdkPlatforms[] = APP_PLATFORM_SERVER;
|
||||
break;
|
||||
case APP_AUTH_TYPE_JWT:
|
||||
$sdkPlatofrms[] = APP_PLATFORM_SERVER;
|
||||
$sdkPlatforms[] = APP_PLATFORM_SERVER;
|
||||
break;
|
||||
case APP_AUTH_TYPE_ADMIN:
|
||||
$sdkPlatofrms[] = APP_PLATFORM_CONSOLE;
|
||||
$sdkPlatforms[] = APP_PLATFORM_CONSOLE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(empty($routeSecurity)) {
|
||||
$sdkPlatofrms[] = APP_PLATFORM_CLIENT;
|
||||
if (empty($routeSecurity)) {
|
||||
$sdkPlatforms[] = APP_PLATFORM_CLIENT;
|
||||
}
|
||||
|
||||
$temp = [
|
||||
'summary' => $route->getDesc(),
|
||||
'operationId' => $route->getLabel('sdk.namespace', 'default').ucfirst($id),
|
||||
// 'consumes' => [],
|
||||
// 'produces' => [$produces],
|
||||
'operationId' => $route->getLabel('sdk.namespace', 'default') . ucfirst($id),
|
||||
'tags' => [$route->getLabel('sdk.namespace', 'default')],
|
||||
'description' => ($desc) ? \file_get_contents($desc) : '',
|
||||
'responses' => [],
|
||||
@@ -140,38 +143,31 @@ class OpenAPI3 extends Format
|
||||
'weight' => $route->getOrder(),
|
||||
'cookies' => $route->getLabel('sdk.cookies', false),
|
||||
'type' => $route->getLabel('sdk.methodType', ''),
|
||||
'demo' => Template::fromCamelCaseToDash($route->getLabel('sdk.namespace', 'default')).'/'.Template::fromCamelCaseToDash($id).'.md',
|
||||
'demo' => Template::fromCamelCaseToDash($route->getLabel('sdk.namespace', 'default')) . '/' . Template::fromCamelCaseToDash($id) . '.md',
|
||||
'edit' => 'https://github.com/appwrite/appwrite/edit/master' . $route->getLabel('sdk.description', ''),
|
||||
'rate-limit' => $route->getLabel('abuse-limit', 0),
|
||||
'rate-time' => $route->getLabel('abuse-time', 3600),
|
||||
'rate-key' => $route->getLabel('abuse-key', 'url:{url},ip:{ip}'),
|
||||
'scope' => $route->getLabel('scope', ''),
|
||||
'platforms' => $sdkPlatofrms,
|
||||
'platforms' => $sdkPlatforms,
|
||||
'packaging' => $route->getLabel('sdk.packaging', false),
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($this->models as $key => $value) {
|
||||
if(\is_array($model)) {
|
||||
$model = \array_map(function($m) use($value) {
|
||||
if($m === $value->getType()) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
return $m;
|
||||
}, $model);
|
||||
foreach ($this->models as $value) {
|
||||
if (\is_array($model)) {
|
||||
$model = \array_map(fn ($m) => $m === $value->getType() ? $value : $m, $model);
|
||||
} else {
|
||||
if($value->getType() === $model) {
|
||||
if ($value->getType() === $model) {
|
||||
$model = $value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if(!(\is_array($model)) && $model->isNone()) {
|
||||
if (!(\is_array($model)) && $model->isNone()) {
|
||||
$temp['responses'][(string)$route->getLabel('sdk.response.code', '500')] = [
|
||||
'description' => (in_array($produces, [
|
||||
'description' => in_array($produces, [
|
||||
'image/*',
|
||||
'image/jpeg',
|
||||
'image/gif',
|
||||
@@ -180,16 +176,11 @@ class OpenAPI3 extends Format
|
||||
'image/svg-x',
|
||||
'image/x-icon',
|
||||
'image/bmp',
|
||||
])) ? 'Image' : 'File',
|
||||
// 'schema' => [
|
||||
// 'type' => 'file'
|
||||
// ],
|
||||
]) ? 'Image' : 'File',
|
||||
];
|
||||
} else {
|
||||
if(\is_array($model)) {
|
||||
$modelDescription = \join(', or ', \array_map(function ($m) {
|
||||
return $m->getName();
|
||||
}, $model));
|
||||
if (\is_array($model)) {
|
||||
$modelDescription = \join(', or ', \array_map(fn ($m) => $m->getName(), $model));
|
||||
|
||||
// model has multiple possible responses, we will use oneOf
|
||||
foreach ($model as $m) {
|
||||
@@ -201,9 +192,7 @@ class OpenAPI3 extends Format
|
||||
'content' => [
|
||||
$produces => [
|
||||
'schema' => [
|
||||
'oneOf' => \array_map(function($m) {
|
||||
return ['$ref' => '#/components/schemas/'.$m->getType()];
|
||||
}, $model)
|
||||
'oneOf' => \array_map(fn ($m) => ['$ref' => '#/components/schemas/' . $m->getType()], $model)
|
||||
],
|
||||
],
|
||||
],
|
||||
@@ -216,16 +205,15 @@ class OpenAPI3 extends Format
|
||||
'content' => [
|
||||
$produces => [
|
||||
'schema' => [
|
||||
'$ref' => '#/components/schemas/'.$model->getType(),
|
||||
'$ref' => '#/components/schemas/' . $model->getType(),
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if($route->getLabel('sdk.response.code', 500) === 204) {
|
||||
if ($route->getLabel('sdk.response.code', 500) === 204) {
|
||||
$temp['responses'][(string)$route->getLabel('sdk.response.code', '500')]['description'] = 'No content';
|
||||
unset($temp['responses'][(string)$route->getLabel('sdk.response.code', '500')]['schema']);
|
||||
}
|
||||
@@ -233,8 +221,8 @@ class OpenAPI3 extends Format
|
||||
if ((!empty($scope))) { // && 'public' != $scope
|
||||
$securities = ['Project' => []];
|
||||
|
||||
foreach($route->getLabel('sdk.auth', []) as $security) {
|
||||
if(array_key_exists($security, $this->keys)) {
|
||||
foreach ($route->getLabel('sdk.auth', []) as $security) {
|
||||
if (array_key_exists($security, $this->keys)) {
|
||||
$securities[$security] = [];
|
||||
}
|
||||
}
|
||||
@@ -257,7 +245,10 @@ class OpenAPI3 extends Format
|
||||
$bodyRequired = [];
|
||||
|
||||
foreach ($route->getParams() as $name => $param) { // Set params
|
||||
$validator = (\is_callable($param['validator'])) ? call_user_func_array($param['validator'], $this->app->getResources($param['injections'])) : $param['validator']; /* @var $validator \Utopia\Validator */
|
||||
/**
|
||||
* @var \Utopia\Validator $validator
|
||||
*/
|
||||
$validator = (\is_callable($param['validator'])) ? call_user_func_array($param['validator'], $this->app->getResources($param['injections'])) : $param['validator'];
|
||||
|
||||
$node = [
|
||||
'name' => $name,
|
||||
@@ -265,10 +256,16 @@ class OpenAPI3 extends Format
|
||||
'required' => !$param['optional'],
|
||||
];
|
||||
|
||||
foreach ($this->services as $service) {
|
||||
if ($route->getLabel('sdk.namespace', 'default') === $service['name'] && in_array($name, $service['x-globalAttributes'] ?? [])) {
|
||||
$node['x-global'] = true;
|
||||
}
|
||||
}
|
||||
|
||||
switch ((!empty($validator)) ? \get_class($validator) : '') {
|
||||
case 'Utopia\Validator\Text':
|
||||
$node['schema']['type'] = $validator->getType();
|
||||
$node['schema']['x-example'] = '['.\strtoupper(Template::fromCamelCaseToSnake($node['name'])).']';
|
||||
$node['schema']['x-example'] = '[' . \strtoupper(Template::fromCamelCaseToSnake($node['name'])) . ']';
|
||||
break;
|
||||
case 'Utopia\Validator\Boolean':
|
||||
$node['schema']['type'] = $validator->getType();
|
||||
@@ -276,14 +273,14 @@ class OpenAPI3 extends Format
|
||||
break;
|
||||
case 'Utopia\Database\Validator\UID':
|
||||
$node['schema']['type'] = $validator->getType();
|
||||
$node['schema']['x-example'] = '['.\strtoupper(Template::fromCamelCaseToSnake($node['name'])).']';
|
||||
$node['schema']['x-example'] = '[' . \strtoupper(Template::fromCamelCaseToSnake($node['name'])) . ']';
|
||||
break;
|
||||
case 'Appwrite\Utopia\Database\Validator\CustomId':
|
||||
if($route->getLabel('sdk.methodType', '') === 'upload') {
|
||||
if ($route->getLabel('sdk.methodType', '') === 'upload') {
|
||||
$node['schema']['x-upload-id'] = true;
|
||||
}
|
||||
$node['schema']['type'] = $validator->getType();
|
||||
$node['schema']['x-example'] = '['.\strtoupper(Template::fromCamelCaseToSnake($node['name'])).']';
|
||||
$node['schema']['x-example'] = '[' . \strtoupper(Template::fromCamelCaseToSnake($node['name'])) . ']';
|
||||
break;
|
||||
case 'Appwrite\Network\Validator\Email':
|
||||
$node['schema']['type'] = $validator->getType();
|
||||
@@ -325,8 +322,9 @@ class OpenAPI3 extends Format
|
||||
$node['schema']['format'] = 'password';
|
||||
$node['schema']['x-example'] = 'password';
|
||||
break;
|
||||
case 'Utopia\Validator\Range': /** @var \Utopia\Validator\Range $validator */
|
||||
$node['schema']['type'] = $validator->getType() === Validator::TYPE_FLOAT ? 'number': $validator->getType();
|
||||
case 'Utopia\Validator\Range':
|
||||
/** @var \Utopia\Validator\Range $validator */
|
||||
$node['schema']['type'] = $validator->getType() === Validator::TYPE_FLOAT ? 'number' : $validator->getType();
|
||||
$node['schema']['format'] = $validator->getType() == Validator::TYPE_INTEGER ? 'int32' : 'float';
|
||||
$node['schema']['x-example'] = $validator->getMin();
|
||||
break;
|
||||
@@ -347,7 +345,8 @@ class OpenAPI3 extends Format
|
||||
$node['schema']['format'] = 'url';
|
||||
$node['schema']['x-example'] = 'https://example.com';
|
||||
break;
|
||||
case 'Utopia\Validator\WhiteList': /** @var \Utopia\Validator\WhiteList $validator */
|
||||
case 'Utopia\Validator\WhiteList':
|
||||
/** @var \Utopia\Validator\WhiteList $validator */
|
||||
$node['schema']['type'] = $validator->getType();
|
||||
$node['schema']['x-example'] = $validator->getList()[0];
|
||||
|
||||
@@ -364,14 +363,14 @@ class OpenAPI3 extends Format
|
||||
$node['schema']['default'] = $param['default'];
|
||||
}
|
||||
|
||||
if (false !== \strpos($url, ':'.$name)) { // Param is in URL path
|
||||
if (false !== \strpos($url, ':' . $name)) { // Param is in URL path
|
||||
$node['in'] = 'path';
|
||||
$temp['parameters'][] = $node;
|
||||
} elseif ($route->getMethod() == 'GET') { // Param is in query
|
||||
$node['in'] = 'query';
|
||||
$temp['parameters'][] = $node;
|
||||
} else { // Param is in payload
|
||||
if(!$param['optional']) {
|
||||
if (!$param['optional']) {
|
||||
$bodyRequired[] = $name;
|
||||
}
|
||||
|
||||
@@ -381,44 +380,39 @@ class OpenAPI3 extends Format
|
||||
'x-example' => $node['schema']['x-example'] ?? null
|
||||
];
|
||||
|
||||
if($node['schema']['x-upload-id'] ?? false) {
|
||||
if ($node['schema']['x-upload-id'] ?? false) {
|
||||
$body['content'][$consumes[0]]['schema']['properties'][$name]['x-upload-id'] = $node['schema']['x-upload-id'];
|
||||
}
|
||||
|
||||
if(isset($node['default'])) {
|
||||
if (isset($node['default'])) {
|
||||
$body['content'][$consumes[0]]['schema']['properties'][$name]['default'] = $node['default'];
|
||||
}
|
||||
|
||||
if(\array_key_exists('items', $node['schema'])) {
|
||||
if (\array_key_exists('items', $node['schema'])) {
|
||||
$body['content'][$consumes[0]]['schema']['properties'][$name]['items'] = $node['schema']['items'];
|
||||
}
|
||||
|
||||
if ($node['x-global'] ?? false) {
|
||||
$body['content'][$consumes[0]]['schema']['properties'][$name]['x-global'] = true;
|
||||
}
|
||||
}
|
||||
|
||||
$url = \str_replace(':'.$name, '{'.$name.'}', $url);
|
||||
$url = \str_replace(':' . $name, '{' . $name . '}', $url);
|
||||
}
|
||||
|
||||
if(!empty($bodyRequired)) {
|
||||
if (!empty($bodyRequired)) {
|
||||
$body['content'][$consumes[0]]['schema']['required'] = $bodyRequired;
|
||||
}
|
||||
|
||||
if(!empty($body['content'][$consumes[0]]['schema']['properties'])) {
|
||||
if (!empty($body['content'][$consumes[0]]['schema']['properties'])) {
|
||||
$temp['requestBody'] = $body;
|
||||
}
|
||||
|
||||
//$temp['consumes'] = $consumes;
|
||||
|
||||
$output['paths'][$url][\strtolower($route->getMethod())] = $temp;
|
||||
}
|
||||
|
||||
foreach ($this->models as $model) {
|
||||
foreach ($model->getRules() as $rule) {
|
||||
if (
|
||||
in_array($model->getType(), $usedModels)
|
||||
&& !in_array($rule['type'], ['string', 'integer', 'boolean', 'json', 'float'])
|
||||
) {
|
||||
$usedModels[] = $rule['type'];
|
||||
}
|
||||
}
|
||||
$this->getNestedModels($model, $usedModels);
|
||||
}
|
||||
|
||||
foreach ($this->models as $model) {
|
||||
@@ -434,19 +428,19 @@ class OpenAPI3 extends Format
|
||||
'type' => 'object',
|
||||
];
|
||||
|
||||
if(!empty($rules)) {
|
||||
if (!empty($rules)) {
|
||||
$output['components']['schemas'][$model->getType()]['properties'] = [];
|
||||
}
|
||||
|
||||
if($model->isAny()) {
|
||||
if ($model->isAny()) {
|
||||
$output['components']['schemas'][$model->getType()]['additionalProperties'] = true;
|
||||
}
|
||||
|
||||
if(!empty($required)) {
|
||||
if (!empty($required)) {
|
||||
$output['components']['schemas'][$model->getType()]['required'] = $required;
|
||||
}
|
||||
|
||||
foreach($model->getRules() as $name => $rule) {
|
||||
foreach ($model->getRules() as $name => $rule) {
|
||||
$type = '';
|
||||
$format = null;
|
||||
$items = null;
|
||||
@@ -484,29 +478,29 @@ class OpenAPI3 extends Format
|
||||
$type = 'object';
|
||||
$rule['type'] = ($rule['type']) ? $rule['type'] : 'none';
|
||||
|
||||
if(\is_array($rule['type'])) {
|
||||
if($rule['array']) {
|
||||
if (\is_array($rule['type'])) {
|
||||
if ($rule['array']) {
|
||||
$items = [
|
||||
'anyOf' => \array_map(function($type) {
|
||||
return ['$ref' => '#/components/schemas/'.$type];
|
||||
'anyOf' => \array_map(function ($type) {
|
||||
return ['$ref' => '#/components/schemas/' . $type];
|
||||
}, $rule['type'])
|
||||
];
|
||||
} else {
|
||||
$items = [
|
||||
'oneOf' => \array_map(function($type) {
|
||||
return ['$ref' => '#/components/schemas/'.$type];
|
||||
'oneOf' => \array_map(function ($type) {
|
||||
return ['$ref' => '#/components/schemas/' . $type];
|
||||
}, $rule['type'])
|
||||
];
|
||||
}
|
||||
} else {
|
||||
$items = [
|
||||
'$ref' => '#/components/schemas/'.$rule['type'],
|
||||
'$ref' => '#/components/schemas/' . $rule['type'],
|
||||
];
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if($rule['array']) {
|
||||
if ($rule['array']) {
|
||||
$output['components']['schemas'][$model->getType()]['properties'][$name] = [
|
||||
'type' => 'array',
|
||||
'description' => $rule['description'] ?? '',
|
||||
@@ -516,10 +510,9 @@ class OpenAPI3 extends Format
|
||||
'x-example' => $rule['example'] ?? null,
|
||||
];
|
||||
|
||||
if($format) {
|
||||
if ($format) {
|
||||
$output['components']['schemas'][$model->getType()]['properties'][$name]['items']['format'] = $format;
|
||||
}
|
||||
|
||||
} else {
|
||||
$output['components']['schemas'][$model->getType()]['properties'][$name] = [
|
||||
'type' => $type,
|
||||
@@ -527,12 +520,11 @@ class OpenAPI3 extends Format
|
||||
'x-example' => $rule['example'] ?? null,
|
||||
];
|
||||
|
||||
if($format) {
|
||||
if ($format) {
|
||||
$output['components']['schemas'][$model->getType()]['properties'][$name]['format'] = $format;
|
||||
}
|
||||
|
||||
}
|
||||
if($items) {
|
||||
if ($items) {
|
||||
$output['components']['schemas'][$model->getType()]['properties'][$name]['items'] = $items;
|
||||
}
|
||||
if (!in_array($name, $required)) {
|
||||
|
||||
@@ -4,34 +4,39 @@ namespace Appwrite\Specification\Format;
|
||||
|
||||
use Appwrite\Specification\Format;
|
||||
use Appwrite\Template\Template;
|
||||
use Appwrite\Utopia\Response\Model;
|
||||
use Utopia\Validator;
|
||||
|
||||
class Swagger2 extends Format
|
||||
{
|
||||
/**
|
||||
* Get Name.
|
||||
*
|
||||
* Get format name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName():string
|
||||
public function getName(): string
|
||||
{
|
||||
return 'Swagger 2';
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse
|
||||
*
|
||||
* Parses Appwrite App to given format
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getNestedModels(Model $model, array &$usedModels): void
|
||||
{
|
||||
foreach ($model->getRules() as $rule) {
|
||||
if (
|
||||
in_array($model->getType(), $usedModels)
|
||||
&& !in_array($rule['type'], ['string', 'integer', 'boolean', 'json', 'float', 'double'])
|
||||
) {
|
||||
$usedModels[] = $rule['type'];
|
||||
foreach ($this->models as $m) {
|
||||
if ($m->getType() === $rule['type']) {
|
||||
$this->getNestedModels($m, $usedModels);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function parse(): array
|
||||
{
|
||||
/*
|
||||
* Specifications (v3.0.0):
|
||||
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md
|
||||
* Specifications (v2.0):
|
||||
* https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md
|
||||
*/
|
||||
$output = [
|
||||
'swagger' => '2.0',
|
||||
@@ -87,7 +92,8 @@ class Swagger2 extends Format
|
||||
|
||||
$usedModels = [];
|
||||
|
||||
foreach ($this->routes as $route) { /** @var \Utopia\Route $route */
|
||||
foreach ($this->routes as $route) {
|
||||
/** @var \Utopia\Route $route */
|
||||
$url = \str_replace('/v1', '', $route->getPath());
|
||||
$scope = $route->getLabel('scope', '');
|
||||
$hide = $route->getLabel('sdk.hide', false);
|
||||
@@ -98,36 +104,36 @@ class Swagger2 extends Format
|
||||
}
|
||||
|
||||
$id = $route->getLabel('sdk.method', \uniqid());
|
||||
$desc = (!empty($route->getLabel('sdk.description', ''))) ? \realpath(__DIR__.'/../../../../'.$route->getLabel('sdk.description', '')) : null;
|
||||
$desc = (!empty($route->getLabel('sdk.description', ''))) ? \realpath(__DIR__ . '/../../../../' . $route->getLabel('sdk.description', '')) : null;
|
||||
$produces = $route->getLabel('sdk.response.type', null);
|
||||
$model = $route->getLabel('sdk.response.model', 'none');
|
||||
$routeSecurity = $route->getLabel('sdk.auth', []);
|
||||
$sdkPlatofrms = [];
|
||||
$sdkPlatforms = [];
|
||||
|
||||
foreach ($routeSecurity as $value) {
|
||||
switch ($value) {
|
||||
case APP_AUTH_TYPE_SESSION:
|
||||
$sdkPlatofrms[] = APP_PLATFORM_CLIENT;
|
||||
$sdkPlatforms[] = APP_PLATFORM_CLIENT;
|
||||
break;
|
||||
case APP_AUTH_TYPE_KEY:
|
||||
$sdkPlatofrms[] = APP_PLATFORM_SERVER;
|
||||
$sdkPlatforms[] = APP_PLATFORM_SERVER;
|
||||
break;
|
||||
case APP_AUTH_TYPE_JWT:
|
||||
$sdkPlatofrms[] = APP_PLATFORM_SERVER;
|
||||
$sdkPlatforms[] = APP_PLATFORM_SERVER;
|
||||
break;
|
||||
case APP_AUTH_TYPE_ADMIN:
|
||||
$sdkPlatofrms[] = APP_PLATFORM_CONSOLE;
|
||||
$sdkPlatforms[] = APP_PLATFORM_CONSOLE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(empty($routeSecurity)) {
|
||||
$sdkPlatofrms[] = APP_PLATFORM_CLIENT;
|
||||
if (empty($routeSecurity)) {
|
||||
$sdkPlatforms[] = APP_PLATFORM_CLIENT;
|
||||
}
|
||||
|
||||
$temp = [
|
||||
'summary' => $route->getDesc(),
|
||||
'operationId' => $route->getLabel('sdk.namespace', 'default').ucfirst($id),
|
||||
'operationId' => $route->getLabel('sdk.namespace', 'default') . ucfirst($id),
|
||||
'consumes' => [],
|
||||
'produces' => [],
|
||||
'tags' => [$route->getLabel('sdk.namespace', 'default')],
|
||||
@@ -138,40 +144,35 @@ class Swagger2 extends Format
|
||||
'weight' => $route->getOrder(),
|
||||
'cookies' => $route->getLabel('sdk.cookies', false),
|
||||
'type' => $route->getLabel('sdk.methodType', ''),
|
||||
'demo' => Template::fromCamelCaseToDash($route->getLabel('sdk.namespace', 'default')).'/'.Template::fromCamelCaseToDash($id).'.md',
|
||||
'demo' => Template::fromCamelCaseToDash($route->getLabel('sdk.namespace', 'default')) . '/' . Template::fromCamelCaseToDash($id) . '.md',
|
||||
'edit' => 'https://github.com/appwrite/appwrite/edit/master' . $route->getLabel('sdk.description', ''),
|
||||
'rate-limit' => $route->getLabel('abuse-limit', 0),
|
||||
'rate-time' => $route->getLabel('abuse-time', 3600),
|
||||
'rate-key' => $route->getLabel('abuse-key', 'url:{url},ip:{ip}'),
|
||||
'scope' => $route->getLabel('scope', ''),
|
||||
'platforms' => $sdkPlatofrms,
|
||||
'platforms' => $sdkPlatforms,
|
||||
'packaging' => $route->getLabel('sdk.packaging', false),
|
||||
],
|
||||
];
|
||||
|
||||
if($produces) {
|
||||
if ($produces) {
|
||||
$temp['produces'][] = $produces;
|
||||
}
|
||||
|
||||
foreach ($this->models as $key => $value) {
|
||||
if(\is_array($model)) {
|
||||
$model = \array_map(function($m) use($value) {
|
||||
if($m === $value->getType()) {
|
||||
return $value;
|
||||
}
|
||||
return $m;
|
||||
}, $model);
|
||||
foreach ($this->models as $value) {
|
||||
if (\is_array($model)) {
|
||||
$model = \array_map(fn ($m) => $m === $value->getType() ? $value : $m, $model);
|
||||
} else {
|
||||
if($value->getType() === $model) {
|
||||
if ($value->getType() === $model) {
|
||||
$model = $value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!(\is_array($model)) && $model->isNone()) {
|
||||
if (!(\is_array($model)) && $model->isNone()) {
|
||||
$temp['responses'][(string)$route->getLabel('sdk.response.code', '500')] = [
|
||||
'description' => (in_array($produces, [
|
||||
'description' => in_array($produces, [
|
||||
'image/*',
|
||||
'image/jpeg',
|
||||
'image/gif',
|
||||
@@ -180,17 +181,14 @@ class Swagger2 extends Format
|
||||
'image/svg-x',
|
||||
'image/x-icon',
|
||||
'image/bmp',
|
||||
])) ? 'Image' : 'File',
|
||||
]) ? 'Image' : 'File',
|
||||
'schema' => [
|
||||
'type' => 'file'
|
||||
],
|
||||
];
|
||||
} else {
|
||||
|
||||
if(\is_array($model)) {
|
||||
$modelDescription = \join(', or ', \array_map(function ($m) {
|
||||
return $m->getName();
|
||||
}, $model));
|
||||
if (\is_array($model)) {
|
||||
$modelDescription = \join(', or ', \array_map(fn ($m) => $m->getName(), $model));
|
||||
// model has multiple possible responses, we will use oneOf
|
||||
foreach ($model as $m) {
|
||||
$usedModels[] = $m->getType();
|
||||
@@ -198,8 +196,8 @@ class Swagger2 extends Format
|
||||
$temp['responses'][(string)$route->getLabel('sdk.response.code', '500')] = [
|
||||
'description' => $modelDescription,
|
||||
'schema' => [
|
||||
'x-oneOf' => \array_map(function($m) {
|
||||
return ['$ref' => '#/definitions/'.$m->getType()];
|
||||
'x-oneOf' => \array_map(function ($m) {
|
||||
return ['$ref' => '#/definitions/' . $m->getType()];
|
||||
}, $model)
|
||||
],
|
||||
];
|
||||
@@ -209,13 +207,13 @@ class Swagger2 extends Format
|
||||
$temp['responses'][(string)$route->getLabel('sdk.response.code', '500')] = [
|
||||
'description' => $model->getName(),
|
||||
'schema' => [
|
||||
'$ref' => '#/definitions/'.$model->getType(),
|
||||
'$ref' => '#/definitions/' . $model->getType(),
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
if(in_array($route->getLabel('sdk.response.code', 500), [204, 301, 302, 308], true)) {
|
||||
if (in_array($route->getLabel('sdk.response.code', 500), [204, 301, 302, 308], true)) {
|
||||
$temp['responses'][(string)$route->getLabel('sdk.response.code', '500')]['description'] = 'No content';
|
||||
unset($temp['responses'][(string)$route->getLabel('sdk.response.code', '500')]['schema']);
|
||||
}
|
||||
@@ -223,8 +221,8 @@ class Swagger2 extends Format
|
||||
if ((!empty($scope))) { // && 'public' != $scope
|
||||
$securities = ['Project' => []];
|
||||
|
||||
foreach($route->getLabel('sdk.auth', []) as $security) {
|
||||
if(array_key_exists($security, $this->keys)) {
|
||||
foreach ($route->getLabel('sdk.auth', []) as $security) {
|
||||
if (array_key_exists($security, $this->keys)) {
|
||||
$securities[$security] = [];
|
||||
}
|
||||
}
|
||||
@@ -245,7 +243,8 @@ class Swagger2 extends Format
|
||||
$bodyRequired = [];
|
||||
|
||||
foreach ($route->getParams() as $name => $param) { // Set params
|
||||
$validator = (\is_callable($param['validator'])) ? call_user_func_array($param['validator'], $this->app->getResources($param['injections'])) : $param['validator']; /** @var \Utopia\Validator $validator */
|
||||
/** @var \Utopia\Validator $validator */
|
||||
$validator = (\is_callable($param['validator'])) ? call_user_func_array($param['validator'], $this->app->getResources($param['injections'])) : $param['validator'];
|
||||
|
||||
$node = [
|
||||
'name' => $name,
|
||||
@@ -253,25 +252,31 @@ class Swagger2 extends Format
|
||||
'required' => !$param['optional'],
|
||||
];
|
||||
|
||||
foreach ($this->services as $service) {
|
||||
if ($route->getLabel('sdk.namespace', 'default') === $service['name'] && in_array($name, $service['x-globalAttributes'] ?? [])) {
|
||||
$node['x-global'] = true;
|
||||
}
|
||||
}
|
||||
|
||||
switch ((!empty($validator)) ? \get_class($validator) : '') {
|
||||
case 'Utopia\Validator\Text':
|
||||
$node['type'] = $validator->getType();
|
||||
$node['x-example'] = '['.\strtoupper(Template::fromCamelCaseToSnake($node['name'])).']';
|
||||
$node['x-example'] = '[' . \strtoupper(Template::fromCamelCaseToSnake($node['name'])) . ']';
|
||||
break;
|
||||
case 'Utopia\Validator\Boolean':
|
||||
$node['type'] = $validator->getType();
|
||||
$node['x-example'] = false;
|
||||
break;
|
||||
case 'Appwrite\Utopia\Database\Validator\CustomId':
|
||||
if($route->getLabel('sdk.methodType', '') === 'upload') {
|
||||
if ($route->getLabel('sdk.methodType', '') === 'upload') {
|
||||
$node['x-upload-id'] = true;
|
||||
}
|
||||
$node['type'] = $validator->getType();
|
||||
$node['x-example'] = '['.\strtoupper(Template::fromCamelCaseToSnake($node['name'])).']';
|
||||
$node['x-example'] = '[' . \strtoupper(Template::fromCamelCaseToSnake($node['name'])) . ']';
|
||||
break;
|
||||
case 'Utopia\Database\Validator\UID':
|
||||
$node['type'] = $validator->getType();
|
||||
$node['x-example'] = '['.\strtoupper(Template::fromCamelCaseToSnake($node['name'])).']';
|
||||
$node['x-example'] = '[' . \strtoupper(Template::fromCamelCaseToSnake($node['name'])) . ']';
|
||||
break;
|
||||
case 'Appwrite\Network\Validator\Email':
|
||||
$node['type'] = $validator->getType();
|
||||
@@ -289,7 +294,6 @@ class Swagger2 extends Format
|
||||
$node['type'] = 'object';
|
||||
$node['default'] = (empty($param['default'])) ? new \stdClass() : $param['default'];
|
||||
$node['x-example'] = '{}';
|
||||
//$node['format'] = 'json';
|
||||
break;
|
||||
case 'Utopia\Storage\Validator\File':
|
||||
$consumes = ['multipart/form-data'];
|
||||
@@ -315,8 +319,9 @@ class Swagger2 extends Format
|
||||
$node['format'] = 'password';
|
||||
$node['x-example'] = 'password';
|
||||
break;
|
||||
case 'Utopia\Validator\Range': /** @var \Utopia\Validator\Range $validator */
|
||||
$node['type'] = $validator->getType() === Validator::TYPE_FLOAT ? 'number': $validator->getType();
|
||||
case 'Utopia\Validator\Range':
|
||||
/** @var \Utopia\Validator\Range $validator */
|
||||
$node['type'] = $validator->getType() === Validator::TYPE_FLOAT ? 'number' : $validator->getType();
|
||||
$node['format'] = $validator->getType() == Validator::TYPE_INTEGER ? 'int32' : 'float';
|
||||
$node['x-example'] = $validator->getMin();
|
||||
break;
|
||||
@@ -337,7 +342,8 @@ class Swagger2 extends Format
|
||||
$node['format'] = 'url';
|
||||
$node['x-example'] = 'https://example.com';
|
||||
break;
|
||||
case 'Utopia\Validator\WhiteList': /** @var \Utopia\Validator\WhiteList $validator */
|
||||
case 'Utopia\Validator\WhiteList':
|
||||
/** @var \Utopia\Validator\WhiteList $validator */
|
||||
$node['type'] = $validator->getType();
|
||||
$node['x-example'] = $validator->getList()[0];
|
||||
|
||||
@@ -354,14 +360,13 @@ class Swagger2 extends Format
|
||||
$node['default'] = $param['default'];
|
||||
}
|
||||
|
||||
if (false !== \strpos($url, ':'.$name)) { // Param is in URL path
|
||||
if (false !== \strpos($url, ':' . $name)) { // Param is in URL path
|
||||
$node['in'] = 'path';
|
||||
$temp['parameters'][] = $node;
|
||||
} elseif ($route->getMethod() == 'GET') { // Param is in query
|
||||
$node['in'] = 'query';
|
||||
$temp['parameters'][] = $node;
|
||||
} else { // Param is in payload
|
||||
|
||||
if (\in_array('multipart/form-data', $consumes)) {
|
||||
$node['in'] = 'formData';
|
||||
$temp['parameters'][] = $node;
|
||||
@@ -380,19 +385,23 @@ class Swagger2 extends Format
|
||||
'x-example' => $node['x-example'] ?? null,
|
||||
];
|
||||
|
||||
if(\array_key_exists('items', $node)) {
|
||||
if ($node['x-global'] ?? false) {
|
||||
$body['schema']['properties'][$name]['x-global'] = true;
|
||||
}
|
||||
|
||||
if (\array_key_exists('items', $node)) {
|
||||
$body['schema']['properties'][$name]['items'] = $node['items'];
|
||||
}
|
||||
}
|
||||
|
||||
$url = \str_replace(':'.$name, '{'.$name.'}', $url);
|
||||
$url = \str_replace(':' . $name, '{' . $name . '}', $url);
|
||||
}
|
||||
|
||||
if(!empty($bodyRequired)) {
|
||||
if (!empty($bodyRequired)) {
|
||||
$body['schema']['required'] = $bodyRequired;
|
||||
}
|
||||
|
||||
if(!empty($body['schema']['properties'])) {
|
||||
if (!empty($body['schema']['properties'])) {
|
||||
$temp['parameters'][] = $body;
|
||||
}
|
||||
|
||||
@@ -402,14 +411,7 @@ class Swagger2 extends Format
|
||||
}
|
||||
|
||||
foreach ($this->models as $model) {
|
||||
foreach ($model->getRules() as $rule) {
|
||||
if (
|
||||
in_array($model->getType(), $usedModels)
|
||||
&& !in_array($rule['type'], ['string', 'integer', 'boolean', 'json', 'float'])
|
||||
) {
|
||||
$usedModels[] = $rule['type'];
|
||||
}
|
||||
}
|
||||
$this->getNestedModels($model, $usedModels);
|
||||
}
|
||||
|
||||
foreach ($this->models as $model) {
|
||||
@@ -425,19 +427,19 @@ class Swagger2 extends Format
|
||||
'type' => 'object',
|
||||
];
|
||||
|
||||
if(!empty($rules)) {
|
||||
if (!empty($rules)) {
|
||||
$output['definitions'][$model->getType()]['properties'] = [];
|
||||
}
|
||||
|
||||
if($model->isAny()) {
|
||||
if ($model->isAny()) {
|
||||
$output['definitions'][$model->getType()]['additionalProperties'] = true;
|
||||
}
|
||||
|
||||
if(!empty($required)) {
|
||||
if (!empty($required)) {
|
||||
$output['definitions'][$model->getType()]['required'] = $required;
|
||||
}
|
||||
|
||||
foreach($model->getRules() as $name => $rule) {
|
||||
foreach ($model->getRules() as $name => $rule) {
|
||||
$type = '';
|
||||
$format = null;
|
||||
$items = null;
|
||||
@@ -474,24 +476,20 @@ class Swagger2 extends Format
|
||||
$type = 'object';
|
||||
$rule['type'] = ($rule['type']) ?: 'none';
|
||||
|
||||
if(\is_array($rule['type'])) {
|
||||
if($rule['array']) {
|
||||
if (\is_array($rule['type'])) {
|
||||
if ($rule['array']) {
|
||||
$items = [
|
||||
'x-anyOf' => \array_map(function($type) {
|
||||
return ['$ref' => '#/definitions/'.$type];
|
||||
}, $rule['type'])
|
||||
'x-anyOf' => \array_map(fn ($type) => ['$ref' => '#/definitions/' . $type], $rule['type'])
|
||||
];
|
||||
} else {
|
||||
$items = [
|
||||
'x-oneOf' => \array_map(function($type) {
|
||||
return ['$ref' => '#/definitions/'.$type];
|
||||
}, $rule['type'])
|
||||
'x-oneOf' => \array_map(fn ($type) => ['$ref' => '#/definitions/' . $type], $rule['type'])
|
||||
];
|
||||
}
|
||||
} else {
|
||||
$items = [
|
||||
'type' => $type,
|
||||
'$ref' => '#/definitions/'.$rule['type'],
|
||||
'$ref' => '#/definitions/' . $rule['type'],
|
||||
];
|
||||
}
|
||||
break;
|
||||
@@ -507,7 +505,7 @@ class Swagger2 extends Format
|
||||
continue;
|
||||
}
|
||||
|
||||
if($rule['array']) {
|
||||
if ($rule['array']) {
|
||||
$output['definitions'][$model->getType()]['properties'][$name] = [
|
||||
'type' => 'array',
|
||||
'description' => $rule['description'] ?? '',
|
||||
@@ -517,10 +515,9 @@ class Swagger2 extends Format
|
||||
'x-example' => $rule['example'] ?? null,
|
||||
];
|
||||
|
||||
if($format) {
|
||||
if ($format) {
|
||||
$output['definitions'][$model->getType()]['properties'][$name]['items']['format'] = $format;
|
||||
}
|
||||
|
||||
} else {
|
||||
$output['definitions'][$model->getType()]['properties'][$name] = [
|
||||
'type' => $type,
|
||||
@@ -528,12 +525,11 @@ class Swagger2 extends Format
|
||||
'x-example' => $rule['example'] ?? null,
|
||||
];
|
||||
|
||||
if($format) {
|
||||
if ($format) {
|
||||
$output['definitions'][$model->getType()]['properties'][$name]['format'] = $format;
|
||||
}
|
||||
|
||||
}
|
||||
if($items) {
|
||||
if ($items) {
|
||||
$output['definitions'][$model->getType()]['properties'][$name]['items'] = $items;
|
||||
}
|
||||
if (!in_array($name, $required)) {
|
||||
|
||||
@@ -4,14 +4,8 @@ namespace Appwrite\Specification;
|
||||
|
||||
class Specification
|
||||
{
|
||||
/**
|
||||
* @var Format
|
||||
*/
|
||||
protected $format;
|
||||
protected Format $format;
|
||||
|
||||
/**
|
||||
* @param Format $format
|
||||
*/
|
||||
public function __construct(Format $format)
|
||||
{
|
||||
$this->format = $format;
|
||||
@@ -24,7 +18,7 @@ class Specification
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName():string
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->format->getName();
|
||||
}
|
||||
@@ -40,4 +34,4 @@ class Specification
|
||||
{
|
||||
return $this->format->parse();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,20 +113,24 @@ class Stats
|
||||
$this->statsd->count('network.all' . $tags, $networkRequestSize + $networkResponseSize);
|
||||
|
||||
$dbMetrics = [
|
||||
'database.collections.create',
|
||||
'database.collections.read',
|
||||
'database.collections.update',
|
||||
'database.collections.delete',
|
||||
'database.documents.create',
|
||||
'database.documents.read',
|
||||
'database.documents.update',
|
||||
'database.documents.delete',
|
||||
'databases.create',
|
||||
'databases.read',
|
||||
'databases.update',
|
||||
'databases.delete',
|
||||
'databases.collections.create',
|
||||
'databases.collections.read',
|
||||
'databases.collections.update',
|
||||
'databases.collections.delete',
|
||||
'databases.documents.create',
|
||||
'databases.documents.read',
|
||||
'databases.documents.update',
|
||||
'databases.documents.delete',
|
||||
];
|
||||
|
||||
foreach ($dbMetrics as $metric) {
|
||||
$value = $this->params[$metric] ?? 0;
|
||||
if ($value >= 1) {
|
||||
$tags = ",projectId={$projectId},collectionId=" . ($this->params['collectionId'] ?? '');
|
||||
$tags = ",projectId={$projectId},collectionId=" . ($this->params['collectionId'] ?? '') . ",databaseId=" . ($this->params['databaseId'] ?? '');
|
||||
$this->statsd->increment($metric . $tags);
|
||||
}
|
||||
}
|
||||
@@ -173,7 +177,7 @@ class Stats
|
||||
foreach ($sessionsMetrics as $metric) {
|
||||
$value = $this->params[$metric] ?? 0;
|
||||
if ($value >= 1) {
|
||||
$tags = ",projectId={$projectId},provider=". ($this->params['provider'] ?? '');
|
||||
$tags = ",projectId={$projectId},provider=" . ($this->params['provider'] ?? '');
|
||||
$this->statsd->count($metric . $tags, $value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,351 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Stats;
|
||||
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use InfluxDB\Database as InfluxDatabase;
|
||||
use DateTime;
|
||||
|
||||
class Usage
|
||||
{
|
||||
protected InfluxDatabase $influxDB;
|
||||
protected Database $database;
|
||||
protected $errorHandler;
|
||||
private array $latestTime = [];
|
||||
|
||||
// all the mertics that we are collecting
|
||||
protected array $metrics = [
|
||||
'requests' => [
|
||||
'table' => 'appwrite_usage_requests_all',
|
||||
],
|
||||
'network' => [
|
||||
'table' => 'appwrite_usage_network_all',
|
||||
],
|
||||
'executions' => [
|
||||
'table' => 'appwrite_usage_executions_all',
|
||||
],
|
||||
'databases.create' => [
|
||||
'table' => 'appwrite_usage_databases_create',
|
||||
],
|
||||
'databases.read' => [
|
||||
'table' => 'appwrite_usage_databases_read',
|
||||
],
|
||||
'databases.update' => [
|
||||
'table' => 'appwrite_usage_databases_update',
|
||||
],
|
||||
'databases.delete' => [
|
||||
'table' => 'appwrite_usage_databases_delete',
|
||||
],
|
||||
'databases.collections.create' => [
|
||||
'table' => 'appwrite_usage_databases_collections_create',
|
||||
],
|
||||
'databases.collections.read' => [
|
||||
'table' => 'appwrite_usage_databases_collections_read',
|
||||
],
|
||||
'databases.collections.update' => [
|
||||
'table' => 'appwrite_usage_databases_collections_update',
|
||||
],
|
||||
'databases.collections.delete' => [
|
||||
'table' => 'appwrite_usage_databases_collections_delete',
|
||||
],
|
||||
'databases.documents.create' => [
|
||||
'table' => 'appwrite_usage_databases_documents_create',
|
||||
],
|
||||
'databases.documents.read' => [
|
||||
'table' => 'appwrite_usage_databases_documents_read',
|
||||
],
|
||||
'databases.documents.update' => [
|
||||
'table' => 'appwrite_usage_databases_documents_update',
|
||||
],
|
||||
'databases.documents.delete' => [
|
||||
'table' => 'appwrite_usage_databases_documents_delete',
|
||||
],
|
||||
'databases.databaseId.collections.create' => [
|
||||
'table' => 'appwrite_usage_databases_collections_create',
|
||||
'groupBy' => ['databaseId'],
|
||||
],
|
||||
'databases.databaseId.collections.read' => [
|
||||
'table' => 'appwrite_usage_databases_collections_read',
|
||||
'groupBy' => ['databaseId'],
|
||||
],
|
||||
'databases.databaseId.collections.update' => [
|
||||
'table' => 'appwrite_usage_databases_collections_update',
|
||||
'groupBy' => ['databaseId'],
|
||||
],
|
||||
'databases.databaseId.collections.delete' => [
|
||||
'table' => 'appwrite_usage_databases_collections_delete',
|
||||
'groupBy' => ['databaseId'],
|
||||
],
|
||||
'databases.databaseId.documents.create' => [
|
||||
'table' => 'appwrite_usage_databases_documents_create',
|
||||
'groupBy' => ['databaseId'],
|
||||
],
|
||||
'databases.databaseId.documents.read' => [
|
||||
'table' => 'appwrite_usage_databases_documents_read',
|
||||
'groupBy' => ['databaseId'],
|
||||
],
|
||||
'database.databaseId.documents.update' => [
|
||||
'table' => 'appwrite_usage_databases_documents_update',
|
||||
'groupBy' => ['databaseId'],
|
||||
],
|
||||
'databases.databaseId.documents.delete' => [
|
||||
'table' => 'appwrite_usage_databases_documents_delete',
|
||||
'groupBy' => ['databaseId'],
|
||||
],
|
||||
'databases.databaseId.collections.collectionId.documents.create' => [
|
||||
'table' => 'appwrite_usage_databases_documents_create',
|
||||
'groupBy' => ['collectionId'],
|
||||
],
|
||||
'databases.databaseId.collections.collectionId.documents.read' => [
|
||||
'table' => 'appwrite_usage_databases_documents_read',
|
||||
'groupBy' => ['databaseId', 'collectionId'],
|
||||
],
|
||||
'databases.databaseId.collections.collectionId.documents.update' => [
|
||||
'table' => 'appwrite_usage_databases_documents_update',
|
||||
'groupBy' => ['databaseId', 'collectionId'],
|
||||
],
|
||||
'databases.databaseId.collections.collectionId.documents.delete' => [
|
||||
'table' => 'appwrite_usage_databases_documents_delete',
|
||||
'groupBy' => ['databaseId', 'collectionId'],
|
||||
],
|
||||
'storage.buckets.create' => [
|
||||
'table' => 'appwrite_usage_storage_buckets_create',
|
||||
],
|
||||
'storage.buckets.read' => [
|
||||
'table' => 'appwrite_usage_storage_buckets_read',
|
||||
],
|
||||
'storage.buckets.update' => [
|
||||
'table' => 'appwrite_usage_storage_buckets_update',
|
||||
],
|
||||
'storage.buckets.delete' => [
|
||||
'table' => 'appwrite_usage_storage_buckets_delete',
|
||||
],
|
||||
'storage.files.create' => [
|
||||
'table' => 'appwrite_usage_storage_files_create',
|
||||
],
|
||||
'storage.files.read' => [
|
||||
'table' => 'appwrite_usage_storage_files_read',
|
||||
],
|
||||
'storage.files.update' => [
|
||||
'table' => 'appwrite_usage_storage_files_update',
|
||||
],
|
||||
'storage.files.delete' => [
|
||||
'table' => 'appwrite_usage_storage_files_delete',
|
||||
],
|
||||
'storage.buckets.bucketId.files.create' => [
|
||||
'table' => 'appwrite_usage_storage_files_create',
|
||||
'groupBy' => ['bucketId'],
|
||||
],
|
||||
'storage.buckets.bucketId.files.read' => [
|
||||
'table' => 'appwrite_usage_storage_files_read',
|
||||
'groupBy' => ['bucketId'],
|
||||
],
|
||||
'storage.buckets.bucketId.files.update' => [
|
||||
'table' => 'appwrite_usage_storage_files_update',
|
||||
'groupBy' => ['bucketId'],
|
||||
],
|
||||
'storage.buckets.bucketId.files.delete' => [
|
||||
'table' => 'appwrite_usage_storage_files_delete',
|
||||
'groupBy' => ['bucketId'],
|
||||
],
|
||||
'users.create' => [
|
||||
'table' => 'appwrite_usage_users_create',
|
||||
],
|
||||
'users.read' => [
|
||||
'table' => 'appwrite_usage_users_read',
|
||||
],
|
||||
'users.update' => [
|
||||
'table' => 'appwrite_usage_users_update',
|
||||
],
|
||||
'users.delete' => [
|
||||
'table' => 'appwrite_usage_users_delete',
|
||||
],
|
||||
'users.sessions.create' => [
|
||||
'table' => 'appwrite_usage_users_sessions_create',
|
||||
],
|
||||
'users.sessions.provider.create' => [
|
||||
'table' => 'appwrite_usage_users_sessions_create',
|
||||
'groupBy' => ['provider'],
|
||||
],
|
||||
'users.sessions.delete' => [
|
||||
'table' => 'appwrite_usage_users_sessions_delete',
|
||||
],
|
||||
'functions.functionId.executions' => [
|
||||
'table' => 'appwrite_usage_executions_all',
|
||||
'groupBy' => ['functionId'],
|
||||
],
|
||||
'functions.functionId.compute' => [
|
||||
'table' => 'appwrite_usage_executions_time',
|
||||
'groupBy' => ['functionId'],
|
||||
],
|
||||
'functions.functionId.failures' => [
|
||||
'table' => 'appwrite_usage_executions_all',
|
||||
'groupBy' => ['functionId'],
|
||||
'filters' => [
|
||||
'functionStatus' => 'failed',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
protected array $periods = [
|
||||
[
|
||||
'key' => '30m',
|
||||
'multiplier' => 1800,
|
||||
'startTime' => '-24 hours',
|
||||
],
|
||||
[
|
||||
'key' => '1d',
|
||||
'multiplier' => 86400,
|
||||
'startTime' => '-90 days',
|
||||
],
|
||||
];
|
||||
|
||||
public function __construct(Database $database, InfluxDatabase $influxDB, callable $errorHandler = null)
|
||||
{
|
||||
$this->database = $database;
|
||||
$this->influxDB = $influxDB;
|
||||
$this->errorHandler = $errorHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create or Update Mertic
|
||||
* Create or update each metric in the stats collection for the given project
|
||||
*
|
||||
* @param string $projectId
|
||||
* @param int $time
|
||||
* @param string $period
|
||||
* @param string $metric
|
||||
* @param int $value
|
||||
* @param int $type
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function createOrUpdateMetric(string $projectId, int $time, string $period, string $metric, int $value, int $type): void
|
||||
{
|
||||
$id = \md5("{$time}_{$period}_{$metric}");
|
||||
$this->database->setNamespace('_console');
|
||||
$project = $this->database->getDocument('projects', $projectId);
|
||||
$this->database->setNamespace('_' . $project->getInternalId());
|
||||
|
||||
try {
|
||||
$document = $this->database->getDocument('stats', $id);
|
||||
if ($document->isEmpty()) {
|
||||
$this->database->createDocument('stats', new Document([
|
||||
'$id' => $id,
|
||||
'period' => $period,
|
||||
'time' => $time,
|
||||
'metric' => $metric,
|
||||
'value' => $value,
|
||||
'type' => $type,
|
||||
]));
|
||||
} else {
|
||||
$this->database->updateDocument(
|
||||
'stats',
|
||||
$document->getId(),
|
||||
$document->setAttribute('value', $value)
|
||||
);
|
||||
}
|
||||
$this->latestTime[$metric][$period] = $time;
|
||||
} catch (\Exception $e) { // if projects are deleted this might fail
|
||||
if (is_callable($this->errorHandler)) {
|
||||
call_user_func($this->errorHandler, $e, "sync_project_{$projectId}_metric_{$metric}");
|
||||
} else {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sync From InfluxDB
|
||||
* Sync stats from influxDB to stats collection in the Appwrite database
|
||||
*
|
||||
* @param string $metric
|
||||
* @param array $options
|
||||
* @param array $period
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function syncFromInfluxDB(string $metric, array $options, array $period): void
|
||||
{
|
||||
$start = DateTime::createFromFormat('U', \strtotime($period['startTime']))->format(DateTime::RFC3339);
|
||||
if (!empty($this->latestTime[$metric][$period['key']])) {
|
||||
$start = DateTime::createFromFormat('U', $this->latestTime[$metric][$period['key']])->format(DateTime::RFC3339);
|
||||
}
|
||||
$end = DateTime::createFromFormat('U', \strtotime('now'))->format(DateTime::RFC3339);
|
||||
|
||||
$table = $options['table']; //Which influxdb table to query for this metric
|
||||
$groupBy = empty($options['groupBy']) ? '' : ', ' . implode(', ', array_map(fn($groupBy) => '"' . $groupBy . '" ', $options['groupBy'])); //Some sub level metrics may be grouped by other tags like collectionId, bucketId, etc
|
||||
|
||||
$filters = $options['filters'] ?? []; // Some metrics might have additional filters, like function's status
|
||||
if (!empty($filters)) {
|
||||
$filters = ' AND ' . implode(' AND ', array_map(fn ($filter, $value) => "\"{$filter}\"='{$value}'", array_keys($filters), array_values($filters)));
|
||||
} else {
|
||||
$filters = '';
|
||||
}
|
||||
|
||||
$query = "SELECT sum(value) AS \"value\" ";
|
||||
$query .= "FROM \"{$table}\" ";
|
||||
$query .= "WHERE \"time\" > '{$start}' ";
|
||||
$query .= "AND \"time\" < '{$end}' ";
|
||||
$query .= "AND \"metric_type\"='counter' {$filters} ";
|
||||
$query .= "GROUP BY time({$period['key']}), \"projectId\" {$groupBy} ";
|
||||
$query .= "FILL(null)";
|
||||
|
||||
$result = $this->influxDB->query($query);
|
||||
|
||||
$points = $result->getPoints();
|
||||
foreach ($points as $point) {
|
||||
$projectId = $point['projectId'];
|
||||
|
||||
if (!empty($projectId) && $projectId !== 'console') {
|
||||
$metricUpdated = $metric;
|
||||
|
||||
if (!empty($groupBy)) {
|
||||
foreach ($options['groupBy'] as $groupBy) {
|
||||
$groupedBy = $point[$groupBy] ?? '';
|
||||
if (empty($groupedBy)) {
|
||||
continue;
|
||||
}
|
||||
$metricUpdated = str_replace($groupBy, $groupedBy, $metric);
|
||||
}
|
||||
}
|
||||
|
||||
$time = \strtotime($point['time']);
|
||||
$value = (!empty($point['value'])) ? $point['value'] : 0;
|
||||
|
||||
$this->createOrUpdateMetric(
|
||||
$projectId,
|
||||
$time,
|
||||
$period['key'],
|
||||
$metricUpdated,
|
||||
$value,
|
||||
0
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect Stats
|
||||
* Collect all the stats from Influd DB to Database
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function collect(): void
|
||||
{
|
||||
foreach ($this->metrics as $metric => $options) { //for each metrics
|
||||
foreach ($this->periods as $period) { // aggregate data for each period
|
||||
try {
|
||||
$this->syncFromInfluxDB($metric, $options, $period);
|
||||
} catch (\Exception $e) {
|
||||
if (is_callable($this->errorHandler)) {
|
||||
call_user_func($this->errorHandler, $e);
|
||||
} else {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,277 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Stats;
|
||||
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
|
||||
class UsageDB extends Usage
|
||||
{
|
||||
public function __construct(Database $database, callable $errorHandler = null)
|
||||
{
|
||||
$this->database = $database;
|
||||
$this->errorHandler = $errorHandler;
|
||||
}
|
||||
/**
|
||||
* Create or Update Mertic
|
||||
* Create or update each metric in the stats collection for the given project
|
||||
*
|
||||
* @param string $projectId
|
||||
* @param string $metric
|
||||
* @param int $value
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function createOrUpdateMetric(string $projectId, string $metric, int $value): void
|
||||
{
|
||||
foreach ($this->periods as $options) {
|
||||
$period = $options['key'];
|
||||
$time = (int) (floor(time() / $options['multiplier']) * $options['multiplier']);
|
||||
$id = \md5("{$time}_{$period}_{$metric}");
|
||||
$this->database->setNamespace('_' . $projectId);
|
||||
|
||||
try {
|
||||
$document = $this->database->getDocument('stats', $id);
|
||||
if ($document->isEmpty()) {
|
||||
$this->database->createDocument('stats', new Document([
|
||||
'$id' => $id,
|
||||
'period' => $period,
|
||||
'time' => $time,
|
||||
'metric' => $metric,
|
||||
'value' => $value,
|
||||
'type' => 1,
|
||||
]));
|
||||
} else {
|
||||
$this->database->updateDocument(
|
||||
'stats',
|
||||
$document->getId(),
|
||||
$document->setAttribute('value', $value)
|
||||
);
|
||||
}
|
||||
} catch (\Exception$e) { // if projects are deleted this might fail
|
||||
if (is_callable($this->errorHandler)) {
|
||||
call_user_func($this->errorHandler, $e, "sync_project_{$projectId}_metric_{$metric}");
|
||||
} else {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Foreach Document
|
||||
* Call provided callback for each document in the collection
|
||||
*
|
||||
* @param string $projectId
|
||||
* @param string $collection
|
||||
* @param array $queries
|
||||
* @param callable $callback
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function foreachDocument(string $projectId, string $collection, array $queries, callable $callback): void
|
||||
{
|
||||
$limit = 50;
|
||||
$results = [];
|
||||
$sum = $limit;
|
||||
$latestDocument = null;
|
||||
$this->database->setNamespace('_' . $projectId);
|
||||
|
||||
while ($sum === $limit) {
|
||||
try {
|
||||
$results = $this->database->find($collection, $queries, $limit, cursor:$latestDocument);
|
||||
} catch (\Exception $e) {
|
||||
if (is_callable($this->errorHandler)) {
|
||||
call_user_func($this->errorHandler, $e, "fetch_documents_project_{$projectId}_collection_{$collection}");
|
||||
return;
|
||||
} else {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
if (empty($results)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$sum = count($results);
|
||||
|
||||
foreach ($results as $document) {
|
||||
if (is_callable($callback)) {
|
||||
$callback($document);
|
||||
}
|
||||
}
|
||||
$latestDocument = $results[array_key_last($results)];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sum
|
||||
* Calculate sum of a attribute of documents in collection
|
||||
*
|
||||
* @param string $projectId
|
||||
* @param string $collection
|
||||
* @param string $attribute
|
||||
* @param string $metric
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
private function sum(string $projectId, string $collection, string $attribute, string $metric): int
|
||||
{
|
||||
$this->database->setNamespace('_' . $projectId);
|
||||
|
||||
try {
|
||||
$sum = (int) $this->database->sum($collection, $attribute);
|
||||
$this->createOrUpdateMetric($projectId, $metric, $sum);
|
||||
return $sum;
|
||||
} catch (\Exception $e) {
|
||||
if (is_callable($this->errorHandler)) {
|
||||
call_user_func($this->errorHandler, $e, "fetch_sum_project_{$projectId}_collection_{$collection}");
|
||||
} else {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Count
|
||||
* Count number of documents in collection
|
||||
*
|
||||
* @param string $projectId
|
||||
* @param string $collection
|
||||
* @param string $metric
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
private function count(string $projectId, string $collection, string $metric): int
|
||||
{
|
||||
$this->database->setNamespace('_' . $projectId);
|
||||
|
||||
try {
|
||||
$count = $this->database->count($collection);
|
||||
$this->createOrUpdateMetric($projectId, $metric, $count);
|
||||
return $count;
|
||||
} catch (\Exception $e) {
|
||||
if (is_callable($this->errorHandler)) {
|
||||
call_user_func($this->errorHandler, $e, "fetch_count_project_{$projectId}_collection_{$collection}");
|
||||
} else {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deployments Total
|
||||
* Total sum of storage used by deployments
|
||||
*
|
||||
* @param string $projectId
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
private function deploymentsTotal(string $projectId): int
|
||||
{
|
||||
return $this->sum($projectId, 'deployments', 'size', 'stroage.deployments.total');
|
||||
}
|
||||
|
||||
/**
|
||||
* Users Stats
|
||||
* Metric: users.count
|
||||
*
|
||||
* @param string $projectId
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function usersStats(string $projectId): void
|
||||
{
|
||||
$this->count($projectId, 'users', 'users.count');
|
||||
}
|
||||
|
||||
/**
|
||||
* Storage Stats
|
||||
* Metrics: storage.total, storage.files.total, storage.buckets.{bucketId}.files.total,
|
||||
* storage.buckets.count, storage.files.count, storage.buckets.{bucketId}.files.count
|
||||
*
|
||||
* @param string $projectId
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function storageStats(string $projectId): void
|
||||
{
|
||||
$deploymentsTotal = $this->deploymentsTotal($projectId);
|
||||
|
||||
$projectFilesTotal = 0;
|
||||
$projectFilesCount = 0;
|
||||
|
||||
$metric = 'storage.buckets.count';
|
||||
$this->count($projectId, 'buckets', $metric);
|
||||
|
||||
$this->foreachDocument($projectId, 'buckets', [], function ($bucket) use (&$projectFilesCount, &$projectFilesTotal, $projectId,) {
|
||||
$metric = "storage.buckets.{$bucket->getId()}.files.count";
|
||||
|
||||
$count = $this->count($projectId, 'bucket_' . $bucket->getInternalId(), $metric);
|
||||
$projectFilesCount += $count;
|
||||
|
||||
$metric = "storage.buckets.{$bucket->getId()}.files.total";
|
||||
$sum = $this->sum($projectId, 'bucket_' . $bucket->getInternalId(), 'sizeOriginal', $metric);
|
||||
$projectFilesTotal += $sum;
|
||||
});
|
||||
|
||||
$this->createOrUpdateMetric($projectId, 'storage.files.count', $projectFilesCount);
|
||||
$this->createOrUpdateMetric($projectId, 'storage.files.total', $projectFilesTotal);
|
||||
|
||||
$this->createOrUpdateMetric($projectId, 'storage.total', $projectFilesTotal + $deploymentsTotal);
|
||||
}
|
||||
|
||||
/**
|
||||
* Database Stats
|
||||
* Collect all database stats
|
||||
* Metrics: database.collections.count, database.collections.{collectionId}.documents.count,
|
||||
* database.documents.count
|
||||
*
|
||||
* @param string $projectId
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function databaseStats(string $projectId): void
|
||||
{
|
||||
$projectDocumentsCount = 0;
|
||||
$projectCollectionsCount = 0;
|
||||
|
||||
$this->count($projectId, 'databases', 'databases.count');
|
||||
|
||||
$this->foreachDocument($projectId, 'databases', [], function ($database) use (&$projectDocumentsCount, &$projectCollectionsCount, $projectId) {
|
||||
$metric = "databases.{$database->getId()}.collections.count";
|
||||
$count = $this->count($projectId, 'database_' . $database->getInternalId(), $metric);
|
||||
$projectCollectionsCount += $count;
|
||||
$databaseDocumentsCount = 0;
|
||||
|
||||
$this->foreachDocument($projectId, 'database_' . $database->getInternalId(), [], function ($collection) use (&$projectDocumentsCount, &$databaseDocumentsCount, $projectId, $database) {
|
||||
$metric = "databases.{$database->getId()}.collections.{$collection->getId()}.documents.count";
|
||||
|
||||
$count = $this->count($projectId, 'database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $metric);
|
||||
$projectDocumentsCount += $count;
|
||||
$databaseDocumentsCount += $count;
|
||||
});
|
||||
|
||||
$this->createOrUpdateMetric($projectId, "databases.{$database->getId()}.documents.count", $databaseDocumentsCount);
|
||||
});
|
||||
|
||||
$this->createOrUpdateMetric($projectId, 'databases.collections.count', $projectCollectionsCount);
|
||||
$this->createOrUpdateMetric($projectId, 'databases.documents.count', $projectDocumentsCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect Stats
|
||||
* Collect all database related stats
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function collect(): void
|
||||
{
|
||||
$this->foreachDocument('console', 'projects', [], function (Document $project) {
|
||||
$projectId = $project->getInternalId();
|
||||
|
||||
$this->usersStats($projectId);
|
||||
$this->databaseStats($projectId);
|
||||
$this->storageStats($projectId);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -74,7 +74,7 @@ class Template extends View
|
||||
} elseif (!empty($this->content)) {
|
||||
$template = $this->print($this->content, self::FILTER_NL2P);
|
||||
} else {
|
||||
throw new Exception('"'.$this->path.'" template is not readable or not found');
|
||||
throw new Exception('"' . $this->path . '" template is not readable or not found');
|
||||
}
|
||||
|
||||
// First replace the variables inside the params. Then replace the variables in the template
|
||||
@@ -109,20 +109,20 @@ class Template extends View
|
||||
*/
|
||||
public static function unParseURL(array $url)
|
||||
{
|
||||
$scheme = isset($url['scheme']) ? $url['scheme'].'://' : '';
|
||||
$scheme = isset($url['scheme']) ? $url['scheme'] . '://' : '';
|
||||
$host = isset($url['host']) ? $url['host'] : '';
|
||||
$port = isset($url['port']) ? ':'.$url['port'] : '';
|
||||
$port = isset($url['port']) ? ':' . $url['port'] : '';
|
||||
|
||||
$user = isset($url['user']) ? $url['user'] : '';
|
||||
$pass = isset($url['pass']) ? ':'.$url['pass'] : '';
|
||||
$pass = isset($url['pass']) ? ':' . $url['pass'] : '';
|
||||
$pass = ($user || $pass) ? "$pass@" : '';
|
||||
|
||||
$path = isset($url['path']) ? $url['path'] : '';
|
||||
$query = isset($url['query']) && !empty($url['query']) ? '?'.$url['query'] : '';
|
||||
$query = isset($url['query']) && !empty($url['query']) ? '?' . $url['query'] : '';
|
||||
|
||||
$fragment = isset($url['fragment']) ? '#'.$url['fragment'] : '';
|
||||
$fragment = isset($url['fragment']) ? '#' . $url['fragment'] : '';
|
||||
|
||||
return $scheme.$user.$pass.$host.$port.$path.$query.$fragment;
|
||||
return $scheme . $user . $pass . $host . $port . $path . $query . $fragment;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -42,28 +42,28 @@ class URL
|
||||
public static function unparse(array $url, array $ommit = []): string
|
||||
{
|
||||
if (isset($url['path']) && \mb_substr($url['path'], 0, 1) !== '/') {
|
||||
$url['path'] = '/'.$url['path'];
|
||||
$url['path'] = '/' . $url['path'];
|
||||
}
|
||||
|
||||
$parts = [];
|
||||
|
||||
$parts['scheme'] = isset($url['scheme']) ? $url['scheme'].'://' : '';
|
||||
$parts['scheme'] = isset($url['scheme']) ? $url['scheme'] . '://' : '';
|
||||
|
||||
$parts['host'] = isset($url['host']) ? $url['host'] : '';
|
||||
|
||||
$parts['port'] = isset($url['port']) ? ':'.$url['port'] : '';
|
||||
$parts['port'] = isset($url['port']) ? ':' . $url['port'] : '';
|
||||
|
||||
$parts['user'] = isset($url['user']) ? $url['user'] : '';
|
||||
|
||||
$parts['pass'] = isset($url['pass']) ? ':'.$url['pass'] : '';
|
||||
$parts['pass'] = isset($url['pass']) ? ':' . $url['pass'] : '';
|
||||
|
||||
$parts['pass'] = ($parts['user'] || $parts['pass']) ? $parts['pass'].'@' : '';
|
||||
$parts['pass'] = ($parts['user'] || $parts['pass']) ? $parts['pass'] . '@' : '';
|
||||
|
||||
$parts['path'] = isset($url['path']) ? $url['path'] : '';
|
||||
|
||||
$parts['query'] = isset($url['query']) && !empty($url['query']) ? '?'.$url['query'] : '';
|
||||
$parts['query'] = isset($url['query']) && !empty($url['query']) ? '?' . $url['query'] : '';
|
||||
|
||||
$parts['fragment'] = isset($url['fragment']) ? '#'.$url['fragment'] : '';
|
||||
$parts['fragment'] = isset($url['fragment']) ? '#' . $url['fragment'] : '';
|
||||
|
||||
if ($ommit) {
|
||||
foreach ($ommit as $key) {
|
||||
@@ -73,7 +73,7 @@ class URL
|
||||
}
|
||||
}
|
||||
|
||||
return $parts['scheme'].$parts['user'].$parts['pass'].$parts['host'].$parts['port'].$parts['path'].$parts['query'].$parts['fragment'];
|
||||
return $parts['scheme'] . $parts['user'] . $parts['pass'] . $parts['host'] . $parts['port'] . $parts['path'] . $parts['query'] . $parts['fragment'];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Utopia\Database\Validator;
|
||||
|
||||
use Utopia\Database\Validator\Key;
|
||||
|
||||
class CustomId extends Key {
|
||||
class CustomId extends Key
|
||||
{
|
||||
/**
|
||||
* Is valid.
|
||||
*
|
||||
@@ -18,4 +20,4 @@ class CustomId extends Key {
|
||||
|
||||
return $value == 'unique()' || parent::isValid($value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Utopia\Database\Validator;
|
||||
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Validator\OrderAttributes as ValidatorOrderAttributes;
|
||||
|
||||
class OrderAttributes extends ValidatorOrderAttributes
|
||||
{
|
||||
/**
|
||||
* Expression constructor
|
||||
*
|
||||
* @param Document[] $attributes
|
||||
* @param Document[] $indexes
|
||||
* @param bool $strict
|
||||
*/
|
||||
public function __construct($attributes, $indexes, $strict)
|
||||
{
|
||||
// Remove failed/stuck/processing indexes
|
||||
$indexes = \array_filter($indexes, function ($index) {
|
||||
return $index->getAttribute('status') === 'available';
|
||||
});
|
||||
|
||||
parent::__construct($attributes, $indexes, $strict);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Utopia\Database\Validator;
|
||||
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Validator\Queries as ValidatorQueries;
|
||||
|
||||
class Queries extends ValidatorQueries
|
||||
{
|
||||
/**
|
||||
* Expression constructor
|
||||
*
|
||||
* @param Document[] $attributes
|
||||
* @param Document[] $indexes
|
||||
* @param bool $strict
|
||||
*/
|
||||
public function __construct($attributes, $indexes, $strict)
|
||||
{
|
||||
// Remove failed/stuck/processing indexes
|
||||
$indexes = \array_filter($indexes, function ($index) {
|
||||
return $index->getAttribute('status') === 'available';
|
||||
});
|
||||
|
||||
parent::__construct($attributes, $indexes, $strict);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace Appwrite\Utopia;
|
||||
|
||||
use Appwrite\Utopia\Request\Filter;
|
||||
@@ -39,7 +38,7 @@ class Request extends UtopiaRequest
|
||||
{
|
||||
$requestParameters = [];
|
||||
|
||||
switch($this->getMethod()) {
|
||||
switch ($this->getMethod()) {
|
||||
case self::METHOD_GET:
|
||||
$requestParameters = (!empty($this->swoole->get)) ? $this->swoole->get : [];
|
||||
break;
|
||||
@@ -125,4 +124,4 @@ class Request extends UtopiaRequest
|
||||
{
|
||||
return self::$route != null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ namespace Appwrite\Utopia\Request;
|
||||
|
||||
abstract class Filter
|
||||
{
|
||||
|
||||
/**
|
||||
* Parse params to another format.
|
||||
*
|
||||
|
||||
@@ -93,9 +93,15 @@ class V12 extends Filter
|
||||
|
||||
protected function removeParentProperties(array $content): array
|
||||
{
|
||||
if (isset($content['parentDocument'])) unset($content['parentDocument']);
|
||||
if (isset($content['parentProperty'])) unset($content['parentProperty']);
|
||||
if (isset($content['parentPropertyType'])) unset($content['parentPropertyType']);
|
||||
if (isset($content['parentDocument'])) {
|
||||
unset($content['parentDocument']);
|
||||
}
|
||||
if (isset($content['parentProperty'])) {
|
||||
unset($content['parentProperty']);
|
||||
}
|
||||
if (isset($content['parentPropertyType'])) {
|
||||
unset($content['parentPropertyType']);
|
||||
}
|
||||
return $content;
|
||||
}
|
||||
|
||||
@@ -128,7 +134,7 @@ class V12 extends Filter
|
||||
{
|
||||
$queries = [];
|
||||
|
||||
if(!empty($content['filters'])) {
|
||||
if (!empty($content['filters'])) {
|
||||
foreach ($content['filters'] as $filter) {
|
||||
$operators = ['=' => 'equal', '!=' => 'notEqual', '>' => 'greater', '<' => 'lesser', '<=' => 'lesserEqual', '>=' => 'greaterEqual'];
|
||||
foreach ($operators as $operator => $operatorVerbose) {
|
||||
@@ -138,10 +144,10 @@ class V12 extends Filter
|
||||
}
|
||||
}
|
||||
|
||||
if(isset($usedOperator)) {
|
||||
if (isset($usedOperator)) {
|
||||
[ $attributeKey, $filterValue ] = \explode($usedOperator, $filter);
|
||||
|
||||
if($filterValue === 'true' || $filterValue === 'false') {
|
||||
if ($filterValue === 'true' || $filterValue === 'false') {
|
||||
// Let's keep it at true and false string, but without "" around
|
||||
// No action needed
|
||||
} else {
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Utopia\Request\Filters;
|
||||
|
||||
use Appwrite\Utopia\Request\Filter;
|
||||
use Appwrite\Migration\Version\V13 as MigrationV13;
|
||||
|
||||
class V14 extends Filter
|
||||
{
|
||||
// Convert 0.13 params format to 0.14 format
|
||||
public function parse(array $content, string $model): array
|
||||
{
|
||||
switch ($model) {
|
||||
case "functions.create":
|
||||
case "functions.update":
|
||||
case "projects.createWebhook":
|
||||
case "projects.updateWebhook":
|
||||
$content = $this->convertEvents($content);
|
||||
break;
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
private function convertEvents($content)
|
||||
{
|
||||
$migration = new MigrationV13();
|
||||
|
||||
$events = $content['events'] ?? [];
|
||||
$content['events'] = $migration->migrateEvents($events);
|
||||
|
||||
return $content;
|
||||
}
|
||||
}
|
||||
+107
-100
@@ -22,11 +22,11 @@ use Appwrite\Utopia\Response\Model\AttributeIP;
|
||||
use Appwrite\Utopia\Response\Model\AttributeURL;
|
||||
use Appwrite\Utopia\Response\Model\BaseList;
|
||||
use Appwrite\Utopia\Response\Model\Collection;
|
||||
use Appwrite\Utopia\Response\Model\Database;
|
||||
use Appwrite\Utopia\Response\Model\Continent;
|
||||
use Appwrite\Utopia\Response\Model\Country;
|
||||
use Appwrite\Utopia\Response\Model\Currency;
|
||||
use Appwrite\Utopia\Response\Model\Document as ModelDocument;
|
||||
use Appwrite\Utopia\Response\Model\DocumentList;
|
||||
use Appwrite\Utopia\Response\Model\Domain;
|
||||
use Appwrite\Utopia\Response\Model\Error;
|
||||
use Appwrite\Utopia\Response\Model\ErrorDev;
|
||||
@@ -65,6 +65,7 @@ use Appwrite\Utopia\Response\Model\Runtime;
|
||||
use Appwrite\Utopia\Response\Model\UsageBuckets;
|
||||
use Appwrite\Utopia\Response\Model\UsageCollection;
|
||||
use Appwrite\Utopia\Response\Model\UsageDatabase;
|
||||
use Appwrite\Utopia\Response\Model\UsageDatabases;
|
||||
use Appwrite\Utopia\Response\Model\UsageFunctions;
|
||||
use Appwrite\Utopia\Response\Model\UsageProject;
|
||||
use Appwrite\Utopia\Response\Model\UsageStorage;
|
||||
@@ -76,116 +77,119 @@ use Appwrite\Utopia\Response\Model\UsageUsers;
|
||||
class Response extends SwooleResponse
|
||||
{
|
||||
// General
|
||||
const MODEL_NONE = 'none';
|
||||
const MODEL_ANY = 'any';
|
||||
const MODEL_LOG = 'log';
|
||||
const MODEL_LOG_LIST = 'logList';
|
||||
const MODEL_ERROR = 'error';
|
||||
const MODEL_METRIC = 'metric';
|
||||
const MODEL_METRIC_LIST = 'metricList';
|
||||
const MODEL_ERROR_DEV = 'errorDev';
|
||||
const MODEL_BASE_LIST = 'baseList';
|
||||
const MODEL_USAGE_DATABASE = 'usageDatabase';
|
||||
const MODEL_USAGE_COLLECTION = 'usageCollection';
|
||||
const MODEL_USAGE_USERS = 'usageUsers';
|
||||
const MODEL_USAGE_BUCKETS = 'usageBuckets';
|
||||
const MODEL_USAGE_STORAGE = 'usageStorage';
|
||||
const MODEL_USAGE_FUNCTIONS = 'usageFunctions';
|
||||
const MODEL_USAGE_PROJECT = 'usageProject';
|
||||
|
||||
public const MODEL_NONE = 'none';
|
||||
public const MODEL_ANY = 'any';
|
||||
public const MODEL_LOG = 'log';
|
||||
public const MODEL_LOG_LIST = 'logList';
|
||||
public const MODEL_ERROR = 'error';
|
||||
public const MODEL_METRIC = 'metric';
|
||||
public const MODEL_METRIC_LIST = 'metricList';
|
||||
public const MODEL_ERROR_DEV = 'errorDev';
|
||||
public const MODEL_BASE_LIST = 'baseList';
|
||||
public const MODEL_USAGE_DATABASES = 'usageDatabases';
|
||||
public const MODEL_USAGE_DATABASE = 'usageDatabase';
|
||||
public const MODEL_USAGE_COLLECTION = 'usageCollection';
|
||||
public const MODEL_USAGE_USERS = 'usageUsers';
|
||||
public const MODEL_USAGE_BUCKETS = 'usageBuckets';
|
||||
public const MODEL_USAGE_STORAGE = 'usageStorage';
|
||||
public const MODEL_USAGE_FUNCTIONS = 'usageFunctions';
|
||||
public const MODEL_USAGE_PROJECT = 'usageProject';
|
||||
|
||||
// Database
|
||||
const MODEL_COLLECTION = 'collection';
|
||||
const MODEL_COLLECTION_LIST = 'collectionList';
|
||||
const MODEL_INDEX = 'index';
|
||||
const MODEL_INDEX_LIST = 'indexList';
|
||||
const MODEL_DOCUMENT = 'document';
|
||||
const MODEL_DOCUMENT_LIST = 'documentList';
|
||||
public const MODEL_DATABASE = 'database';
|
||||
public const MODEL_DATABASE_LIST = 'databaseList';
|
||||
public const MODEL_COLLECTION = 'collection';
|
||||
public const MODEL_COLLECTION_LIST = 'collectionList';
|
||||
public const MODEL_INDEX = 'index';
|
||||
public const MODEL_INDEX_LIST = 'indexList';
|
||||
public const MODEL_DOCUMENT = 'document';
|
||||
public const MODEL_DOCUMENT_LIST = 'documentList';
|
||||
|
||||
// Database Attributes
|
||||
const MODEL_ATTRIBUTE = 'attribute';
|
||||
const MODEL_ATTRIBUTE_LIST = 'attributeList';
|
||||
const MODEL_ATTRIBUTE_STRING = 'attributeString';
|
||||
const MODEL_ATTRIBUTE_INTEGER = 'attributeInteger';
|
||||
const MODEL_ATTRIBUTE_FLOAT = 'attributeFloat';
|
||||
const MODEL_ATTRIBUTE_BOOLEAN = 'attributeBoolean';
|
||||
const MODEL_ATTRIBUTE_EMAIL = 'attributeEmail';
|
||||
const MODEL_ATTRIBUTE_ENUM = 'attributeEnum';
|
||||
const MODEL_ATTRIBUTE_IP = 'attributeIp';
|
||||
const MODEL_ATTRIBUTE_URL= 'attributeUrl';
|
||||
public const MODEL_ATTRIBUTE = 'attribute';
|
||||
public const MODEL_ATTRIBUTE_LIST = 'attributeList';
|
||||
public const MODEL_ATTRIBUTE_STRING = 'attributeString';
|
||||
public const MODEL_ATTRIBUTE_INTEGER = 'attributeInteger';
|
||||
public const MODEL_ATTRIBUTE_FLOAT = 'attributeFloat';
|
||||
public const MODEL_ATTRIBUTE_BOOLEAN = 'attributeBoolean';
|
||||
public const MODEL_ATTRIBUTE_EMAIL = 'attributeEmail';
|
||||
public const MODEL_ATTRIBUTE_ENUM = 'attributeEnum';
|
||||
public const MODEL_ATTRIBUTE_IP = 'attributeIp';
|
||||
public const MODEL_ATTRIBUTE_URL = 'attributeUrl';
|
||||
|
||||
// Users
|
||||
const MODEL_USER = 'user';
|
||||
const MODEL_USER_LIST = 'userList';
|
||||
const MODEL_SESSION = 'session';
|
||||
const MODEL_SESSION_LIST = 'sessionList';
|
||||
const MODEL_TOKEN = 'token';
|
||||
const MODEL_JWT = 'jwt';
|
||||
const MODEL_PREFERENCES = 'preferences';
|
||||
|
||||
public const MODEL_USER = 'user';
|
||||
public const MODEL_USER_LIST = 'userList';
|
||||
public const MODEL_SESSION = 'session';
|
||||
public const MODEL_SESSION_LIST = 'sessionList';
|
||||
public const MODEL_TOKEN = 'token';
|
||||
public const MODEL_JWT = 'jwt';
|
||||
public const MODEL_PREFERENCES = 'preferences';
|
||||
|
||||
// Storage
|
||||
const MODEL_FILE = 'file';
|
||||
const MODEL_FILE_LIST = 'fileList';
|
||||
const MODEL_BUCKET = 'bucket';
|
||||
const MODEL_BUCKET_LIST = 'bucketList';
|
||||
public const MODEL_FILE = 'file';
|
||||
public const MODEL_FILE_LIST = 'fileList';
|
||||
public const MODEL_BUCKET = 'bucket';
|
||||
public const MODEL_BUCKET_LIST = 'bucketList';
|
||||
|
||||
// Locale
|
||||
const MODEL_LOCALE = 'locale';
|
||||
const MODEL_COUNTRY = 'country';
|
||||
const MODEL_COUNTRY_LIST = 'countryList';
|
||||
const MODEL_CONTINENT = 'continent';
|
||||
const MODEL_CONTINENT_LIST = 'continentList';
|
||||
const MODEL_CURRENCY = 'currency';
|
||||
const MODEL_CURRENCY_LIST = 'currencyList';
|
||||
const MODEL_LANGUAGE = 'language';
|
||||
const MODEL_LANGUAGE_LIST = 'languageList';
|
||||
const MODEL_PHONE = 'phone';
|
||||
const MODEL_PHONE_LIST = 'phoneList';
|
||||
public const MODEL_LOCALE = 'locale';
|
||||
public const MODEL_COUNTRY = 'country';
|
||||
public const MODEL_COUNTRY_LIST = 'countryList';
|
||||
public const MODEL_CONTINENT = 'continent';
|
||||
public const MODEL_CONTINENT_LIST = 'continentList';
|
||||
public const MODEL_CURRENCY = 'currency';
|
||||
public const MODEL_CURRENCY_LIST = 'currencyList';
|
||||
public const MODEL_LANGUAGE = 'language';
|
||||
public const MODEL_LANGUAGE_LIST = 'languageList';
|
||||
public const MODEL_PHONE = 'phone';
|
||||
public const MODEL_PHONE_LIST = 'phoneList';
|
||||
|
||||
// Teams
|
||||
const MODEL_TEAM = 'team';
|
||||
const MODEL_TEAM_LIST = 'teamList';
|
||||
const MODEL_MEMBERSHIP = 'membership';
|
||||
const MODEL_MEMBERSHIP_LIST = 'membershipList';
|
||||
public const MODEL_TEAM = 'team';
|
||||
public const MODEL_TEAM_LIST = 'teamList';
|
||||
public const MODEL_MEMBERSHIP = 'membership';
|
||||
public const MODEL_MEMBERSHIP_LIST = 'membershipList';
|
||||
|
||||
// Functions
|
||||
const MODEL_FUNCTION = 'function';
|
||||
const MODEL_FUNCTION_LIST = 'functionList';
|
||||
const MODEL_RUNTIME = 'runtime';
|
||||
const MODEL_RUNTIME_LIST = 'runtimeList';
|
||||
const MODEL_DEPLOYMENT = 'deployment';
|
||||
const MODEL_DEPLOYMENT_LIST = 'deploymentList';
|
||||
const MODEL_EXECUTION = 'execution';
|
||||
const MODEL_EXECUTION_LIST = 'executionList';
|
||||
const MODEL_BUILD = 'build';
|
||||
const MODEL_BUILD_LIST = 'buildList'; // Not used anywhere yet
|
||||
const MODEL_FUNC_PERMISSIONS = 'funcPermissions';
|
||||
|
||||
public const MODEL_FUNCTION = 'function';
|
||||
public const MODEL_FUNCTION_LIST = 'functionList';
|
||||
public const MODEL_RUNTIME = 'runtime';
|
||||
public const MODEL_RUNTIME_LIST = 'runtimeList';
|
||||
public const MODEL_DEPLOYMENT = 'deployment';
|
||||
public const MODEL_DEPLOYMENT_LIST = 'deploymentList';
|
||||
public const MODEL_EXECUTION = 'execution';
|
||||
public const MODEL_EXECUTION_LIST = 'executionList';
|
||||
public const MODEL_BUILD = 'build';
|
||||
public const MODEL_BUILD_LIST = 'buildList'; // Not used anywhere yet
|
||||
public const MODEL_FUNC_PERMISSIONS = 'funcPermissions';
|
||||
|
||||
// Project
|
||||
const MODEL_PROJECT = 'project';
|
||||
const MODEL_PROJECT_LIST = 'projectList';
|
||||
const MODEL_WEBHOOK = 'webhook';
|
||||
const MODEL_WEBHOOK_LIST = 'webhookList';
|
||||
const MODEL_KEY = 'key';
|
||||
const MODEL_KEY_LIST = 'keyList';
|
||||
const MODEL_PLATFORM = 'platform';
|
||||
const MODEL_PLATFORM_LIST = 'platformList';
|
||||
const MODEL_DOMAIN = 'domain';
|
||||
const MODEL_DOMAIN_LIST = 'domainList';
|
||||
public const MODEL_PROJECT = 'project';
|
||||
public const MODEL_PROJECT_LIST = 'projectList';
|
||||
public const MODEL_WEBHOOK = 'webhook';
|
||||
public const MODEL_WEBHOOK_LIST = 'webhookList';
|
||||
public const MODEL_KEY = 'key';
|
||||
public const MODEL_KEY_LIST = 'keyList';
|
||||
public const MODEL_PLATFORM = 'platform';
|
||||
public const MODEL_PLATFORM_LIST = 'platformList';
|
||||
public const MODEL_DOMAIN = 'domain';
|
||||
public const MODEL_DOMAIN_LIST = 'domainList';
|
||||
|
||||
// Health
|
||||
const MODEL_HEALTH_STATUS = 'healthStatus';
|
||||
const MODEL_HEALTH_VERSION = 'healthVersion';
|
||||
const MODEL_HEALTH_QUEUE = 'healthQueue';
|
||||
const MODEL_HEALTH_TIME = 'healthTime';
|
||||
const MODEL_HEALTH_ANTIVIRUS = 'healthAntivirus';
|
||||
|
||||
public const MODEL_HEALTH_STATUS = 'healthStatus';
|
||||
public const MODEL_HEALTH_VERSION = 'healthVersion';
|
||||
public const MODEL_HEALTH_QUEUE = 'healthQueue';
|
||||
public const MODEL_HEALTH_TIME = 'healthTime';
|
||||
public const MODEL_HEALTH_ANTIVIRUS = 'healthAntivirus';
|
||||
|
||||
// Deprecated
|
||||
const MODEL_PERMISSIONS = 'permissions';
|
||||
const MODEL_RULE = 'rule';
|
||||
const MODEL_TASK = 'task';
|
||||
public const MODEL_PERMISSIONS = 'permissions';
|
||||
public const MODEL_RULE = 'rule';
|
||||
public const MODEL_TASK = 'task';
|
||||
|
||||
// Tests (keep last)
|
||||
const MODEL_MOCK = 'mock';
|
||||
public const MODEL_MOCK = 'mock';
|
||||
|
||||
/**
|
||||
* @var Filter
|
||||
@@ -213,6 +217,7 @@ class Response extends SwooleResponse
|
||||
// Lists
|
||||
->setModel(new BaseList('Documents List', self::MODEL_DOCUMENT_LIST, 'documents', self::MODEL_DOCUMENT))
|
||||
->setModel(new BaseList('Collections List', self::MODEL_COLLECTION_LIST, 'collections', self::MODEL_COLLECTION))
|
||||
->setModel(new BaseList('Databases List', self::MODEL_DATABASE_LIST, 'databases', self::MODEL_DATABASE))
|
||||
->setModel(new BaseList('Indexes List', self::MODEL_INDEX_LIST, 'indexes', self::MODEL_INDEX))
|
||||
->setModel(new BaseList('Users List', self::MODEL_USER_LIST, 'users', self::MODEL_USER))
|
||||
->setModel(new BaseList('Sessions List', self::MODEL_SESSION_LIST, 'sessions', self::MODEL_SESSION))
|
||||
@@ -238,6 +243,7 @@ class Response extends SwooleResponse
|
||||
->setModel(new BaseList('Phones List', self::MODEL_PHONE_LIST, 'phones', self::MODEL_PHONE))
|
||||
->setModel(new BaseList('Metric List', self::MODEL_METRIC_LIST, 'metrics', self::MODEL_METRIC, true, false))
|
||||
// Entities
|
||||
->setModel(new Database())
|
||||
->setModel(new Collection())
|
||||
->setModel(new Attribute())
|
||||
->setModel(new AttributeList())
|
||||
@@ -283,6 +289,7 @@ class Response extends SwooleResponse
|
||||
->setModel(new HealthTime())
|
||||
->setModel(new HealthVersion())
|
||||
->setModel(new Metric())
|
||||
->setModel(new UsageDatabases())
|
||||
->setModel(new UsageDatabase())
|
||||
->setModel(new UsageCollection())
|
||||
->setModel(new UsageUsers())
|
||||
@@ -302,7 +309,7 @@ class Response extends SwooleResponse
|
||||
/**
|
||||
* HTTP content types
|
||||
*/
|
||||
const CONTENT_TYPE_YAML = 'application/x-yaml';
|
||||
public const CONTENT_TYPE_YAML = 'application/x-yaml';
|
||||
|
||||
/**
|
||||
* List of defined output objects
|
||||
@@ -329,7 +336,7 @@ class Response extends SwooleResponse
|
||||
public function getModel(string $key): Model
|
||||
{
|
||||
if (!isset($this->models[$key])) {
|
||||
throw new Exception('Undefined model: '.$key);
|
||||
throw new Exception('Undefined model: ' . $key);
|
||||
}
|
||||
|
||||
return $this->models[$key];
|
||||
@@ -392,13 +399,13 @@ class Response extends SwooleResponse
|
||||
if (!is_null($rule['default'])) {
|
||||
$document->setAttribute($key, $rule['default']);
|
||||
} else {
|
||||
throw new Exception('Model '.$model->getName().' is missing response key: '.$key);
|
||||
throw new Exception('Model ' . $model->getName() . ' is missing response key: ' . $key);
|
||||
}
|
||||
}
|
||||
|
||||
if ($rule['array']) {
|
||||
if (!is_array($data[$key])) {
|
||||
throw new Exception($key.' must be an array of type '.$rule['type']);
|
||||
throw new Exception($key . ' must be an array of type ' . $rule['type']);
|
||||
}
|
||||
|
||||
foreach ($data[$key] as &$item) {
|
||||
@@ -408,7 +415,7 @@ class Response extends SwooleResponse
|
||||
$condition = false;
|
||||
foreach ($this->getModel($type)->conditions as $attribute => $val) {
|
||||
$condition = $item->getAttribute($attribute) === $val;
|
||||
if(!$condition) {
|
||||
if (!$condition) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -422,7 +429,7 @@ class Response extends SwooleResponse
|
||||
}
|
||||
|
||||
if (!array_key_exists($ruleType, $this->models)) {
|
||||
throw new Exception('Missing model for rule: '. $ruleType);
|
||||
throw new Exception('Missing model for rule: ' . $ruleType);
|
||||
}
|
||||
|
||||
$item = $this->output($item, $ruleType);
|
||||
@@ -465,7 +472,7 @@ class Response extends SwooleResponse
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getPayload():array
|
||||
public function getPayload(): array
|
||||
{
|
||||
return $this->payload;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ namespace Appwrite\Utopia\Response;
|
||||
|
||||
abstract class Filter
|
||||
{
|
||||
|
||||
/**
|
||||
* Parse the content to another format.
|
||||
*
|
||||
|
||||
@@ -4,7 +4,6 @@ namespace Appwrite\Utopia\Response\Filters;
|
||||
|
||||
use Appwrite\Utopia\Response;
|
||||
use Appwrite\Utopia\Response\Filter;
|
||||
use Exception;
|
||||
|
||||
class V11 extends Filter
|
||||
{
|
||||
@@ -28,21 +27,21 @@ class V11 extends Filter
|
||||
case Response::MODEL_FILE_LIST:
|
||||
$parsedResponse = $this->parseFileList($content);
|
||||
break;
|
||||
|
||||
|
||||
case Response::MODEL_EXECUTION:
|
||||
$parsedResponse = $this->parseExecutionPermissions($content);
|
||||
break;
|
||||
case Response::MODEL_EXECUTION_LIST:
|
||||
$parsedResponse = $this->parseExecutionsList($content);
|
||||
break;
|
||||
|
||||
|
||||
case Response::MODEL_FUNCTION:
|
||||
$parsedResponse = $this->parseFunctionPermissions($content);
|
||||
break;
|
||||
case Response::MODEL_FUNCTION_LIST:
|
||||
$parsedResponse = $this->parseFunctionsList($content);
|
||||
break;
|
||||
|
||||
|
||||
// Convert status from boolean to int
|
||||
case Response::MODEL_USER:
|
||||
$parsedResponse = $this->parseStatus($content);
|
||||
@@ -50,7 +49,7 @@ class V11 extends Filter
|
||||
case Response::MODEL_USER_LIST:
|
||||
$parsedResponse = $this->parseUserList($content);
|
||||
break;
|
||||
|
||||
|
||||
// Convert all Health responses back to original
|
||||
case Response::MODEL_HEALTH_STATUS:
|
||||
$parsedResponse = $this->parseHealthStatus($content);
|
||||
@@ -94,7 +93,7 @@ class V11 extends Filter
|
||||
return $parsedResponse;
|
||||
}
|
||||
|
||||
protected function parseDocumentList(array $content)
|
||||
protected function parseDocumentList(array $content)
|
||||
{
|
||||
$documents = $content['documents'];
|
||||
$parsedResponse = [];
|
||||
@@ -105,7 +104,7 @@ class V11 extends Filter
|
||||
return $content;
|
||||
}
|
||||
|
||||
protected function parseFileList(array $content)
|
||||
protected function parseFileList(array $content)
|
||||
{
|
||||
$files = $content['files'];
|
||||
$parsedResponse = [];
|
||||
@@ -116,7 +115,7 @@ class V11 extends Filter
|
||||
return $content;
|
||||
}
|
||||
|
||||
protected function parseExecutionsList(array $content)
|
||||
protected function parseExecutionsList(array $content)
|
||||
{
|
||||
$executions = $content['executions'];
|
||||
$parsedResponse = [];
|
||||
@@ -127,7 +126,7 @@ class V11 extends Filter
|
||||
return $content;
|
||||
}
|
||||
|
||||
protected function parseFunctionsList(array $content)
|
||||
protected function parseFunctionsList(array $content)
|
||||
{
|
||||
$functions = $content['functions'];
|
||||
$parsedResponse = [];
|
||||
@@ -138,7 +137,7 @@ class V11 extends Filter
|
||||
return $content;
|
||||
}
|
||||
|
||||
protected function parseUserList(array $content)
|
||||
protected function parseUserList(array $content)
|
||||
{
|
||||
$users = $content['users'];
|
||||
$parsedResponse = [];
|
||||
@@ -149,7 +148,7 @@ class V11 extends Filter
|
||||
return $content;
|
||||
}
|
||||
|
||||
protected function parseCollection(array $content)
|
||||
protected function parseCollection(array $content)
|
||||
{
|
||||
$parsedResponse = [];
|
||||
$parsedResponse = $this->parsePermissions($content);
|
||||
@@ -163,7 +162,7 @@ class V11 extends Filter
|
||||
return $parsedResponse;
|
||||
}
|
||||
|
||||
protected function parseCollectionList(array $content)
|
||||
protected function parseCollectionList(array $content)
|
||||
{
|
||||
$collections = $content['collections'];
|
||||
$parsedResponse = [];
|
||||
@@ -174,7 +173,7 @@ class V11 extends Filter
|
||||
return $content;
|
||||
}
|
||||
|
||||
protected function parseLog(array $content)
|
||||
protected function parseLog(array $content)
|
||||
{
|
||||
$parsedResponse = [];
|
||||
$parsedResponse = $this->removeRule($content, 'userId');
|
||||
@@ -185,7 +184,7 @@ class V11 extends Filter
|
||||
return $parsedResponse;
|
||||
}
|
||||
|
||||
protected function parseLogList(array $content)
|
||||
protected function parseLogList(array $content)
|
||||
{
|
||||
$logs = $content['logs'];
|
||||
$parsedResponse = [];
|
||||
@@ -207,7 +206,7 @@ class V11 extends Filter
|
||||
return $parsedResponse;
|
||||
}
|
||||
|
||||
protected function parseProjectList(array $content)
|
||||
protected function parseProjectList(array $content)
|
||||
{
|
||||
$projects = $content['projects'];
|
||||
$parsedResponse = [];
|
||||
@@ -220,11 +219,11 @@ class V11 extends Filter
|
||||
|
||||
protected function parseHealthAntivirus(array $content)
|
||||
{
|
||||
if($content['status'] === 'pass') {
|
||||
if ($content['status'] === 'pass') {
|
||||
$content['status'] = 'online';
|
||||
}
|
||||
|
||||
if($content['status'] === 'fail') {
|
||||
if ($content['status'] === 'fail') {
|
||||
$content['status'] = 'offline';
|
||||
}
|
||||
|
||||
@@ -276,7 +275,7 @@ class V11 extends Filter
|
||||
|
||||
protected function parseAttributes(array $content)
|
||||
{
|
||||
$content['rules'] = \array_map(function($attribute) use($content) {
|
||||
$content['rules'] = \array_map(function ($attribute) use ($content) {
|
||||
return [
|
||||
'$id' => $attribute['key'],
|
||||
'$collection' => $content['$id'],
|
||||
@@ -308,7 +307,7 @@ class V11 extends Filter
|
||||
|
||||
foreach ($content as $key => $value) {
|
||||
\preg_match_all($regexPattern, $key, $regexGroups);
|
||||
if(\count($regexGroups[1]) > 0 && \count($regexGroups[2]) > 0) {
|
||||
if (\count($regexGroups[1]) > 0 && \count($regexGroups[2]) > 0) {
|
||||
$providerName = $regexGroups[1][0];
|
||||
$valueKey = $regexGroups[2][0];
|
||||
$content['usersOauth2' . $providerName . $valueKey] = $value;
|
||||
@@ -325,7 +324,7 @@ class V11 extends Filter
|
||||
|
||||
foreach ($content as $key => $value) {
|
||||
\preg_match_all($regexPattern, $key, $regexGroups);
|
||||
if(\count($regexGroups[1]) > 0) {
|
||||
if (\count($regexGroups[1]) > 0) {
|
||||
$providerName = $regexGroups[1][0];
|
||||
|
||||
$content[$providerName] = $value;
|
||||
@@ -341,7 +340,7 @@ class V11 extends Filter
|
||||
// Such a key is part of new response, but is not part of old one. We simply remove it, older version never
|
||||
// expected it anyway.
|
||||
foreach ($content as $key => $value) {
|
||||
if(\str_starts_with($key, 'serviceStatusFor')) {
|
||||
if (\str_starts_with($key, 'serviceStatusFor')) {
|
||||
unset($content[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ namespace Appwrite\Utopia\Response\Filters;
|
||||
|
||||
use Appwrite\Utopia\Response;
|
||||
use Appwrite\Utopia\Response\Filter;
|
||||
use Exception;
|
||||
|
||||
class V12 extends Filter
|
||||
{
|
||||
@@ -220,14 +219,16 @@ class V12 extends Filter
|
||||
return $content;
|
||||
}
|
||||
|
||||
protected function parseExecution($content) {
|
||||
protected function parseExecution($content)
|
||||
{
|
||||
$content['exitCode'] = $content['statusCode'];
|
||||
unset($content['statusCode']);
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
protected function parseExecutionList($content) {
|
||||
protected function parseExecutionList($content)
|
||||
{
|
||||
$executions = $content['executions'];
|
||||
$parsedResponse = [];
|
||||
foreach ($executions as $document) {
|
||||
@@ -239,13 +240,15 @@ class V12 extends Filter
|
||||
return $content;
|
||||
}
|
||||
|
||||
protected function parseTeam($content) {
|
||||
protected function parseTeam($content)
|
||||
{
|
||||
$content['sum'] = $content['total'];
|
||||
unset($content['total']);
|
||||
return $content;
|
||||
}
|
||||
|
||||
protected function parseTeamList($content) {
|
||||
protected function parseTeamList($content)
|
||||
{
|
||||
$teams = $content['teams'];
|
||||
$parsedResponse = [];
|
||||
foreach ($teams as $document) {
|
||||
@@ -257,9 +260,10 @@ class V12 extends Filter
|
||||
return $content;
|
||||
}
|
||||
|
||||
protected function parseList($content) {
|
||||
protected function parseList($content)
|
||||
{
|
||||
$content['sum'] = $content['total'];
|
||||
unset($content['total']);
|
||||
return $content;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Utopia\Response\Filters;
|
||||
|
||||
use Appwrite\Utopia\Response;
|
||||
use Appwrite\Utopia\Response\Filter;
|
||||
|
||||
class V13 extends Filter
|
||||
{
|
||||
// Convert 0.14 Data format to 0.13 format
|
||||
public function parse(array $content, string $model): array
|
||||
{
|
||||
$parsedResponse = $content;
|
||||
|
||||
switch ($model) {
|
||||
case Response::MODEL_PROJECT:
|
||||
$parsedResponse = $this->parseProject($content);
|
||||
break;
|
||||
|
||||
case Response::MODEL_PROJECT_LIST:
|
||||
$parsedResponse = $this->parseProjectList($content);
|
||||
break;
|
||||
|
||||
case Response::MODEL_MEMBERSHIP:
|
||||
$parsedResponse = $this->parseMembership($content);
|
||||
break;
|
||||
case Response::MODEL_MEMBERSHIP_LIST:
|
||||
$parsedResponse = $this->parseMembershipList($content);
|
||||
break;
|
||||
|
||||
case Response::MODEL_EXECUTION:
|
||||
$parsedResponse = $this->parseExecution($content);
|
||||
break;
|
||||
case Response::MODEL_EXECUTION_LIST:
|
||||
$parsedResponse = $this->parseExecutionList($content);
|
||||
break;
|
||||
}
|
||||
|
||||
return $parsedResponse;
|
||||
}
|
||||
|
||||
protected function parseExecution($content)
|
||||
{
|
||||
$content['stdout'] = $content['response'];
|
||||
unset($content['response']);
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
protected function parseExecutionList($content)
|
||||
{
|
||||
$executions = $content['executions'];
|
||||
$parsedResponse = [];
|
||||
foreach ($executions as $document) {
|
||||
$parsedResponse[] = $this->parseExecution($document);
|
||||
}
|
||||
$content['executions'] = $parsedResponse;
|
||||
return $content;
|
||||
}
|
||||
|
||||
protected function parseProject($content)
|
||||
{
|
||||
$content['providers'] = $content['authProviders'];
|
||||
unset($content['authProviders']);
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
protected function parseProjectList($content)
|
||||
{
|
||||
$projects = $content['projects'];
|
||||
$parsedResponse = [];
|
||||
foreach ($projects as $document) {
|
||||
$parsedResponse[] = $this->parseProject($document);
|
||||
}
|
||||
$content['projects'] = $parsedResponse;
|
||||
return $content;
|
||||
}
|
||||
|
||||
protected function parseMembership($content)
|
||||
{
|
||||
$content['name'] = $content['userName'];
|
||||
unset($content['userName']);
|
||||
|
||||
$content['email'] = $content['userEmail'];
|
||||
unset($content['userEmail']);
|
||||
|
||||
unset($content['teamName']);
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
protected function parseMembershipList($content)
|
||||
{
|
||||
$memberships = $content['memberships'];
|
||||
$parsedResponse = [];
|
||||
foreach ($memberships as $document) {
|
||||
$parsedResponse[] = $this->parseMembership($document);
|
||||
}
|
||||
$content['memberships'] = $parsedResponse;
|
||||
return $content;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Utopia\Response\Filters;
|
||||
|
||||
use Appwrite\Utopia\Response;
|
||||
use Appwrite\Utopia\Response\Filter;
|
||||
|
||||
class V14 extends Filter
|
||||
{
|
||||
// Convert 0.15 Data format to 0.14 format
|
||||
public function parse(array $content, string $model): array
|
||||
{
|
||||
$parsedResponse = $content;
|
||||
|
||||
switch ($model) {
|
||||
case Response::MODEL_SESSION:
|
||||
case Response::MODEL_TOKEN:
|
||||
$parsedResponse = $this->parseRemoveAttributes($content, ['$createdAt']);
|
||||
|
||||
break;
|
||||
case Response::MODEL_SESSION_LIST:
|
||||
$parsedResponse = $this->parseRemoveAttributesList($content, 'domains', ['$createdAt']);
|
||||
|
||||
break;
|
||||
case Response::MODEL_DOCUMENT:
|
||||
case Response::MODEL_DOMAIN:
|
||||
case Response::MODEL_FUNCTION:
|
||||
case Response::MODEL_TEAM:
|
||||
case Response::MODEL_MEMBERSHIP:
|
||||
case Response::MODEL_PLATFORM:
|
||||
case Response::MODEL_PROJECT:
|
||||
case Response::MODEL_USER:
|
||||
case Response::MODEL_WEBHOOK:
|
||||
$parsedResponse = $this->parseRemoveAttributes($content, ['$createdAt', '$updatedAt']);
|
||||
|
||||
break;
|
||||
case Response::MODEL_DOCUMENT_LIST:
|
||||
$parsedResponse = $this->parseRemoveAttributesList($content, 'documents', ['$createdAt', '$updatedAt']);
|
||||
|
||||
break;
|
||||
case Response::MODEL_DOMAIN_LIST:
|
||||
$parsedResponse = $this->parseRemoveAttributesList($content, 'domains', ['$createdAt', '$updatedAt']);
|
||||
|
||||
break;
|
||||
case Response::MODEL_FUNCTION_LIST:
|
||||
$parsedResponse = $this->parseRemoveAttributesList($content, 'functions', ['$createdAt', '$updatedAt']);
|
||||
|
||||
break;
|
||||
case Response::MODEL_TEAM_LIST:
|
||||
$parsedResponse = $this->parseRemoveAttributesList($content, 'teams', ['$createdAt', '$updatedAt']);
|
||||
|
||||
break;
|
||||
case Response::MODEL_MEMBERSHIP_LIST:
|
||||
$parsedResponse = $this->parseRemoveAttributesList($content, 'memberships', ['$createdAt', '$updatedAt']);
|
||||
|
||||
break;
|
||||
case Response::MODEL_PLATFORM_LIST:
|
||||
$parsedResponse = $this->parseRemoveAttributesList($content, 'platforms', ['$createdAt', '$updatedAt']);
|
||||
|
||||
break;
|
||||
case Response::MODEL_PROJECT_LIST:
|
||||
$parsedResponse = $this->parseRemoveAttributesList($content, 'projects', ['$createdAt', '$updatedAt']);
|
||||
|
||||
break;
|
||||
case Response::MODEL_USER_LIST:
|
||||
$parsedResponse = $this->parseRemoveAttributesList($content, 'users', ['$createdAt', '$updatedAt']);
|
||||
|
||||
break;
|
||||
case Response::MODEL_WEBHOOK_LIST:
|
||||
$parsedResponse = $this->parseRemoveAttributesList($content, 'webhooks', ['$createdAt', '$updatedAt']);
|
||||
|
||||
break;
|
||||
case Response::MODEL_TEAM:
|
||||
case Response::MODEL_EXECUTION:
|
||||
case Response::MODEL_FILE:
|
||||
$parsedResponse = $this->parseCreatedAt($content);
|
||||
break;
|
||||
|
||||
case Response::MODEL_TEAM_LIST:
|
||||
$parsedResponse = $this->parseCreatedAtList($content, 'teams');
|
||||
break;
|
||||
|
||||
case Response::MODEL_EXECUTION_LIST:
|
||||
$parsedResponse = $this->parseCreatedAtList($content, 'executions');
|
||||
break;
|
||||
|
||||
case Response::MODEL_FILE_LIST:
|
||||
$parsedResponse = $this->parseCreatedAtList($content, 'files');
|
||||
break;
|
||||
|
||||
case Response::MODEL_FUNCTION:
|
||||
case Response::MODEL_DEPLOYMENT:
|
||||
case Response::MODEL_BUCKET:
|
||||
$parsedResponse = $this->parseCreatedAtAndUpdatedAt($content);
|
||||
break;
|
||||
|
||||
case Response::MODEL_FUNCTION_LIST:
|
||||
$parsedResponse = $this->parseCreatedAtAndUpdatedAtList($content, 'functions');
|
||||
break;
|
||||
|
||||
case Response::MODEL_DEPLOYMENT_LIST:
|
||||
$parsedResponse = $this->parseCreatedAtAndUpdatedAtList($content, 'deployments');
|
||||
break;
|
||||
|
||||
case Response::MODEL_BUCKET_LIST:
|
||||
$parsedResponse = $this->parseCreatedAtAndUpdatedAtList($content, 'buckets');
|
||||
break;
|
||||
}
|
||||
|
||||
return $parsedResponse;
|
||||
}
|
||||
|
||||
protected function parseRemoveAttributes(array $content, array $attributes)
|
||||
{
|
||||
foreach ($attributes as $attribute) {
|
||||
unset($content[$attribute]);
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
protected function parseRemoveAttributesList(array $content, string $property, array $attributes)
|
||||
{
|
||||
$documents = $content[$property];
|
||||
$parsedResponse = [];
|
||||
foreach ($documents as $document) {
|
||||
$parsedResponse[] = $this->parseRemoveAttributes($document, $attributes);
|
||||
}
|
||||
$content[$property] = $parsedResponse;
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
protected function parseCreatedAt(array $content)
|
||||
{
|
||||
$content['dateCreated'] = $content['$createdAt'];
|
||||
unset($content['$createdAt']);
|
||||
unset($content['$updatedAt']);
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
protected function parseCreatedAtList(array $content, string $property)
|
||||
{
|
||||
$documents = $content[$property];
|
||||
$parsedResponse = [];
|
||||
foreach ($documents as $document) {
|
||||
$parsedResponse[] = $this->parseCreatedAt($document);
|
||||
}
|
||||
$content[$property] = $parsedResponse;
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
protected function parseCreatedAtAndUpdatedAt(array $content)
|
||||
{
|
||||
$content['dateCreated'] = $content['$createdAt'];
|
||||
$content['dateUpdated'] = $content['$updatedAt'];
|
||||
unset($content['$createdAt']);
|
||||
unset($content['$updatedAt']);
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
protected function parseCreatedAtAndUpdatedAtList(array $content, string $property)
|
||||
{
|
||||
$documents = $content[$property];
|
||||
$parsedResponse = [];
|
||||
foreach ($documents as $document) {
|
||||
$parsedResponse[] = $this->parseCreatedAtAndUpdatedAt($document);
|
||||
}
|
||||
$content[$property] = $parsedResponse;
|
||||
|
||||
return $content;
|
||||
}
|
||||
}
|
||||
@@ -6,11 +6,11 @@ use Utopia\Database\Document;
|
||||
|
||||
abstract class Model
|
||||
{
|
||||
const TYPE_STRING = 'string';
|
||||
const TYPE_INTEGER = 'integer';
|
||||
const TYPE_FLOAT = 'double';
|
||||
const TYPE_BOOLEAN = 'boolean';
|
||||
const TYPE_JSON = 'json';
|
||||
public const TYPE_STRING = 'string';
|
||||
public const TYPE_INTEGER = 'integer';
|
||||
public const TYPE_FLOAT = 'double';
|
||||
public const TYPE_BOOLEAN = 'boolean';
|
||||
public const TYPE_JSON = 'json';
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
@@ -34,7 +34,7 @@ abstract class Model
|
||||
|
||||
/**
|
||||
* Filter Document Structure
|
||||
*
|
||||
*
|
||||
* @return Document
|
||||
*/
|
||||
public function filter(Document $document): Document
|
||||
|
||||
@@ -17,7 +17,7 @@ class Any extends Model
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName():string
|
||||
public function getName(): string
|
||||
{
|
||||
return 'Any';
|
||||
}
|
||||
@@ -27,7 +27,7 @@ class Any extends Model
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getType():string
|
||||
public function getType(): string
|
||||
{
|
||||
return Response::MODEL_ANY;
|
||||
}
|
||||
|
||||
@@ -48,20 +48,20 @@ class Attribute extends Model
|
||||
|
||||
/**
|
||||
* Get Name
|
||||
*
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName():string
|
||||
public function getName(): string
|
||||
{
|
||||
return 'Attribute';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Collection
|
||||
*
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getType():string
|
||||
public function getType(): string
|
||||
{
|
||||
return Response::MODEL_ATTRIBUTE;
|
||||
}
|
||||
|
||||
@@ -12,6 +12,18 @@ class AttributeBoolean extends Attribute
|
||||
parent::__construct();
|
||||
|
||||
$this
|
||||
->addRule('key', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'Attribute Key.',
|
||||
'default' => '',
|
||||
'example' => 'isEnabled',
|
||||
])
|
||||
->addRule('type', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'Attribute type.',
|
||||
'default' => '',
|
||||
'example' => 'boolean',
|
||||
])
|
||||
->addRule('default', [
|
||||
'type' => self::TYPE_BOOLEAN,
|
||||
'description' => 'Default value for attribute when not provided. Cannot be set when attribute is required.',
|
||||
@@ -29,21 +41,21 @@ class AttributeBoolean extends Attribute
|
||||
|
||||
/**
|
||||
* Get Name
|
||||
*
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName():string
|
||||
public function getName(): string
|
||||
{
|
||||
return 'AttributeBoolean';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Type
|
||||
*
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getType():string
|
||||
public function getType(): string
|
||||
{
|
||||
return Response::MODEL_ATTRIBUTE_BOOLEAN;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,18 @@ class AttributeEmail extends Attribute
|
||||
parent::__construct();
|
||||
|
||||
$this
|
||||
->addRule('key', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'Attribute Key.',
|
||||
'default' => '',
|
||||
'example' => 'userEmail',
|
||||
])
|
||||
->addRule('type', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'Attribute type.',
|
||||
'default' => '',
|
||||
'example' => 'string',
|
||||
])
|
||||
->addRule('format', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'String format.',
|
||||
@@ -38,21 +50,21 @@ class AttributeEmail extends Attribute
|
||||
|
||||
/**
|
||||
* Get Name
|
||||
*
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName():string
|
||||
public function getName(): string
|
||||
{
|
||||
return 'AttributeEmail';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Type
|
||||
*
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getType():string
|
||||
public function getType(): string
|
||||
{
|
||||
return Response::MODEL_ATTRIBUTE_EMAIL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user