Merge branch '0.14.x' of https://github.com/appwrite/appwrite into feat-runtime-versioning
This commit is contained in:
commit
acaf60efaf
55
CHANGES.md
55
CHANGES.md
|
@ -1,6 +1,57 @@
|
|||
# Unreleased Version
|
||||
- Renamed `providers` to `authProviders` in project collection **Breaking Change**
|
||||
# Version 0.14.0
|
||||
|
||||
## Features
|
||||
- New Event Model **Breaking Change**
|
||||
- The new Event Model allows you to define events for Webhooks or Functions more granular
|
||||
- Account and Users events have been merged to just Users
|
||||
- Examples:
|
||||
- `database.documents.create` is now `collections.[COLLECTION_ID].documents.[DOCUMENT_ID].create`
|
||||
- Both placeholders needs to be replaced with either `*` for wildcard or an ID of the respective collection or document
|
||||
- So you can listen to every document that is created in the `posts` collection with `collections.posts.*.documents.*.create`
|
||||
- `event` in the Realtime payload has been renamed to `events` and contains all possible events
|
||||
- `X-Appwrite-Webhook-Event` Webhook header has been renamed to `X-Appwrite-Webhook-Events` and contains all possible events
|
||||
- Renamed `providers` to `authProviders` in Projects **Breaking Change**
|
||||
- Renamed `stdout` to `response` in Projects **Breaking Change**
|
||||
- Removed deleted endpoint from the Accounts API
|
||||
- Added new endpoint to update user's status from the Accounts API
|
||||
- Deleted users will now free their ID and not reserve it anymore
|
||||
- Added new endpoint to list all memberships on the Users API
|
||||
- Increased Execution response to 1MB
|
||||
- Added Wildcard support to Platforms
|
||||
- Added Activity page to Teams console
|
||||
- Added button to verify/unverify user's e-mail address in the console
|
||||
- Added Docker log limits to `docker-compose.yaml`
|
||||
- Renamed `_APP_EXECUTOR_RUNTIME_NETWORK` environment variable to `OPEN_RUNTIMES_NETWORK`
|
||||
## Bugs
|
||||
- Fixed issues with `min`, `max` and `default` values for float attributes
|
||||
- Fixed account created with Magic URL to set a new password
|
||||
- Fixed Database to respect `null` values
|
||||
- Fixed missing realtime events from the Users API
|
||||
- Fixed missing events when all sessions are deleted from the Users and Account API
|
||||
- Fixed dots in database attributes
|
||||
- Fixed renewal of SSL certificates
|
||||
- Fixed errors in the certificates workers
|
||||
- Fixed HTTPS redirect bug for non GET requests
|
||||
- Fixed search when a User is updated
|
||||
- Fixed aspect ratio bug in Avatars API
|
||||
- Fixed wrong `Fail to Warmup ...` error message in Executor
|
||||
- Fixed UI when file uploader is covered by jumpt to top button
|
||||
- Fixed bug that allowed Queries on failed indexes
|
||||
- Fixed UI when an alert with a lot text disappears too fast by increasing duration
|
||||
- Fixed issues with cache and case-sensivity on ID's
|
||||
- Fixed storage stats by upgrading to `BIGINT`
|
||||
- Fixed `storage.total` stats which now is a sum of `storage.files.total` and `storage.deployments.total`
|
||||
- Fixed Project logo preview
|
||||
- Fixed UI for missing icons in Collection attributes
|
||||
- Fixed UI to allow single-character custom ID's
|
||||
- Fixed array size validation in the Database Service
|
||||
- Fixed file preview when file extension is missing
|
||||
- Fixed `Open an Issue` link in the console
|
||||
- Fixed missing environment variables on Executor service
|
||||
- Fixed all endpoints that expect an Array in their params to have not more than 100 items
|
||||
|
||||
## Security
|
||||
- OAuth2 provider now check if the email is verified
|
||||
# Version 0.13.4
|
||||
|
||||
## Features
|
||||
|
|
|
@ -1090,18 +1090,7 @@ $collections = [
|
|||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'deleted',
|
||||
'type' => Database::VAR_BOOLEAN,
|
||||
'format' => '',
|
||||
'size' => 0,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
]
|
||||
],
|
||||
'indexes' => [
|
||||
[
|
||||
|
@ -1117,14 +1106,7 @@ $collections = [
|
|||
'attributes' => ['search'],
|
||||
'lengths' => [],
|
||||
'orders' => [],
|
||||
],
|
||||
[
|
||||
'$id' => '_key_deleted_email',
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['deleted', 'email'],
|
||||
'lengths' => [0, 320],
|
||||
'orders' => [Database::ORDER_ASC, Database::ORDER_ASC],
|
||||
],
|
||||
]
|
||||
],
|
||||
],
|
||||
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -81,9 +81,7 @@ App::post('/v1/account')
|
|||
$limit = $project->getAttribute('auths', [])['limit'] ?? 0;
|
||||
|
||||
if ($limit !== 0) {
|
||||
$total = $dbForProject->count('users', [
|
||||
new Query('deleted', Query::TYPE_EQUAL, [false]),
|
||||
], APP_LIMIT_USERS);
|
||||
$total = $dbForProject->count('users', max: APP_LIMIT_USERS);
|
||||
|
||||
if ($total >= $limit) {
|
||||
throw new Exception('Project registration is restricted. Contact your administrator for more information.', 501, Exception::USER_COUNT_EXCEEDED);
|
||||
|
@ -108,8 +106,7 @@ App::post('/v1/account')
|
|||
'sessions' => null,
|
||||
'tokens' => null,
|
||||
'memberships' => null,
|
||||
'search' => implode(' ', [$userId, $email, $name]),
|
||||
'deleted' => false
|
||||
'search' => implode(' ', [$userId, $email, $name])
|
||||
])));
|
||||
} catch (Duplicate $th) {
|
||||
throw new Exception('Account already exists', 409, Exception::USER_ALREADY_EXISTS);
|
||||
|
@ -170,7 +167,6 @@ App::post('/v1/account/sessions')
|
|||
$protocol = $request->getProtocol();
|
||||
|
||||
$profile = $dbForProject->findOne('users', [
|
||||
new Query('deleted', Query::TYPE_EQUAL, [false]),
|
||||
new Query('email', Query::TYPE_EQUAL, [$email])]
|
||||
);
|
||||
|
||||
|
@ -475,21 +471,21 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
|
|||
if ($user === false || $user->isEmpty()) { // No user logged in or with OAuth2 provider ID, create new one or connect with account with same email
|
||||
$name = $oauth2->getUserName($accessToken);
|
||||
$email = $oauth2->getUserEmail($accessToken);
|
||||
|
||||
/**
|
||||
* Is verified is not used yet, since we don't know after an accout is created anymore if it was verified or not.
|
||||
*/
|
||||
$isVerified = $oauth2->isEmailVerified($accessToken);
|
||||
|
||||
if ($isVerified === true) {
|
||||
// Get user by email address
|
||||
$user = $dbForProject->findOne('users', [
|
||||
new Query('deleted', Query::TYPE_EQUAL, [false]),
|
||||
new Query('email', Query::TYPE_EQUAL, [$email])]
|
||||
);
|
||||
}
|
||||
$user = $dbForProject->findOne('users', [
|
||||
new Query('email', Query::TYPE_EQUAL, [$email])]
|
||||
);
|
||||
|
||||
if ($user === false || $user->isEmpty()) { // Last option -> create the user, generate random password
|
||||
$limit = $project->getAttribute('auths', [])['limit'] ?? 0;
|
||||
|
||||
if ($limit !== 0) {
|
||||
$total = $dbForProject->count('users', [new Query('deleted', Query::TYPE_EQUAL, [false])], APP_LIMIT_USERS);
|
||||
$total = $dbForProject->count('users', max: APP_LIMIT_USERS);
|
||||
|
||||
if ($total >= $limit) {
|
||||
throw new Exception('Project registration is restricted. Contact your administrator for more information.', 501, Exception::USER_COUNT_EXCEEDED);
|
||||
|
@ -503,7 +499,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
|
|||
'$read' => ['role:all'],
|
||||
'$write' => ['user:' . $userId],
|
||||
'email' => $email,
|
||||
'emailVerification' => $isVerified,
|
||||
'emailVerification' => true,
|
||||
'status' => true, // Email should already be authenticated by OAuth2 provider
|
||||
'password' => Auth::passwordHash(Auth::passwordGenerator()),
|
||||
'passwordUpdate' => 0,
|
||||
|
@ -514,8 +510,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
|
|||
'sessions' => null,
|
||||
'tokens' => null,
|
||||
'memberships' => null,
|
||||
'search' => implode(' ', [$userId, $email, $name]),
|
||||
'deleted' => false
|
||||
'search' => implode(' ', [$userId, $email, $name])
|
||||
])));
|
||||
} catch (Duplicate $th) {
|
||||
throw new Exception('Account already exists', 409, Exception::USER_ALREADY_EXISTS);
|
||||
|
@ -663,9 +658,7 @@ App::post('/v1/account/sessions/magic-url')
|
|||
$limit = $project->getAttribute('auths', [])['limit'] ?? 0;
|
||||
|
||||
if ($limit !== 0) {
|
||||
$total = $dbForProject->count('users', [
|
||||
new Query('deleted', Query::TYPE_EQUAL, [false]),
|
||||
], APP_LIMIT_USERS);
|
||||
$total = $dbForProject->count('users', max: APP_LIMIT_USERS);
|
||||
|
||||
if ($total >= $limit) {
|
||||
throw new Exception('Project registration is restricted. Contact your administrator for more information.', 501, Exception::USER_COUNT_EXCEEDED);
|
||||
|
@ -689,8 +682,7 @@ App::post('/v1/account/sessions/magic-url')
|
|||
'sessions' => null,
|
||||
'tokens' => null,
|
||||
'memberships' => null,
|
||||
'search' => implode(' ', [$userId, $email]),
|
||||
'deleted' => false
|
||||
'search' => implode(' ', [$userId, $email])
|
||||
])));
|
||||
}
|
||||
|
||||
|
@ -790,7 +782,7 @@ App::put('/v1/account/sessions/magic-url')
|
|||
|
||||
$user = Authorization::skip(fn() => $dbForProject->getDocument('users', $userId));
|
||||
|
||||
if ($user->isEmpty() || $user->getAttribute('deleted')) {
|
||||
if ($user->isEmpty()) {
|
||||
throw new Exception('User not found', 404, Exception::USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
|
@ -925,9 +917,7 @@ App::post('/v1/account/sessions/anonymous')
|
|||
$limit = $project->getAttribute('auths', [])['limit'] ?? 0;
|
||||
|
||||
if ($limit !== 0) {
|
||||
$total = $dbForProject->count('users', [
|
||||
new Query('deleted', Query::TYPE_EQUAL, [false]),
|
||||
], APP_LIMIT_USERS);
|
||||
$total = $dbForProject->count('users', max: APP_LIMIT_USERS);
|
||||
|
||||
if ($total >= $limit) {
|
||||
throw new Exception('Project registration is restricted. Contact your administrator for more information.', 501, Exception::USER_COUNT_EXCEEDED);
|
||||
|
@ -951,8 +941,7 @@ App::post('/v1/account/sessions/anonymous')
|
|||
'sessions' => null,
|
||||
'tokens' => null,
|
||||
'memberships' => null,
|
||||
'search' => $userId,
|
||||
'deleted' => false
|
||||
'search' => $userId
|
||||
])));
|
||||
|
||||
// Create session token
|
||||
|
@ -1469,17 +1458,18 @@ App::patch('/v1/account/prefs')
|
|||
$response->dynamic($user, Response::MODEL_USER);
|
||||
});
|
||||
|
||||
App::delete('/v1/account')
|
||||
->desc('Delete Account')
|
||||
App::patch('/v1/account/status')
|
||||
->desc('Update Account Status')
|
||||
->groups(['api', 'account'])
|
||||
->label('event', 'users.[userId].delete')
|
||||
->label('event', 'users.[userId].update.status')
|
||||
->label('scope', 'account')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
|
||||
->label('sdk.namespace', 'account')
|
||||
->label('sdk.method', 'delete')
|
||||
->label('sdk.description', '/docs/references/account/delete.md')
|
||||
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
|
||||
->label('sdk.response.model', Response::MODEL_NONE)
|
||||
->label('sdk.method', 'updateStatus')
|
||||
->label('sdk.description', '/docs/references/account/update-status.md')
|
||||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_USER)
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->inject('user')
|
||||
|
@ -1496,28 +1486,15 @@ App::delete('/v1/account')
|
|||
/** @var Appwrite\Event\Event $events */
|
||||
/** @var Appwrite\Stats\Stats $usage */
|
||||
|
||||
$protocol = $request->getProtocol();
|
||||
$user = $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('status', false));
|
||||
|
||||
// TODO Seems to be related to users.php/App::delete('/v1/users/:userId'). Can we share code between these two? Do todos below apply to users.php?
|
||||
|
||||
// TODO delete all tokens or only current session?
|
||||
// TODO delete all user data according to GDPR. Make sure everything is backed up and backups are deleted later
|
||||
/**
|
||||
* Data to delete
|
||||
* * Tokens
|
||||
* * Memberships
|
||||
*/
|
||||
|
||||
$audits
|
||||
->setResource('user/' . $user->getId())
|
||||
->setPayload($response->output($user, Response::MODEL_USER))
|
||||
;
|
||||
->setPayload($response->output($user, Response::MODEL_USER));
|
||||
|
||||
$events
|
||||
->setParam('userId', $user->getId())
|
||||
->setPayload($response->output($user, Response::MODEL_USER))
|
||||
;
|
||||
->setPayload($response->output($user, Response::MODEL_USER));
|
||||
|
||||
if (!Config::getParam('domainVerification')) {
|
||||
$response->addHeader('X-Fallback-Cookies', \json_encode([]));
|
||||
|
@ -1525,11 +1502,7 @@ App::delete('/v1/account')
|
|||
|
||||
$usage->setParam('users.delete', 1);
|
||||
|
||||
$response
|
||||
->addCookie(Auth::$cookieName . '_legacy', '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null)
|
||||
->addCookie(Auth::$cookieName, '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite'))
|
||||
->noContent()
|
||||
;
|
||||
$response->dynamic($user, Response::MODEL_USER);
|
||||
});
|
||||
|
||||
App::delete('/v1/account/sessions/:sessionId')
|
||||
|
@ -1839,7 +1812,6 @@ App::post('/v1/account/recovery')
|
|||
$email = \strtolower($email);
|
||||
|
||||
$profile = $dbForProject->findOne('users', [
|
||||
new Query('deleted', Query::TYPE_EQUAL, [false]),
|
||||
new Query('email', Query::TYPE_EQUAL, [$email])
|
||||
]);
|
||||
|
||||
|
@ -1942,7 +1914,7 @@ App::put('/v1/account/recovery')
|
|||
|
||||
$profile = $dbForProject->getDocument('users', $userId);
|
||||
|
||||
if ($profile->isEmpty() || $profile->getAttribute('deleted')) {
|
||||
if ($profile->isEmpty()) {
|
||||
throw new Exception('User not found', 404, Exception::USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
|
|
|
@ -348,8 +348,7 @@ App::post('/v1/teams/:teamId/memberships')
|
|||
'sessions' => null,
|
||||
'tokens' => null,
|
||||
'memberships' => null,
|
||||
'search' => implode(' ', [$userId, $email, $name]),
|
||||
'deleted' => false
|
||||
'search' => implode(' ', [$userId, $email, $name])
|
||||
])));
|
||||
} catch (Duplicate $th) {
|
||||
throw new Exception('Account already exists', 409, Exception::USER_ALREADY_EXISTS);
|
||||
|
|
|
@ -68,8 +68,7 @@ App::post('/v1/users')
|
|||
'sessions' => null,
|
||||
'tokens' => null,
|
||||
'memberships' => null,
|
||||
'search' => implode(' ', [$userId, $email, $name]),
|
||||
'deleted' => false
|
||||
'search' => implode(' ', [$userId, $email, $name])
|
||||
]));
|
||||
} catch (Duplicate $th) {
|
||||
throw new Exception('Account already exists', 409, Exception::USER_ALREADY_EXISTS);
|
||||
|
@ -120,9 +119,7 @@ App::get('/v1/users')
|
|||
}
|
||||
}
|
||||
|
||||
$queries = [
|
||||
new Query('deleted', Query::TYPE_EQUAL, [false])
|
||||
];
|
||||
$queries = [];
|
||||
|
||||
if (!empty($search)) {
|
||||
$queries[] = new Query('search', Query::TYPE_SEARCH, [$search]);
|
||||
|
@ -160,7 +157,7 @@ App::get('/v1/users/:userId')
|
|||
|
||||
$user = $dbForProject->getDocument('users', $userId);
|
||||
|
||||
if ($user->isEmpty() || $user->getAttribute('deleted')) {
|
||||
if ($user->isEmpty()) {
|
||||
throw new Exception('User not found', 404, Exception::USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
|
@ -192,7 +189,7 @@ App::get('/v1/users/:userId/prefs')
|
|||
|
||||
$user = $dbForProject->getDocument('users', $userId);
|
||||
|
||||
if ($user->isEmpty() || $user->getAttribute('deleted')) {
|
||||
if ($user->isEmpty()) {
|
||||
throw new Exception('User not found', 404, Exception::USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
|
@ -228,7 +225,7 @@ App::get('/v1/users/:userId/sessions')
|
|||
|
||||
$user = $dbForProject->getDocument('users', $userId);
|
||||
|
||||
if ($user->isEmpty() || $user->getAttribute('deleted')) {
|
||||
if ($user->isEmpty()) {
|
||||
throw new Exception('User not found', 404, Exception::USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
|
@ -274,7 +271,7 @@ App::get('/v1/users/:userId/memberships')
|
|||
|
||||
$user = $dbForProject->getDocument('users', $userId);
|
||||
|
||||
if ($user->isEmpty() || $user->getAttribute('deleted')) {
|
||||
if ($user->isEmpty()) {
|
||||
throw new Exception('User not found', 404, Exception::USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
|
@ -324,7 +321,7 @@ App::get('/v1/users/:userId/logs')
|
|||
|
||||
$user = $dbForProject->getDocument('users', $userId);
|
||||
|
||||
if ($user->isEmpty() || $user->getAttribute('deleted')) {
|
||||
if ($user->isEmpty()) {
|
||||
throw new Exception('User not found', 404, Exception::USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
|
@ -409,7 +406,7 @@ App::patch('/v1/users/:userId/status')
|
|||
|
||||
$user = $dbForProject->getDocument('users', $userId);
|
||||
|
||||
if ($user->isEmpty() || $user->getAttribute('deleted')) {
|
||||
if ($user->isEmpty()) {
|
||||
throw new Exception('User not found', 404, Exception::USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
|
@ -452,7 +449,7 @@ App::patch('/v1/users/:userId/verification')
|
|||
|
||||
$user = $dbForProject->getDocument('users', $userId);
|
||||
|
||||
if ($user->isEmpty() || $user->getAttribute('deleted')) {
|
||||
if ($user->isEmpty()) {
|
||||
throw new Exception('User not found', 404, Exception::USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
|
@ -495,7 +492,7 @@ App::patch('/v1/users/:userId/name')
|
|||
|
||||
$user = $dbForProject->getDocument('users', $userId);
|
||||
|
||||
if ($user->isEmpty() || $user->getAttribute('deleted')) {
|
||||
if ($user->isEmpty()) {
|
||||
throw new Exception('User not found', 404, Exception::USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
|
@ -543,7 +540,7 @@ App::patch('/v1/users/:userId/password')
|
|||
|
||||
$user = $dbForProject->getDocument('users', $userId);
|
||||
|
||||
if ($user->isEmpty() || $user->getAttribute('deleted')) {
|
||||
if ($user->isEmpty()) {
|
||||
throw new Exception('User not found', 404, Exception::USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
|
@ -590,7 +587,7 @@ App::patch('/v1/users/:userId/email')
|
|||
|
||||
$user = $dbForProject->getDocument('users', $userId);
|
||||
|
||||
if ($user->isEmpty() || $user->getAttribute('deleted')) {
|
||||
if ($user->isEmpty()) {
|
||||
throw new Exception('User not found', 404, Exception::USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
|
@ -650,7 +647,7 @@ App::patch('/v1/users/:userId/prefs')
|
|||
|
||||
$user = $dbForProject->getDocument('users', $userId);
|
||||
|
||||
if ($user->isEmpty() || $user->getAttribute('deleted')) {
|
||||
if ($user->isEmpty()) {
|
||||
throw new Exception('User not found', 404, Exception::USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
|
@ -692,7 +689,7 @@ App::delete('/v1/users/:userId/sessions/:sessionId')
|
|||
|
||||
$user = $dbForProject->getDocument('users', $userId);
|
||||
|
||||
if ($user->isEmpty() || $user->getAttribute('deleted')) {
|
||||
if ($user->isEmpty()) {
|
||||
throw new Exception('User not found', 404, Exception::USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
|
@ -743,7 +740,7 @@ App::delete('/v1/users/:userId/sessions')
|
|||
|
||||
$user = $dbForProject->getDocument('users', $userId);
|
||||
|
||||
if ($user->isEmpty() || $user->getAttribute('deleted')) {
|
||||
if ($user->isEmpty()) {
|
||||
throw new Exception('User not found', 404, Exception::USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
|
@ -795,28 +792,14 @@ App::delete('/v1/users/:userId')
|
|||
|
||||
$user = $dbForProject->getDocument('users', $userId);
|
||||
|
||||
if ($user->isEmpty() || $user->getAttribute('deleted')) {
|
||||
if ($user->isEmpty()) {
|
||||
throw new Exception('User not found', 404, Exception::USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
/**
|
||||
* DO NOT DELETE THE USER RECORD ITSELF.
|
||||
* WE RETAIN THE USER RECORD TO RESERVE THE USER ID AND ENSURE THAT THE USER ID IS NOT REUSED.
|
||||
*/
|
||||
|
||||
// clone user object to send to workers
|
||||
$clone = clone $user;
|
||||
|
||||
$user
|
||||
->setAttribute("name", null)
|
||||
->setAttribute("email", null)
|
||||
->setAttribute("password", null)
|
||||
->setAttribute("deleted", true)
|
||||
->setAttribute("tokens", null)
|
||||
->setAttribute("search", null)
|
||||
;
|
||||
|
||||
$dbForProject->updateDocument('users', $userId, $user);
|
||||
$dbForProject->deleteDocument('users', $userId);
|
||||
|
||||
$deletes
|
||||
->setType(DELETE_TYPE_DOCUMENT)
|
||||
|
|
|
@ -31,7 +31,8 @@ class DeletesV1 extends Worker
|
|||
*/
|
||||
protected $consoleDB = null;
|
||||
|
||||
public function getName(): string {
|
||||
public function getName(): string
|
||||
{
|
||||
return "deletes";
|
||||
}
|
||||
|
||||
|
@ -202,11 +203,6 @@ class DeletesV1 extends Worker
|
|||
*/
|
||||
protected function deleteUser(Document $document, string $projectId): void
|
||||
{
|
||||
/**
|
||||
* DO NOT DELETE THE USER RECORD ITSELF.
|
||||
* WE RETAIN THE USER RECORD TO RESERVE THE USER ID AND ENSURE THAT THE USER ID IS NOT REUSED.
|
||||
*/
|
||||
|
||||
$userId = $document->getId();
|
||||
|
||||
// Delete all sessions of this user from the sessions table and update the sessions field of the user record
|
||||
|
@ -225,9 +221,14 @@ class DeletesV1 extends Worker
|
|||
$teamId = $document->getAttribute('teamId');
|
||||
$team = $this->getProjectDB($projectId)->getDocument('teams', $teamId);
|
||||
if (!$team->isEmpty()) {
|
||||
$team = $this->getProjectDB($projectId)->updateDocument('teams', $teamId, new Document(\array_merge($team->getArrayCopy(), [
|
||||
'total' => \max($team->getAttribute('total', 0) - 1, 0), // Ensure that total >= 0
|
||||
])));
|
||||
$team = $this
|
||||
->getProjectDB($projectId)
|
||||
->updateDocument(
|
||||
'teams',
|
||||
$teamId,
|
||||
// Ensure that total >= 0
|
||||
$team->setAttribute('total', \max($team->getAttribute('total', 0) - 1, 0))
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -348,7 +349,7 @@ class DeletesV1 extends Worker
|
|||
*/
|
||||
Console::info("Deleting builds for function " . $functionId);
|
||||
$storageBuilds = new Local(APP_STORAGE_BUILDS . '/app-' . $projectId);
|
||||
foreach ($deploymentIds as $deploymentId) {
|
||||
foreach ($deploymentIds as $deploymentId) {
|
||||
$this->deleteByGroup('builds', [
|
||||
new Query('deploymentId', Query::TYPE_EQUAL, [$deploymentId])
|
||||
], $dbForProject, function (Document $document) use ($storageBuilds, $deploymentId) {
|
||||
|
@ -362,7 +363,7 @@ class DeletesV1 extends Worker
|
|||
|
||||
/**
|
||||
* Delete Executions
|
||||
*/
|
||||
*/
|
||||
Console::info("Deleting executions for function " . $functionId);
|
||||
$this->deleteByGroup('executions', [
|
||||
new Query('functionId', Query::TYPE_EQUAL, [$functionId])
|
||||
|
@ -380,7 +381,6 @@ class DeletesV1 extends Worker
|
|||
Console::error($th->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -474,7 +474,7 @@ class DeletesV1 extends Worker
|
|||
$chunk++;
|
||||
|
||||
/** @var string[] $projectIds */
|
||||
$projectIds = array_map(fn(Document $project) => $project->getId(), $projects);
|
||||
$projectIds = array_map(fn (Document $project) => $project->getId(), $projects);
|
||||
|
||||
$sum = count($projects);
|
||||
|
||||
|
@ -533,21 +533,21 @@ class DeletesV1 extends Worker
|
|||
$consoleDB = $this->getConsoleDB();
|
||||
|
||||
// If domain has certificate generated
|
||||
if(isset($document['certificateId'])) {
|
||||
if (isset($document['certificateId'])) {
|
||||
$domainUsingCertificate = $consoleDB->findOne('domains', [
|
||||
new Query('certificateId', Query::TYPE_EQUAL, [$document['certificateId']])
|
||||
]);
|
||||
|
||||
if(!$domainUsingCertificate) {
|
||||
if (!$domainUsingCertificate) {
|
||||
$mainDomain = App::getEnv('_APP_DOMAIN_TARGET', '');
|
||||
if($mainDomain === $document->getAttribute('domain')) {
|
||||
if ($mainDomain === $document->getAttribute('domain')) {
|
||||
$domainUsingCertificate = $mainDomain;
|
||||
}
|
||||
}
|
||||
|
||||
// If certificate is still used by some domain, mark we can't delete.
|
||||
// Current domain should not be found, because we only have copy. Original domain is already deleted from database.
|
||||
if($domainUsingCertificate) {
|
||||
if ($domainUsingCertificate) {
|
||||
Console::warning("Skipping certificate deletion, because a domain is still using it.");
|
||||
return;
|
||||
}
|
||||
|
@ -559,7 +559,7 @@ class DeletesV1 extends Worker
|
|||
|
||||
if ($domain && $checkTraversal && is_dir($directory)) {
|
||||
// Delete certificate document, so Appwrite is aware of change
|
||||
if(isset($document['certificateId'])) {
|
||||
if (isset($document['certificateId'])) {
|
||||
$consoleDB->deleteDocument('certificates', $document['certificateId']);
|
||||
}
|
||||
|
||||
|
@ -577,8 +577,8 @@ class DeletesV1 extends Worker
|
|||
$dbForProject = $this->getProjectDB($projectId);
|
||||
$dbForProject->deleteCollection('bucket_' . $document->getInternalId());
|
||||
|
||||
$device = new Local(APP_STORAGE_UPLOADS.'/app-'.$projectId);
|
||||
|
||||
$device = new Local(APP_STORAGE_UPLOADS . '/app-' . $projectId);
|
||||
|
||||
switch (App::getEnv('_APP_STORAGE_DEVICE', Storage::DEVICE_LOCAL)) {
|
||||
case Storage::DEVICE_S3:
|
||||
$s3AccessKey = App::getEnv('_APP_STORAGE_S3_ACCESS_KEY', '');
|
||||
|
@ -597,7 +597,7 @@ class DeletesV1 extends Worker
|
|||
$device = new DOSpaces(APP_STORAGE_UPLOADS . '/app-' . $projectId, $doSpacesAccessKey, $doSpacesSecretKey, $doSpacesBucket, $doSpacesRegion, $doSpacesAcl);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
$device->deletePath($document->getId());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,7 +45,7 @@
|
|||
"utopia-php/cache": "0.6.*",
|
||||
"utopia-php/cli": "0.12.*",
|
||||
"utopia-php/config": "0.2.*",
|
||||
"utopia-php/database": "0.16.*",
|
||||
"utopia-php/database": "0.17.*",
|
||||
"utopia-php/locale": "0.4.*",
|
||||
"utopia-php/registry": "0.5.*",
|
||||
"utopia-php/preloader": "0.2.*",
|
||||
|
|
14
composer.lock
generated
14
composer.lock
generated
|
@ -4,7 +4,7 @@
|
|||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "9aecfa40a8ee2573bcbcac04edc46b88",
|
||||
"content-hash": "cb8163f39b3bac09c2c547d9afb1064b",
|
||||
"packages": [
|
||||
{
|
||||
"name": "adhocore/jwt",
|
||||
|
@ -2133,16 +2133,16 @@
|
|||
},
|
||||
{
|
||||
"name": "utopia-php/database",
|
||||
"version": "0.16.1",
|
||||
"version": "0.17.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/database.git",
|
||||
"reference": "ab30b2a2ae27b83bd1756928e4ffa47fd8c9e602"
|
||||
"reference": "a4d001452b78b85335ffbd34176cd45d2b13c1ca"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/database/zipball/ab30b2a2ae27b83bd1756928e4ffa47fd8c9e602",
|
||||
"reference": "ab30b2a2ae27b83bd1756928e4ffa47fd8c9e602",
|
||||
"url": "https://api.github.com/repos/utopia-php/database/zipball/a4d001452b78b85335ffbd34176cd45d2b13c1ca",
|
||||
"reference": "a4d001452b78b85335ffbd34176cd45d2b13c1ca",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -2190,9 +2190,9 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/database/issues",
|
||||
"source": "https://github.com/utopia-php/database/tree/0.16.1"
|
||||
"source": "https://github.com/utopia-php/database/tree/0.17.1"
|
||||
},
|
||||
"time": "2022-05-12T17:05:57+00:00"
|
||||
"time": "2022-05-16T09:11:44+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/domains",
|
||||
|
|
|
@ -18,7 +18,7 @@ public class MainActivity extends AppCompatActivity {
|
|||
|
||||
Account account = new Account(client);
|
||||
|
||||
account.delete(new Continuation<Object>() {
|
||||
account.updateStatus(new Continuation<Object>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
|
@ -17,7 +17,7 @@ class MainActivity : AppCompatActivity() {
|
|||
val account = Account(client)
|
||||
|
||||
GlobalScope.launch {
|
||||
val response = account.delete()
|
||||
val response = account.updateStatus()
|
||||
val json = response.body?.string()
|
||||
}
|
||||
}
|
|
@ -5,7 +5,7 @@ func main() async throws {
|
|||
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2") // Your project ID
|
||||
let account = Account(client)
|
||||
let result = try await account.delete()
|
||||
let user = try await account.updateStatus()
|
||||
|
||||
print(String(describing: result)
|
||||
print(String(describing: user)
|
||||
}
|
|
@ -8,7 +8,7 @@ void main() { // Init SDK
|
|||
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
|
||||
.setProject('5df5acd0d48c2') // Your project ID
|
||||
;
|
||||
Future result = account.delete();
|
||||
Future result = account.updateStatus();
|
||||
|
||||
result
|
||||
.then((response) {
|
|
@ -0,0 +1,14 @@
|
|||
const sdk = new Appwrite();
|
||||
|
||||
sdk
|
||||
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
|
||||
.setProject('5df5acd0d48c2') // Your project ID
|
||||
;
|
||||
|
||||
let promise = sdk.account.updateStatus();
|
||||
|
||||
promise.then(function (response) {
|
||||
console.log(response); // Success
|
||||
}, function (error) {
|
||||
console.log(error); // Failure
|
||||
});
|
|
@ -1 +0,0 @@
|
|||
appwrite account delete
|
|
@ -0,0 +1 @@
|
|||
appwrite account updateStatus
|
|
@ -0,0 +1,14 @@
|
|||
const sdk = new Appwrite();
|
||||
|
||||
sdk
|
||||
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
|
||||
.setProject('5df5acd0d48c2') // Your project ID
|
||||
;
|
||||
|
||||
let promise = sdk.account.updateStatus();
|
||||
|
||||
promise.then(function (response) {
|
||||
console.log(response); // Success
|
||||
}, function (error) {
|
||||
console.log(error); // Failure
|
||||
});
|
|
@ -10,7 +10,7 @@ void main() { // Init SDK
|
|||
.setJWT('eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ...') // Your secret JSON Web Token
|
||||
;
|
||||
|
||||
Future result = account.delete();
|
||||
Future result = account.updateStatus();
|
||||
|
||||
result
|
||||
.then((response) {
|
|
@ -12,7 +12,7 @@ client
|
|||
;
|
||||
|
||||
|
||||
let promise = account.delete();
|
||||
let promise = account.updateStatus();
|
||||
|
||||
promise.then(function (response) {
|
||||
console.log(response);
|
|
@ -8,7 +8,7 @@ public void main() {
|
|||
.setJWT("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ..."); // Your secret JSON Web Token
|
||||
|
||||
Account account = new Account(client);
|
||||
account.delete(new Continuation<Response>() {
|
||||
account.updateStatus(new Continuation<Response>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public CoroutineContext getContext() {
|
|
@ -8,6 +8,6 @@ suspend fun main() {
|
|||
.setJWT("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ...") // Your secret JSON Web Token
|
||||
|
||||
val account = Account(client)
|
||||
val response = account.delete()
|
||||
val response = account.updateStatus()
|
||||
val json = response.body?.string()
|
||||
}
|
|
@ -11,7 +11,7 @@ client
|
|||
.setJWT('eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ...') // Your secret JSON Web Token
|
||||
;
|
||||
|
||||
let promise = account.delete();
|
||||
let promise = account.updateStatus();
|
||||
|
||||
promise.then(function (response) {
|
||||
console.log(response);
|
|
@ -13,4 +13,4 @@ $client
|
|||
|
||||
$account = new Account($client);
|
||||
|
||||
$result = $account->delete();
|
||||
$result = $account->updateStatus();
|
|
@ -11,4 +11,4 @@ client = Client()
|
|||
|
||||
account = Account(client)
|
||||
|
||||
result = account.delete()
|
||||
result = account.update_status()
|
|
@ -9,6 +9,6 @@ client
|
|||
|
||||
account = Appwrite::Account.new(client)
|
||||
|
||||
response = account.delete()
|
||||
response = account.update_status()
|
||||
|
||||
puts response.inspect
|
|
@ -6,7 +6,7 @@ func main() async throws {
|
|||
.setProject("5df5acd0d48c2") // Your project ID
|
||||
.setJWT("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ...") // Your secret JSON Web Token
|
||||
let account = Account(client)
|
||||
let result = try await account.delete()
|
||||
let user = try await account.updateStatus()
|
||||
|
||||
print(String(describing: result)
|
||||
print(String(describing: user)
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
Delete a currently logged in user account. Behind the scene, the user record is not deleted but permanently blocked from any access. This is done to avoid deleted accounts being overtaken by new users with the same email address. Any user-related resources like documents or storage files should be deleted separately.
|
1
docs/references/account/update-status.md
Normal file
1
docs/references/account/update-status.md
Normal file
|
@ -0,0 +1 @@
|
|||
Block the currently logged in user account. Behind the scene, the user record is not deleted but permanently blocked from any access. To completely delete a user, use the Users API instead.
|
|
@ -1 +1 @@
|
|||
Delete a user by its unique ID.
|
||||
Delete a user by its unique ID, thereby releasing it's ID. Since ID is released and can be reused, all user-related resources like documents or storage files should be deleted before user deletion. If you want to keep ID reserved, use the [updateStatus](/docs/server/users#usersUpdateStatus) endpoint instead.
|
|
@ -1 +1 @@
|
|||
Update the user status by its unique ID.
|
||||
Update the user status by its unique ID. Use this endpoint as an alternative to deleting a user if you want to keep user's ID reserved.
|
|
@ -6,7 +6,7 @@
|
|||
convertNoticesToExceptions="true"
|
||||
convertWarningsToExceptions="true"
|
||||
processIsolation="false"
|
||||
stopOnFailure="false"
|
||||
stopOnFailure="true"
|
||||
>
|
||||
<extensions>
|
||||
<extension class="Appwrite\Tests\TestHook" />
|
||||
|
|
|
@ -84,20 +84,10 @@ class V13 extends Migration
|
|||
|
||||
Console::log("- {$id}");
|
||||
switch ($id) {
|
||||
case 'executions':
|
||||
try {
|
||||
/**
|
||||
* Rename stdout to response
|
||||
*/
|
||||
$this->projectDB->renameAttribute($id, 'stdout', 'response');
|
||||
} catch (\Throwable $th) {
|
||||
Console::warning("'stdout' from {$id}: {$th->getMessage()}");
|
||||
}
|
||||
break;
|
||||
case 'projects':
|
||||
try {
|
||||
/**
|
||||
* Rename providers to authProviders
|
||||
* Rename providers to authProviders.
|
||||
*/
|
||||
$this->projectDB->renameAttribute($id, 'providers', 'authProviders');
|
||||
} catch (\Throwable $th) {
|
||||
|
@ -106,6 +96,9 @@ class V13 extends Migration
|
|||
break;
|
||||
case 'users':
|
||||
try {
|
||||
/**
|
||||
* Recreate sessions for new subquery.
|
||||
*/
|
||||
$this->projectDB->deleteAttribute($id, 'sessions');
|
||||
$this->projectDB->createAttribute(
|
||||
collection: $id,
|
||||
|
@ -120,6 +113,9 @@ class V13 extends Migration
|
|||
Console::warning("'sessions' from {$id}: {$th->getMessage()}");
|
||||
}
|
||||
try {
|
||||
/**
|
||||
* Recreate tokens for new subquery.
|
||||
*/
|
||||
$this->projectDB->deleteAttribute($id, 'tokens');
|
||||
$this->projectDB->createAttribute(
|
||||
collection: $id,
|
||||
|
@ -134,6 +130,9 @@ class V13 extends Migration
|
|||
Console::warning("'tokens' from {$id}: {$th->getMessage()}");
|
||||
}
|
||||
try {
|
||||
/**
|
||||
* Recreate memberships for new subquery.
|
||||
*/
|
||||
$this->projectDB->deleteAttribute($id, 'memberships');
|
||||
$this->projectDB->createAttribute(
|
||||
collection: $id,
|
||||
|
@ -158,32 +157,64 @@ class V13 extends Migration
|
|||
Console::warning("'_key_user' from {$id}: {$th->getMessage()}");
|
||||
}
|
||||
break;
|
||||
case 'executions':
|
||||
// TODO: migrate stdout (size => 1000000)
|
||||
// TODO: migrate stderr (size => 1000000)
|
||||
break;
|
||||
case 'executions':
|
||||
try {
|
||||
/**
|
||||
* Rename stdout to response
|
||||
*/
|
||||
$this->projectDB->renameAttribute($id, 'stdout', 'response');
|
||||
// TODO: migrate response (size => 1000000)
|
||||
// TODO: migrate stderr (size => 1000000)
|
||||
} catch (\Throwable $th) {
|
||||
Console::warning("'stdout' from {$id}: {$th->getMessage()}");
|
||||
}
|
||||
break;
|
||||
case 'stats':
|
||||
//TODO: migrate value (size => 8)
|
||||
break;
|
||||
case 'tokens':
|
||||
try {
|
||||
$this->createCollection('tokens');
|
||||
} catch (\Throwable $th) {
|
||||
Console::warning("'tokens': {$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);
|
||||
}
|
||||
|
@ -225,6 +256,15 @@ class V13 extends Migration
|
|||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'users':
|
||||
/**
|
||||
* Remove deleted users.
|
||||
*/
|
||||
if ($document->getAttribute('deleted', false) === true) {
|
||||
$this->projectDB->deleteDocument('users', $document->getId());
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return $document;
|
||||
|
|
|
@ -17,7 +17,7 @@ class AccountCustomClientTest extends Scope
|
|||
/**
|
||||
* @depends testCreateAccountSession
|
||||
*/
|
||||
public function testCreateOAuth2AccountSession():array
|
||||
public function testCreateOAuth2AccountSession(): array
|
||||
{
|
||||
$provider = 'mock';
|
||||
$appId = '1';
|
||||
|
@ -26,7 +26,7 @@ class AccountCustomClientTest extends Scope
|
|||
/**
|
||||
* Test for SUCCESS
|
||||
*/
|
||||
$response = $this->client->call(Client::METHOD_PATCH, '/projects/'.$this->getProject()['$id'].'/oauth2', array_merge([
|
||||
$response = $this->client->call(Client::METHOD_PATCH, '/projects/' . $this->getProject()['$id'] . '/oauth2', array_merge([
|
||||
'origin' => 'http://localhost',
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => 'console',
|
||||
|
@ -39,7 +39,7 @@ class AccountCustomClientTest extends Scope
|
|||
|
||||
$this->assertEquals($response['headers']['status-code'], 200);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/account/sessions/oauth2/'.$provider, array_merge([
|
||||
$response = $this->client->call(Client::METHOD_GET, '/account/sessions/oauth2/' . $provider, array_merge([
|
||||
'origin' => 'http://localhost',
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
|
@ -54,9 +54,9 @@ class AccountCustomClientTest extends Scope
|
|||
return [];
|
||||
}
|
||||
|
||||
public function testBlockedAccount():array
|
||||
public function testBlockedAccount(): array
|
||||
{
|
||||
$email = uniqid().'user@localhost.test';
|
||||
$email = uniqid() . 'user@localhost.test';
|
||||
$password = 'password';
|
||||
$name = 'User Name (blocked)';
|
||||
|
||||
|
@ -77,7 +77,7 @@ class AccountCustomClientTest extends Scope
|
|||
$id = $response['body']['$id'];
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 201);
|
||||
|
||||
|
||||
$response = $this->client->call(Client::METHOD_POST, '/account/sessions', array_merge([
|
||||
'origin' => 'http://localhost',
|
||||
'content-type' => 'application/json',
|
||||
|
@ -90,13 +90,13 @@ class AccountCustomClientTest extends Scope
|
|||
$this->assertEquals($response['headers']['status-code'], 201);
|
||||
|
||||
$sessionId = $response['body']['$id'];
|
||||
$session = $this->client->parseCookie((string)$response['headers']['set-cookie'])['a_session_'.$this->getProject()['$id']];
|
||||
$session = $this->client->parseCookie((string)$response['headers']['set-cookie'])['a_session_' . $this->getProject()['$id']];
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/account', array_merge([
|
||||
'origin' => 'http://localhost',
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'cookie' => 'a_session_'.$this->getProject()['$id'].'=' . $session,
|
||||
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
|
||||
]));
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 200);
|
||||
|
@ -115,7 +115,7 @@ class AccountCustomClientTest extends Scope
|
|||
'origin' => 'http://localhost',
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'cookie' => 'a_session_'.$this->getProject()['$id'].'=' . $session,
|
||||
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
|
||||
]));
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 401);
|
||||
|
@ -134,9 +134,89 @@ class AccountCustomClientTest extends Scope
|
|||
return [];
|
||||
}
|
||||
|
||||
public function testCreateJWT():array
|
||||
|
||||
public function testSelfBlockedAccount(): array
|
||||
{
|
||||
$email = uniqid().'user@localhost.test';
|
||||
$email = uniqid() . 'user55@localhost.test';
|
||||
$password = 'password';
|
||||
$name = 'User Name (self blocked)';
|
||||
|
||||
/**
|
||||
* Test for SUCCESS
|
||||
*/
|
||||
$response = $this->client->call(Client::METHOD_POST, '/account', array_merge([
|
||||
'origin' => 'http://localhost',
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
]), [
|
||||
'userId' => 'unique()',
|
||||
'email' => $email,
|
||||
'password' => $password,
|
||||
'name' => $name,
|
||||
]);
|
||||
|
||||
$id = $response['body']['$id'];
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 201);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_POST, '/account/sessions', array_merge([
|
||||
'origin' => 'http://localhost',
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
]), [
|
||||
'email' => $email,
|
||||
'password' => $password,
|
||||
]);
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 201);
|
||||
|
||||
$session = $this->client->parseCookie((string)$response['headers']['set-cookie'])['a_session_' . $this->getProject()['$id']];
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/account', array_merge([
|
||||
'origin' => 'http://localhost',
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
|
||||
]));
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 200);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_PATCH, '/account/status', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
|
||||
], [
|
||||
'status' => false,
|
||||
]);
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 200);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/account', array_merge([
|
||||
'origin' => 'http://localhost',
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
|
||||
]));
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 401);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_POST, '/account/sessions', array_merge([
|
||||
'origin' => 'http://localhost',
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
]), [
|
||||
'email' => $email,
|
||||
'password' => $password,
|
||||
]);
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 401);
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
public function testCreateJWT(): array
|
||||
{
|
||||
$email = uniqid() . 'user@localhost.test';
|
||||
$password = 'password';
|
||||
$name = 'User Name (JWT)';
|
||||
|
||||
|
@ -157,7 +237,7 @@ class AccountCustomClientTest extends Scope
|
|||
$id = $response['body']['$id'];
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 201);
|
||||
|
||||
|
||||
$response = $this->client->call(Client::METHOD_POST, '/account/sessions', array_merge([
|
||||
'origin' => 'http://localhost',
|
||||
'content-type' => 'application/json',
|
||||
|
@ -170,13 +250,13 @@ class AccountCustomClientTest extends Scope
|
|||
$this->assertEquals($response['headers']['status-code'], 201);
|
||||
|
||||
$sessionId = $response['body']['$id'];
|
||||
$session = $this->client->parseCookie((string)$response['headers']['set-cookie'])['a_session_'.$this->getProject()['$id']];
|
||||
$session = $this->client->parseCookie((string)$response['headers']['set-cookie'])['a_session_' . $this->getProject()['$id']];
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/account', array_merge([
|
||||
'origin' => 'http://localhost',
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'cookie' => 'a_session_'.$this->getProject()['$id'].'=' . $session,
|
||||
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
|
||||
]));
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 200);
|
||||
|
@ -185,7 +265,7 @@ class AccountCustomClientTest extends Scope
|
|||
'origin' => 'http://localhost',
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'cookie' => 'a_session_'.$this->getProject()['$id'].'=' . $session,
|
||||
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
|
||||
]));
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 201);
|
||||
|
@ -212,11 +292,11 @@ class AccountCustomClientTest extends Scope
|
|||
|
||||
$this->assertEquals($response['headers']['status-code'], 200);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_DELETE, '/account/sessions/'.$sessionId, array_merge([
|
||||
$response = $this->client->call(Client::METHOD_DELETE, '/account/sessions/' . $sessionId, array_merge([
|
||||
'origin' => 'http://localhost',
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'cookie' => 'a_session_'.$this->getProject()['$id'].'=' . $session,
|
||||
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
|
||||
]));
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 204);
|
||||
|
@ -249,7 +329,7 @@ class AccountCustomClientTest extends Scope
|
|||
$this->assertNotEmpty($response['body']);
|
||||
$this->assertNotEmpty($response['body']['$id']);
|
||||
|
||||
$session = $this->client->parseCookie((string)$response['headers']['set-cookie'])['a_session_'.$this->getProject()['$id']];
|
||||
$session = $this->client->parseCookie((string)$response['headers']['set-cookie'])['a_session_' . $this->getProject()['$id']];
|
||||
|
||||
/**
|
||||
* Test for FAILURE
|
||||
|
@ -258,7 +338,7 @@ class AccountCustomClientTest extends Scope
|
|||
'origin' => 'http://localhost',
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'cookie' => 'a_session_'.$this->getProject()['$id'].'=' . $session,
|
||||
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
|
||||
]);
|
||||
|
||||
$this->assertEquals(401, $response['headers']['status-code']);
|
||||
|
@ -278,7 +358,7 @@ class AccountCustomClientTest extends Scope
|
|||
'origin' => 'http://localhost',
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'cookie' => 'a_session_'.$this->getProject()['$id'].'=' . $session,
|
||||
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
|
||||
]), [
|
||||
'oldPassword' => '',
|
||||
]);
|
||||
|
@ -293,7 +373,7 @@ class AccountCustomClientTest extends Scope
|
|||
*/
|
||||
public function testUpdateAnonymousAccountEmail($session)
|
||||
{
|
||||
$email = uniqid().'new@localhost.test';
|
||||
$email = uniqid() . 'new@localhost.test';
|
||||
|
||||
/**
|
||||
* Test for FAILURE
|
||||
|
@ -302,7 +382,7 @@ class AccountCustomClientTest extends Scope
|
|||
'origin' => 'http://localhost',
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'cookie' => 'a_session_'.$this->getProject()['$id'].'=' . $session,
|
||||
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
|
||||
]), [
|
||||
'email' => $email,
|
||||
'password' => '',
|
||||
|
@ -316,7 +396,7 @@ class AccountCustomClientTest extends Scope
|
|||
public function testConvertAnonymousAccount()
|
||||
{
|
||||
$session = $this->testCreateAnonymousAccount();
|
||||
$email = uniqid().'new@localhost.test';
|
||||
$email = uniqid() . 'new@localhost.test';
|
||||
$password = 'new-password';
|
||||
|
||||
/**
|
||||
|
@ -336,7 +416,7 @@ class AccountCustomClientTest extends Scope
|
|||
'origin' => 'http://localhost',
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'cookie' => 'a_session_'.$this->getProject()['$id'].'=' . $session,
|
||||
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
|
||||
]), [
|
||||
'email' => $email,
|
||||
'password' => $password,
|
||||
|
@ -347,13 +427,13 @@ class AccountCustomClientTest extends Scope
|
|||
/**
|
||||
* Test for SUCCESS
|
||||
*/
|
||||
$email = uniqid().'new@localhost.test';
|
||||
$email = uniqid() . 'new@localhost.test';
|
||||
|
||||
$response = $this->client->call(Client::METHOD_PATCH, '/account/email', array_merge([
|
||||
'origin' => 'http://localhost',
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'cookie' => 'a_session_'.$this->getProject()['$id'].'=' . $session,
|
||||
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
|
||||
]), [
|
||||
'email' => $email,
|
||||
'password' => $password,
|
||||
|
@ -394,14 +474,14 @@ class AccountCustomClientTest extends Scope
|
|||
'origin' => 'http://localhost',
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'cookie' => 'a_session_'.$this->getProject()['$id'].'=' . $session,
|
||||
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
|
||||
]));
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 200);
|
||||
|
||||
$userId = $response['body']['$id'] ?? '';
|
||||
|
||||
$response = $this->client->call(Client::METHOD_PATCH, '/projects/'.$this->getProject()['$id'].'/oauth2', array_merge([
|
||||
|
||||
$response = $this->client->call(Client::METHOD_PATCH, '/projects/' . $this->getProject()['$id'] . '/oauth2', array_merge([
|
||||
'origin' => 'http://localhost',
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => 'console',
|
||||
|
@ -414,26 +494,26 @@ class AccountCustomClientTest extends Scope
|
|||
|
||||
$this->assertEquals($response['headers']['status-code'], 200);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/account/sessions/oauth2/'.$provider, array_merge([
|
||||
$response = $this->client->call(Client::METHOD_GET, '/account/sessions/oauth2/' . $provider, array_merge([
|
||||
'origin' => 'http://localhost',
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'cookie' => 'a_session_'.$this->getProject()['$id'].'=' . $session,
|
||||
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
|
||||
]), [
|
||||
'success' => 'http://localhost/v1/mock/tests/general/oauth2/success',
|
||||
'failure' => 'http://localhost/v1/mock/tests/general/oauth2/failure',
|
||||
]);
|
||||
|
||||
$session = $this->client->parseCookie((string)$response['headers']['set-cookie'])['a_session_'.$this->getProject()['$id']];
|
||||
$session = $this->client->parseCookie((string)$response['headers']['set-cookie'])['a_session_' . $this->getProject()['$id']];
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertEquals('success', $response['body']['result']);
|
||||
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/account', array_merge([
|
||||
'origin' => 'http://localhost',
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'cookie' => 'a_session_'.$this->getProject()['$id'].'=' . $session,
|
||||
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
|
||||
]));
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 200);
|
||||
|
@ -449,7 +529,7 @@ class AccountCustomClientTest extends Scope
|
|||
'origin' => 'http://localhost',
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'cookie' => 'a_session_'.$this->getProject()['$id'].'=' . $session,
|
||||
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
|
||||
]));
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
|
@ -458,14 +538,14 @@ class AccountCustomClientTest extends Scope
|
|||
$this->assertGreaterThan(\time() + 14400 - 5, $response['body']['providerAccessTokenExpiry']); // 5 seconds allowed networking delay
|
||||
|
||||
$initialExpiry = $response['body']['providerAccessTokenExpiry'];
|
||||
|
||||
|
||||
sleep(3);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_PATCH, '/account/sessions/current', array_merge([
|
||||
'origin' => 'http://localhost',
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'cookie' => 'a_session_'.$this->getProject()['$id'].'=' . $session,
|
||||
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
|
||||
]));
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
|
@ -476,14 +556,15 @@ class AccountCustomClientTest extends Scope
|
|||
return [];
|
||||
}
|
||||
|
||||
public function testGetSessionByID() {
|
||||
public function testGetSessionByID()
|
||||
{
|
||||
$session = $this->testCreateAnonymousAccount();
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/account/sessions/current', array_merge([
|
||||
'origin' => 'http://localhost',
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'cookie' => 'a_session_'.$this->getProject()['$id'].'=' . $session,
|
||||
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
|
||||
]));
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 200);
|
||||
|
@ -491,11 +572,11 @@ class AccountCustomClientTest extends Scope
|
|||
|
||||
$sessionID = $response['body']['$id'];
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/account/sessions/'.$sessionID, array_merge([
|
||||
$response = $this->client->call(Client::METHOD_GET, '/account/sessions/' . $sessionID, array_merge([
|
||||
'origin' => 'http://localhost',
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'cookie' => 'a_session_'.$this->getProject()['$id'].'=' . $session,
|
||||
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
|
||||
]));
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 200);
|
||||
|
@ -505,7 +586,7 @@ class AccountCustomClientTest extends Scope
|
|||
'origin' => 'http://localhost',
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'cookie' => 'a_session_'.$this->getProject()['$id'].'=' . $session,
|
||||
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
|
||||
]));
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 404);
|
||||
|
|
|
@ -12,49 +12,4 @@ class UsersCustomServerTest extends Scope
|
|||
use UsersBase;
|
||||
use ProjectCustom;
|
||||
use SideServer;
|
||||
|
||||
public function testDeprecatedUsers():array
|
||||
{
|
||||
/**
|
||||
* Test for FAILURE (don't allow recreating account with same custom ID)
|
||||
*/
|
||||
|
||||
// Create user with custom ID 'meldiron'
|
||||
$response = $this->client->call(Client::METHOD_POST, '/users', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'userId' => 'meldiron',
|
||||
'email' => 'matej@appwrite.io',
|
||||
'password' => 'my-superstr0ng-password',
|
||||
'name' => 'Matej Bačo'
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $response['headers']['status-code']);
|
||||
|
||||
// Delete user with custom ID 'meldiron'
|
||||
$response = $this->client->call(Client::METHOD_DELETE, '/users/meldiron', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
|
||||
$this->assertEquals(204, $response['headers']['status-code']);
|
||||
|
||||
// Try to create user with custom ID 'meldiron' again, but now it should fail
|
||||
$response1 = $this->client->call(Client::METHOD_POST, '/users', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'userId' => 'meldiron',
|
||||
'email' => 'matej2@appwrite.io',
|
||||
'password' => 'someones-superstr0ng-password',
|
||||
'name' => 'Matej Bačo Second'
|
||||
]);
|
||||
|
||||
$this->assertEquals(409, $response1['headers']['status-code']);
|
||||
$this->assertEquals('Account already exists', $response1['body']['message']);
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
}
|
|
@ -101,15 +101,14 @@ class WebhooksCustomClientTest extends Scope
|
|||
$id = $account['body']['$id'];
|
||||
$session = $this->client->parseCookie((string)$accountSession['headers']['set-cookie'])['a_session_' . $this->getProject()['$id']];
|
||||
|
||||
$account = $this->client->call(Client::METHOD_DELETE, '/account', array_merge([
|
||||
$account = $this->client->call(Client::METHOD_PATCH, '/account/status', array_merge([
|
||||
'origin' => 'http://localhost',
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
|
||||
]));
|
||||
|
||||
$this->assertEquals($account['headers']['status-code'], 204);
|
||||
$this->assertEmpty($account['body']);
|
||||
$this->assertEquals($account['headers']['status-code'], 200);
|
||||
|
||||
$webhook = $this->getLastRequest();
|
||||
|
||||
|
@ -117,9 +116,9 @@ class WebhooksCustomClientTest extends Scope
|
|||
$this->assertEquals($webhook['headers']['Content-Type'], 'application/json');
|
||||
$this->assertEquals($webhook['headers']['User-Agent'], 'Appwrite-Server vdev. Please report abuse at security@appwrite.io');
|
||||
$this->assertStringContainsString('users.*', $webhook['headers']['X-Appwrite-Webhook-Events']);
|
||||
$this->assertStringContainsString('users.*.delete', $webhook['headers']['X-Appwrite-Webhook-Events']);
|
||||
$this->assertStringContainsString('users.*.update.status', $webhook['headers']['X-Appwrite-Webhook-Events']);
|
||||
$this->assertStringContainsString("users.{$id}", $webhook['headers']['X-Appwrite-Webhook-Events']);
|
||||
$this->assertStringContainsString("users.{$id}.delete", $webhook['headers']['X-Appwrite-Webhook-Events']);
|
||||
$this->assertStringContainsString("users.{$id}.update.status", $webhook['headers']['X-Appwrite-Webhook-Events']);
|
||||
$this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Signature'], 'not-yet-implemented');
|
||||
$this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Id'] ?? '', $this->getProject()['webhookId']);
|
||||
$this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Project-Id'] ?? '', $this->getProject()['$id']);
|
||||
|
|
Loading…
Reference in a new issue